diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..801b16a2 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,35 @@ +**/.git +**/.gitignore +**/.github +**/.vscode +**/.idea +**/coverage +**/.aws +**/.ssh +**/.DS_Store +**/.aof +**/venv +**/env +**/bin +# **/docs we want to keep dje/templates/rest_framework/docs/ +docs/ +# **/dist we want to keep ./thirdparty/dist/ +# **/etc we need to keep ./etc/ +**/lib +**/include +**/share +**/var +**/*.egg-info +**/*.log +**/__pycache__ +*.pyc +.dockerignore +.readthedocs.yaml +docker.env +.env +Makefile +Dockerfile +README.rst +docker-compose.yml +fabfile.py +pyvenv.cfg diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml new file mode 100644 index 00000000..3333879d --- /dev/null +++ b/.github/workflows/ci-docker.yml @@ -0,0 +1,17 @@ +name: Test on Docker CI + +on: [push, pull_request] + +jobs: + test: + runs-on: ubuntu-22.04 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Generate the .env file and the SECRET_KEY + run: make envfile + + - name: Run tests + run: docker compose run web python ./manage.py test --verbosity=2 --noinput diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..31f65a47 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,59 @@ +name: Test CI + +on: [push, pull_request] + +env: + DATABASE_NAME: dejacode + DATABASE_USER: dejacode + DATABASE_PASSWORD: dejacode + POSTGRES_INITDB_ARGS: --encoding=UTF-8 --lc-collate=en_US.UTF-8 --lc-ctype=en_US.UTF-8 + +jobs: + test: + runs-on: ubuntu-22.04 + + services: + postgres: + image: postgres:16 + env: + POSTGRES_DB: ${{ env.DATABASE_NAME }} + POSTGRES_USER: ${{ env.DATABASE_USER }} + POSTGRES_PASSWORD: ${{ env.DATABASE_PASSWORD }} + POSTGRES_INITDB_ARGS: ${{ env.POSTGRES_INITDB_ARGS }} + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: "3.10" + + - name: Install python-ldap OS dependencies + run: sudo apt-get install -y libsasl2-dev libldap2-dev libssl-dev + + - name: Install dependencies + run: make dev envfile + + - name: Validate code format + run: make check + + - name: Start Redis + uses: supercharge/redis-github-action@1.5.0 + +# - name: Check Django deployment settings +# run: make check-deploy + + - name: Build the documentation + run: make docs + + - name: Run tests + run: bin/python manage.py test --verbosity=2 --noinput --parallel diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..23cd2bdc --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +*.pyc +*.db +.installed.cfg +parts +develop-eggs +bin +eggs +downloads +lib +lib64 +build +.project +.pydevproject +include +.settings +TAGS +.idea +Include +Lib +.env +Procfile +tmp +var +.Python +local.cfg +_build +pip-selfcheck.json +pyvenv.cfg +geckodriver.log +.DS_Store +share +selenium +local +/dejacode.egg-info/ +/dist/ +/.cache/ +/.python-version +/enterprise.egg-info/ diff --git a/CHANGELOG.rst b/CHANGELOG.rst new file mode 100644 index 00000000..ec53c922 --- /dev/null +++ b/CHANGELOG.rst @@ -0,0 +1,4 @@ +Release notes +============= + +### Version 5.0.0 diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst new file mode 100644 index 00000000..9f8bf81d --- /dev/null +++ b/CONTRIBUTING.rst @@ -0,0 +1,6 @@ +======================== +Contributing to DejaCode +======================== + +Documented at https://dejacode.readthedocs.io/en/latest/contributing.html +or in docs/contributing.rst for details. diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..b550c725 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,63 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# DejaCode is a trademark of nexB Inc. +# SPDX-License-Identifier: AGPL-3.0-only +# See https://github.com/nexB/dejacode for support or download. +# See https://aboutcode.org for more information about AboutCode FOSS projects. +# + +FROM python:3.10-slim + +LABEL org.opencontainers.image.source="https://github.com/nexB/dejacode" +LABEL org.opencontainers.image.description="DejaCode" +LABEL org.opencontainers.image.licenses="AGPL-3.0-only" + +ENV APP_NAME dejacode +ENV APP_USER app +ENV APP_DIR /opt/$APP_NAME +ENV VIRTUAL_ENV /opt/$APP_NAME/venv + +# Force Python unbuffered stdout and stderr (they are flushed to terminal immediately) +ENV PYTHONUNBUFFERED 1 +# Do not write Python .pyc files +ENV PYTHONDONTWRITEBYTECODE 1 +# Add the app dir in the Python path for entry points availability +ENV PYTHONPATH $PYTHONPATH:$APP_DIR + +# OS requirements +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + build-essential \ + libldap2-dev \ + libsasl2-dev \ + libpq5 \ + wait-for-it \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + +# Create the APP_USER group, user, and directory with proper permissions +RUN addgroup --system $APP_USER \ + && adduser --system --group --home=$APP_DIR $APP_USER \ + && chown $APP_USER:$APP_USER $APP_DIR \ + && mkdir -p /var/$APP_NAME \ + && chown $APP_USER:$APP_USER /var/$APP_NAME + +# Setup the work directory and the user as APP_USER for the remaining stages +WORKDIR $APP_DIR +USER $APP_USER + +# Create directories for static and media files +RUN mkdir -p /var/$APP_NAME/static/ /var/$APP_NAME/media/ + +# Create the virtualenv +RUN python -m venv $VIRTUAL_ENV +# Enable the virtualenv, similar effect as "source activate" +ENV PATH $VIRTUAL_ENV/bin:$PATH + +# Install the dependencies before the codebase COPY for proper Docker layer caching +COPY --chown=$APP_USER:$APP_USER setup.cfg setup.py $APP_DIR/ +COPY --chown=$APP_USER:$APP_USER ./thirdparty/dist/ $APP_DIR/thirdparty/dist/ +RUN pip install --find-links=$APP_DIR/thirdparty/dist/ --no-index --no-cache-dir . + +# Copy the codebase and set the proper permissions for the APP_USER +COPY --chown=$APP_USER:$APP_USER . $APP_DIR diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..05311ca6 --- /dev/null +++ b/LICENSE @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. \ No newline at end of file diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 00000000..aea848c4 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,42 @@ +include *LICENSE +include *NOTICE +include *.py +include MANIFEST.in +include Makefile +include *.rst +include setup.* +include *.ini +include *.yml +include *.yaml +include *ignore +include Dockerfile +include docker* +include .VERSION + +graft component_catalog +graft dejacode +graft dejacode_toolkit +graft dje +graft docs +graft etc +graft license_library +graft notification +graft organization +graft policy +graft product_portfolio +graft purldb +graft reporting +graft thirdparty +graft workflow + +prune dejacode.egg-info +prune docs/_build +prune bin +prune tmp +prune lib +prune lib64 +prune local +prune var + +recursive-exclude * __pycache__ +recursive-exclude * *.py[co] \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..04538607 --- /dev/null +++ b/Makefile @@ -0,0 +1,164 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# DejaCode is a trademark of nexB Inc. +# SPDX-License-Identifier: AGPL-3.0-only +# See https://github.com/nexB/dejacode for support or download. +# See https://aboutcode.org for more information about AboutCode FOSS projects. +# + +PYTHON_EXE=python3.10 +MANAGE=bin/python manage.py +ACTIVATE?=. bin/activate; +PIP_ARGS=--find-links=./thirdparty/dist/ --no-index --no-cache-dir +GET_SECRET_KEY=`cat /dev/urandom | head -c 50 | base64` +# Customize with `$ make envfile ENV_FILE=/etc/dejacode/.env` +ENV_FILE=.env +FIXTURES_LOCATION=./dje/fixtures +DOCS_LOCATION=./docs +MODIFIED_PYTHON_FILES=`git ls-files -m "*.py"` +BLACK_ARGS=--exclude="migrations|data|lib/|lib64|bin|var|dist|.cache" -l 100 +DOCKER_COMPOSE=docker compose -f docker-compose.yml +DOCKER_EXEC=${DOCKER_COMPOSE} exec +DB_NAME=dejacode_db +DB_USERNAME=dejacode +DB_CONTAINER_NAME=db +DB_INIT_FILE=./data/postgresql/initdb.sql.gz +POSTGRES_INITDB_ARGS=--encoding=UTF-8 --lc-collate=en_US.UTF-8 --lc-ctype=en_US.UTF-8 +TIMESTAMP=$(shell date +"%Y-%m-%d_%H%M") + +virtualenv: + @echo "-> Bootstrap the virtualenv with PYTHON_EXE=${PYTHON_EXE}" + ${PYTHON_EXE} -m venv . + +conf: virtualenv + @echo "-> Install dependencies" + @${ACTIVATE} pip install ${PIP_ARGS} --editable . + @echo "-> Create the var/ directory" + @mkdir -p var + +dev: virtualenv + @echo "-> Configure and install development dependencies" + @${ACTIVATE} pip install ${PIP_ARGS} --editable .[dev] + +envfile: + @echo "-> Create the .env file and generate a secret key" + @if test -f ${ENV_FILE}; then echo "${ENV_FILE} file exists already"; exit 1; fi + @mkdir -p $(shell dirname ${ENV_FILE}) && touch ${ENV_FILE} + @echo "SECRET_KEY=${GET_SECRET_KEY}" > ${ENV_FILE} + +isort: + @echo "-> Apply isort changes to ensure proper imports ordering" + @${ACTIVATE} isort . + +black: + @echo "-> Apply black code formatter" + @${ACTIVATE} black ${BLACK_ARGS} . + +doc8: + @echo "-> Run doc8 validation" + @${ACTIVATE} doc8 --max-line-length 100 --ignore-path docs/_build/ \ + --ignore-path docs/installation_and_sysadmin/ --quiet docs/ + +valid: isort black doc8 check + +bandit: + @echo "-> Run source code security analyzer" + @${ACTIVATE} pip install bandit + @${ACTIVATE} bandit --recursive . \ + --exclude ./bin,./data,./dist,./docs,./include,./lib,./share,./thirdparty,./var,tests \ + --quiet + +check: doc8 bandit + @echo "-> Run flake8 (pycodestyle, pyflakes, mccabe) validation" + @${ACTIVATE} flake8 . + @echo "-> Run isort imports ordering validation" + @${ACTIVATE} isort --check-only . + @echo "-> Run black validation" + @${ACTIVATE} black --check ${BLACK_ARGS} . + @echo "-> Running ABOUT files validation" + @${ACTIVATE} about check ./thirdparty/ + @$(MAKE) check-docstrings + +check-docstrings: + @echo "-> Run docstring validation" + @${ACTIVATE} pip install pydocstyle + @${ACTIVATE} pydocstyle component_catalog dejacode dejacode_toolkit dje \ + license_library notification organization policy product_portfolio purldb \ + reporting workflow + +check-deploy: + @echo "-> Check Django deployment settings" + ${MANAGE} check --deploy + +clean: + @echo "-> Cleaning the Python env" + rm -rf bin/ lib/ lib64/ include/ build/ dist/ share/ pip-selfcheck.json pyvenv.cfg + find . -type f -name '*.py[co]' -delete -o -type d -name __pycache__ -delete + +initdb: + @echo "-> Stop Docker services that access the database" + ${DOCKER_COMPOSE} stop web worker + @echo "-> Ensure the db Docker service is started" + ${DOCKER_COMPOSE} start db + @echo "-> Rename the current ${DB_NAME} database as backup" + ${DOCKER_EXEC} --no-TTY ${DB_CONTAINER_NAME} psql --username=${DB_USERNAME} postgres \ + --command='ALTER DATABASE ${DB_NAME} RENAME TO "${DB_NAME}_${TIMESTAMP}"' + @echo "-> Create the ${DB_NAME} database" + ${DOCKER_EXEC} --no-TTY ${DB_CONTAINER_NAME} \ + createdb --username=${DB_USERNAME} --encoding=utf-8 --owner=dejacode ${DB_NAME} + echo "-> Loading initial data" + gunzip < ${DB_INIT_FILE} | ${DOCKER_EXEC} --no-TTY ${DB_CONTAINER_NAME} \ + psql --username=${DB_USERNAME} ${DB_NAME} + @echo "Starting Docker services" + ${DOCKER_COMPOSE} start + +migrate: + @echo "-> Apply database migrations" + ${MANAGE} migrate + +# make postgresdb DB_PASSWORD=YOUR_PASSWORD +postgresdb: + @echo "-> Configure PostgreSQL database" + @echo "-> Create database user ${DB_NAME}" + @createuser --no-createrole --no-superuser --login --inherit --createdb '${DB_USERNAME}' || true + @psql -c "alter user ${DB_USERNAME} with encrypted password '${DB_PASSWORD}';" || true + @echo "-> Drop ${DB_NAME} database if exists" + @dropdb ${DB_NAME} || true + @echo "-> Create ${DB_NAME} database" + @createdb --owner=${DB_USERNAME} ${POSTGRES_INITDB_ARGS} ${DB_NAME} + +run: + ${MANAGE} runserver 8000 + +test: + @echo "-> Run the test suite" + ${MANAGE} test --noinput --parallel auto + +docs: + @echo "-> Builds the installation_and_sysadmin docs" + rm -rf ${DOCS_LOCATION}/_build/ + @${ACTIVATE} pip install Sphinx sphinx_rtd_theme furo + @${ACTIVATE} sphinx-build -b singlehtml ${DOCS_LOCATION} ${DOCS_LOCATION}/_build/singlehtml/ + @${ACTIVATE} sphinx-build -b html ${DOCS_LOCATION} ${DOCS_LOCATION}/_build/html/ + +build: + @echo "-> Build the Docker images" + ${DOCKER_COMPOSE} build + +bash: + ${DOCKER_EXEC} web bash + +shell: + ${DOCKER_EXEC} web ./manage.py shell + +psql: + ${DOCKER_EXEC} ${DB_CONTAINER_NAME} psql --username=${DB_USERNAME} postgres + +# $ make log SERVICE=db +log: + ${DOCKER_COMPOSE} logs --tail="100" ${SERVICE} + +createsuperuser: + ${DOCKER_EXEC} web ./manage.py createsuperuser + +.PHONY: virtualenv conf dev envfile check bandit isort black doc8 valid check-docstrings check-deploy clean initdb postgresdb migrate run test docs build psql bash shell log createsuperuse diff --git a/NOTICE b/NOTICE new file mode 100644 index 00000000..10f9ea3a --- /dev/null +++ b/NOTICE @@ -0,0 +1,33 @@ +DejaCode License Notice +----------------------- + +DejaCode is an enterprise-level application to automate open source license +compliance and ensure software supply chain integrity, powered by ScanCode, +the industry-leading code scanner. + +SPDX-License-Identifier: AGPL-3.0-only + +Copyright (c) nexB Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, version 3 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . + +DejaCode embeds third-party free and open source software packages under various +licenses including copyleft licenses. The origin and license of third-party packages is +documented by .ABOUT files in the GitHub project repository. + +Commercial License Option +------------------------- + +DejaCode is offered under a nexB commercial license as an alternative. +You can learn more about this option by contacting us at +https://www.nexb.com/contact-us/ \ No newline at end of file diff --git a/README.rst b/README.rst new file mode 100644 index 00000000..6ba7bb93 --- /dev/null +++ b/README.rst @@ -0,0 +1,84 @@ +DejaCode +======== + +DejaCode is a complete enterprise-level application to automate open source license +compliance and ensure software supply chain integrity, powered by +`ScanCode `_, +the industry-leading code scanner. + +- Run scans and track all the open source and third-party products and components used + in your software. +- Apply usage policies at the license or component level, and integrate into + ScanCode to ensure compliance. +- Capture software inventories (SBOMs), generate compliance artifacts, and keep + historical data. +- Ensure FOSS compliance with enterprise-grade features and integrations for DevOps and + software systems. +- Scan a software package, simply by providing its Download URL, to get comprehensive + details of its composition and create an SBOM. +- Load software package data into DejaCode with the integration for the open source + ScanCode.io and ScanCode Toolkit projects to create a product’s SBOM. +- Track and report vulnerability tracking and reporting by integrating with the open + source VulnerableCode project. +- Create, publish and share SBOM documents in DejaCode, including detailed attribution + documentation and custom reports in multiple file formats and standards, such as + CycloneDX and SPDX. + +Getting started +--------------- + +The DejaCode documentation is available here: https://dejacode.readthedocs.org/ + +If you have questions please ask them in +`Discussions `_. + +If you want to contribute to DejaCode, start with our +`Contributing `_ page. + +Build and tests status +---------------------- + ++------------+-------------------+ +| **Tests** | **Documentation** | ++============+===================+ +| |ci-tests| | |docs-rtd| | ++------------+-------------------+ + +DejaCode License Notice +----------------------- + +DejaCode is an enterprise-level application to automate open source license +compliance and ensure software supply chain integrity, powered by ScanCode, +the industry-leading code scanner. + +SPDX-License-Identifier: AGPL-3.0-only + +Copyright (c) nexB Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, version 3 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . + +Commercial License option +------------------------- + +DejaCode is offered under a nexB commercial license as an alternative. +You can learn more about this option by contacting us at +https://www.nexb.com/contact-us/ + + +.. |ci-tests| image:: https://github.com/nexB/dejacode/actions/workflows/ci.yml/badge.svg?branch=main + :target: https://github.com/nexB/dejacode/actions/workflows/ci.yml + :alt: CI Tests Status + +.. |docs-rtd| image:: https://readthedocs.org/projects/dejacode/badge/?version=latest + :target: https://dejacode.readthedocs.io/en/latest/?badge=latest + :alt: Documentation Build Status diff --git a/component_catalog/__init__.py b/component_catalog/__init__.py new file mode 100644 index 00000000..7aa35de2 --- /dev/null +++ b/component_catalog/__init__.py @@ -0,0 +1,7 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# DejaCode is a trademark of nexB Inc. +# SPDX-License-Identifier: AGPL-3.0-only +# See https://github.com/nexB/dejacode for support or download. +# See https://aboutcode.org for more information about AboutCode FOSS projects. +# diff --git a/component_catalog/admin.py b/component_catalog/admin.py new file mode 100644 index 00000000..6f745659 --- /dev/null +++ b/component_catalog/admin.py @@ -0,0 +1,1063 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# DejaCode is a trademark of nexB Inc. +# SPDX-License-Identifier: AGPL-3.0-only +# See https://github.com/nexB/dejacode for support or download. +# See https://aboutcode.org for more information about AboutCode FOSS projects. +# + +from django.contrib import admin +from django.contrib import messages +from django.contrib.admin.options import IS_POPUP_VAR +from django.db import transaction +from django.forms import Media +from django.http import QueryDict +from django.shortcuts import redirect +from django.urls import path +from django.urls import reverse +from django.utils.html import format_html +from django.utils.http import urlencode +from django.utils.translation import gettext_lazy as _ + +from component_catalog.filters import HierarchyRelatedLookupListFilter +from component_catalog.filters import ParentRelatedLookupListFilter +from component_catalog.forms import ComponentAdminForm +from component_catalog.forms import ComponentMassUpdateForm +from component_catalog.forms import PackageAdminForm +from component_catalog.forms import PackageMassUpdateForm +from component_catalog.forms import SubcomponentAdminForm +from component_catalog.forms import SubcomponentMassUpdateForm +from component_catalog.importers import ComponentImporter +from component_catalog.importers import PackageImporter +from component_catalog.importers import SubcomponentImporter +from component_catalog.inlines import ComponentAssignedPackageInline +from component_catalog.inlines import ComponentAssignedPackageInline2 +from component_catalog.inlines import SubcomponentChildInline +from component_catalog.license_expression_dje import validate_expression_on_relations +from component_catalog.models import AcceptableLinkage +from component_catalog.models import Component +from component_catalog.models import ComponentAssignedPackage +from component_catalog.models import ComponentKeyword +from component_catalog.models import ComponentStatus +from component_catalog.models import ComponentType +from component_catalog.models import Package +from component_catalog.models import Subcomponent +from component_catalog.programming_languages import PROGRAMMING_LANGUAGES +from component_catalog.views import ComponentAddToProductAdminView +from component_catalog.views import PackageAddToProductAdminView +from component_catalog.views import SetComponentPolicyView +from component_catalog.views import SetPackagePolicyView +from component_catalog.views import SetSubcomponentPolicyView +from dje.admin import ChangelistPopupPermissionMixin +from dje.admin import DataspacedAdmin +from dje.admin import ExternalReferenceInline +from dje.admin import dejacode_site +from dje.admin import get_additional_information_fieldset +from dje.admin import get_hierarchy_link +from dje.client_data import add_client_data +from dje.filters import DataspaceFilter +from dje.filters import IsNullFieldListFilter +from dje.filters import LevelFieldListFilter +from dje.filters import LimitToDataspaceListFilter +from dje.filters import MissingInFilter +from dje.filters import RelatedLookupListFilter +from dje.list_display import AsJoinList +from dje.list_display import AsLink +from dje.list_display import AsLinkList +from dje.list_display import AsNaturalTime +from dje.list_display import AsURL +from dje.models import History +from dje.tasks import package_collect_data +from dje.templatetags.dje_tags import urlize_target_blank +from dje.utils import CHANGELIST_LINK_TEMPLATE +from dje.utils import get_instance_from_referer +from license_library.models import License +from reporting.filters import ReportingQueryListFilter + + +class FormDataOutdated(Exception): + pass + + +class AwesompleteAdminMixin: + awesomplete_data = {} + + @property + def media(self): + base_media = super().media + extra_media = Media( + css={"all": ("awesomplete/awesomplete-1.1.5.css",)}, + js=( + "awesomplete/awesomplete-1.1.5.min.js", + "js/awesomplete_fields.js", + ), + ) + return base_media + extra_media + + def get_awesomplete_data(self): + return self.awesomplete_data + + def add_view(self, request, form_url="", extra_context=None): + add_client_data(request, awesomplete_data=self.get_awesomplete_data()) + return super().add_view(request, form_url, extra_context) + + def change_view(self, request, object_id, form_url="", extra_context=None): + add_client_data(request, awesomplete_data=self.get_awesomplete_data()) + return super().change_view(request, object_id, form_url, extra_context) + + +class LicenseExpressionBuilderAdminMixin: + """ + The full set of license keys available in the dataspace context is always returned. + The limited keys from the related component are collected on the client side + (AJAX API call) based on the form state and events. + """ + + expression_field_name = "license_expression" + + @property + def media(self): + base_media = super().media + extra_media = Media( + css={"all": ("awesomplete/awesomplete-1.1.5.css",)}, + js=( + "awesomplete/awesomplete-1.1.5.min.js", + "js/license_expression_builder.js", + ), + ) + return base_media + extra_media + + @staticmethod + def get_license_data_for_builder(request, instance=None): + """ + Return the list of formatted licenses scoped by the instance dataspace, or + by the request user dataspace, when an instance is not available (Addition). + """ + dataspace = instance.dataspace if instance else request.user.dataspace + license_qs = License.objects.scope(dataspace).filter(is_active=True) + return license_qs.data_for_expression_builder() + + def setup_license_builder(self, request, instance=None): + add_client_data( + request, + license_data=self.get_license_data_for_builder(request, instance), + expression_field_name=self.expression_field_name, + ) + + def add_view(self, request, form_url="", extra_context=None): + self.setup_license_builder(request) + return super().add_view(request, form_url, extra_context) + + def change_view(self, request, object_id, form_url="", extra_context=None): + response = super().change_view(request, object_id, form_url, extra_context) + + context_data = response.context_data if hasattr(response, "context_data") else None + instance = context_data.get("original") if context_data else None + self.setup_license_builder(request, instance) + + return response + + +@admin.register(ComponentType, site=dejacode_site) +class ComponentTypeAdmin(DataspacedAdmin): + list_display = ( + "label", + "notes", + "get_dataspace", + ) + search_fields = ( + "label", + "notes", + ) + + short_description = _("A component type provides a label to filter and sort components.") + + long_description = _( + "Your dataspace has the specific component types that meet the " + "filtering and sorting requirements of your business." + ) + + +class BaseStatusAdmin(DataspacedAdmin): + list_display = ( + "label", + "text", + "default_on_addition", + "get_dataspace", + ) + search_fields = ( + "label", + "text", + ) + fieldsets = (("", {"fields": ("label", "text", "default_on_addition", "dataspace", "uuid")}),) + + +@admin.register(ComponentStatus, site=dejacode_site) +class ComponentStatusAdmin(BaseStatusAdmin): + short_description = _( + "The Component Status allows to indicate the level of review that has been performed on " + "a Component." + ) + + long_description = _( + "The Component Status can be used within a Dataspace to communicate the current stage " + "of the review process and whether additional review is required." + ) + + +@admin.register(ComponentKeyword, site=dejacode_site) +class ComponentKeywordAdmin(DataspacedAdmin): + list_display = ("label", "description") + search_fields = ("label", "description") + fieldsets = (("", {"fields": ("label", "description", "dataspace", "uuid")}),) + list_filter = DataspacedAdmin.list_filter + (MissingInFilter,) + + short_description = _( + "Component Keywords support the classification of components, providing users with " + "additional component search criteria." + ) + + long_description = _( + "You can define as many Component Keywords as you need to meet the requirements of " + "your users. Note that one or more keywords can be assigned to each component." + ) + + +@admin.register(AcceptableLinkage, site=dejacode_site) +class AcceptableLinkageAdmin(DataspacedAdmin): + short_description = _( + "Acceptable linkages identify the different kinds of interactions that " + "can exist as integrations between third-party software components and " + "your organization products." + ) + long_description = _( + "Common linkages include Dynamic Linkage, Static Linkage, Stand Alone, " + "etc. but your legal review team can also define specific linkages " + "that are appropriate for your organization's software platforms." + ) + list_display = ("label", "description") + search_fields = ("label", "description") + fieldsets = (("", {"fields": ("label", "description", "dataspace", "uuid")}),) + + def get_readonly_fields(self, request, obj=None): + """Force `label` as readonly on edit.""" + readonly_fields = super().get_readonly_fields(request, obj) + if obj: + readonly_fields += ("label",) + return readonly_fields + + def has_delete_permission(self, request, obj=None): + perm = super().has_delete_permission(request, obj) + # Not deletable once at least 1 Component reference this entry + if not perm or (obj and obj.has_references()): + return False + return True + + +@admin.register(Component, site=dejacode_site) +class ComponentAdmin( + AwesompleteAdminMixin, + LicenseExpressionBuilderAdminMixin, + ChangelistPopupPermissionMixin, + DataspacedAdmin, +): + list_display = ( + get_hierarchy_link, + "changelist_view_on_site", + AsNaturalTime("last_modified_date", short_description="Last modified"), + "name", + "version", + "license_expression", + "copyright", + AsURL("homepage_url", short_description="Homepage URL"), + AsLink("owner"), + AsJoinList("keywords", "
", short_description="Keywords"), + "configuration_status", + AsLinkList("packages", "component", qs_limit=5, html_class="width300 word-break"), + "cpe", + "project", + "is_active", + "usage_policy", + "completion_level_pct", + "curation_level", + "primary_language", + "type", + "get_dataspace", + ) + list_display_links = ( + "name", + "version", + ) + list_filter = ( + ("owner", RelatedLookupListFilter), + ("licenses", RelatedLookupListFilter), + DataspaceFilter, + ReportingQueryListFilter, + ("type", LimitToDataspaceListFilter), + ("configuration_status", LimitToDataspaceListFilter), + ("completion_level", LevelFieldListFilter), + ("curation_level", LevelFieldListFilter), + "is_active", + ("usage_policy", LimitToDataspaceListFilter), + MissingInFilter, + ) + search_fields = ( + "name", + "version", + "owner__name", + "copyright", + "homepage_url", + "owner__alias", + "primary_language", + "packages__filename", + "project", + ) + ordering = ("-last_modified_date",) + fieldsets = [ + ( + None, + { + "fields": ( + "name", + "version", + "owner", + "copyright", + "holder", + "license_expression", + "reference_notes", + "release_date", + "description", + "keywords", + "homepage_url", + "vcs_url", + "code_view_url", + "bug_tracking_url", + "primary_language", + "cpe", + "project", + "codescan_identifier", + "type", + ) + }, + ), + ( + "Notice", + { + "classes": ("grp-collapse grp-open",), + "fields": ( + "notice_text", + "is_license_notice", + "is_copyright_notice", + "is_notice_in_codebase", + "notice_filename", + "notice_url", + "website_terms_of_use", + "dependencies", + ), + }, + ), + ( + "Usage Policy", + { + "classes": ("grp-collapse grp-open",), + "fields": ( + "usage_policy", + "guidance", + "legal_reviewed", + "approval_reference", + "distribution_formats_allowed", + "acceptable_linkages", + "export_restrictions", + "approved_download_location", + "approved_community_interaction", + ), + }, + ), + ( + "Configuration", + { + "classes": ("grp-collapse grp-open",), + "fields": ( + "configuration_status", + "is_active", + "curation_level", + "completion_level", + "admin_notes", + ), + }, + ), + ( + "Legal", + { + "classes": ("grp-collapse grp-open",), + "fields": ( + "ip_sensitivity_approved", + "affiliate_obligations", + "affiliate_obligation_triggers", + "concluded_license", + "legal_comments", + "sublicense_allowed", + "express_patent_grant", + "covenant_not_to_assert", + "indemnification", + ), + }, + ), + ("", {"classes": ("placeholder related_children-group",), "fields": ()}), + ("", {"classes": ("placeholder componentassignedpackage_set-group",), "fields": ()}), + ( + "", + { + "classes": ("placeholder dje-externalreference-content_type-object_id-group",), + "fields": (), + }, + ), + get_additional_information_fieldset(pre_fields=("urn_link",)), + ] + raw_id_fields = ("owner",) + autocomplete_lookup_fields = {"fk": ["owner"]} + # We have to use 'completion_level' rather than the 'completion_level_pct' + # callable to keep the help_text available during render in the template. + readonly_fields = DataspacedAdmin.readonly_fields + ("urn_link", "completion_level") + form = ComponentAdminForm + inlines = [ + SubcomponentChildInline, + ComponentAssignedPackageInline2, + ExternalReferenceInline, + ] + change_form_template = "admin/component_catalog/component/change_form.html" + delete_confirmation_template = "admin/component_catalog/component/delete_confirmation.html" + delete_selected_confirmation_template = ( + "admin/component_catalog/component/delete_selected_confirmation.html" + ) + actions = [ + "copy_to", + "compare_with", + "check_updates_in_reference", + "check_newer_version_in_reference", + "add_to_product", + "set_policy", + ] + importer_class = ComponentImporter + navigation_buttons = True + mass_update_form = ComponentMassUpdateForm + view_on_site = DataspacedAdmin.changeform_view_on_site + content_type_scope_fields = ["usage_policy"] + awesomplete_data = {"primary_language": PROGRAMMING_LANGUAGES} + + short_description = _( + "A component is any software-related object. Any component can contain " + "one or more subcomponents." + ) + + long_description = _("Component Name + Component Version must be unique.") + + def get_queryset(self, request): + return ( + super() + .get_queryset(request) + .select_related( + "type", + "owner", + "configuration_status", + "usage_policy", + ) + .prefetch_related( + "componentassignedlicense_set__license", + "packages", + "related_parents", + "related_children", + ) + ) + + @admin.display( + ordering="completion_level", + description="Completion level", + ) + def completion_level_pct(self, obj): + return format_html("{}%", obj.completion_level) + + def response_change(self, request, obj): + """ + Add a warning message in the response including links to the changelist of impacted + objects in case of license_expression invalidity on relations. + """ + response = super().response_change(request, obj) + errors = validate_expression_on_relations(obj) + changelist_links = [] + + for model_class, ids in errors.items(): + opts = model_class._meta + url = reverse(f"admin:{opts.app_label}_{opts.model_name}_changelist") + href = "{}?{}".format(url, urlencode({"id__in": ",".join(str(id_) for id_ in ids)})) + changelist_links.append( + CHANGELIST_LINK_TEMPLATE.format(href, len(ids), opts.verbose_name_plural) + ) + + if changelist_links: + msg = ( + "This license change impacts component usage in a Product or in another " + "Component.
{}".format(", ".join(changelist_links)) + ) + self.message_user(request, format_html(msg), messages.WARNING) + + return response + + def save_related(self, request, form, formsets, change): + """ + Update the completion_level at the end of the saving process, when + all the m2m and related are saved too. + """ + super().save_related(request, form, formsets, change) + # Update the completion_level once everything including m2m were saved + form.instance.update_completion_level() + + def get_urls(self): + info = self.model._meta.app_label, self.model._meta.model_name + + urls = [ + path( + "add_to_product/", + self.admin_site.admin_view(ComponentAddToProductAdminView.as_view()), + name="{}_{}_add_to_product".format(*info), + ), + path( + "set_policy/", + self.admin_site.admin_view(SetComponentPolicyView.as_view()), + name="{}_{}_set_policy".format(*info), + ), + ] + + return urls + super().get_urls() + + @admin.display(description=_("Add the selected components to a product")) + def add_to_product(self, request, queryset): + """Add the selected Component object(s) to a Product.""" + return self.base_action_with_redirect(request, queryset, "add_to_product") + + @admin.display(description=_("Set usage policy from licenses")) + def set_policy(self, request, queryset): + return self.base_action_with_redirect(request, queryset, "set_policy") + + def get_actions(self, request): + actions = super().get_actions(request) + is_another_dataspace = DataspaceFilter.parameter_name in request.GET + has_perm = request.user.has_perm("product_portfolio.add_productcomponent") + if (is_another_dataspace or not has_perm) and "add_to_product" in actions: + del actions["add_to_product"] + if is_another_dataspace and "set_policy" in actions: + del actions["set_policy"] + return actions + + def log_deletion(self, request, object, object_repr): + """ + Add the option to delete associated `Package` instances. + We use this method rather than `self.delete_model()` since we want to support both + the delete_view and the `delete_selected` action. + """ + super().log_deletion(request, object, object_repr) + if request.POST.get("delete_packages"): + object.packages.all().delete() + + def changeform_view(self, request, object_id=None, form_url="", extra_context=None): + """ + Catch FormDataOutdated exception that may be raised in get_inline_formsets(). + If an issue with the inline_formsets data is detected, for example an inline relation has + been deleted from another view since this page was opened, the page if refresh to its + initial state. + """ + try: + return super().changeform_view(request, object_id, form_url, extra_context) + except FormDataOutdated: + messages.error( + request, "Form data outdated or inconsistent. " "The form data has been refreshed." + ) + return redirect(request.path) + + def get_inline_formsets(self, request, formsets, inline_instances, obj=None): + """ + Ensure that the QuerySet count and the initial_form_count is equal for each + inline_formset. + If not, the FormDataOutdated is raised to be catch in the changeform_view. + """ + inline_formsets = super().get_inline_formsets(request, formsets, inline_instances, obj) + + for inline_formset in inline_formsets: + if not inline_formset.formset.is_bound: + continue + + qs_count = inline_formset.formset.get_queryset().count() + if qs_count != inline_formset.formset.initial_form_count(): + raise FormDataOutdated + + return inline_formsets + + @staticmethod + def _get_initial_from_related_instance(instance): + fields = [ + "name", + "version", + "description", + "release_date", + "primary_language", + "project", + "license_expression", + "copyright", + "notice_text", + "homepage_url", + "reference_notes", + "dependencies", + ] + + initial = {} + for field in fields: + value = getattr(instance, field, None) + if value: + initial[field] = value + + return initial + + def get_changeform_initial_data(self, request): + """ + Set initial values from the Package instance when adding a Component + from a Package changeform view. + """ + initial = super().get_changeform_initial_data(request) + + instance = get_instance_from_referer(request) + # Limit the feature to get values from Package instance only. + if instance and isinstance(instance, Package): + initial.update(self._get_initial_from_related_instance(instance)) + + return initial + + +@admin.register(Subcomponent, site=dejacode_site) +class SubcomponentAdmin(LicenseExpressionBuilderAdminMixin, DataspacedAdmin): + list_display = ( + "__str__", + AsLink("parent"), + AsLink("child"), + "license_expression", + "usage_policy", + "purpose", + "is_deployed", + "is_modified", + "get_dataspace", + ) + search_fields = ( + "parent__name", + "child__name", + ) + list_filter = ( + ("parent", ParentRelatedLookupListFilter), + DataspaceFilter, + ("usage_policy", LimitToDataspaceListFilter), + "purpose", + ReportingQueryListFilter, + ) + raw_id_fields = ( + "parent", + "child", + ) + autocomplete_lookup_fields = {"fk": ["parent", "child"]} + fieldsets = ( + (None, {"fields": ["parent"]}), + (None, {"fields": SubcomponentChildInline.fieldsets[0][1]["fields"]}), + get_additional_information_fieldset(), + ) + form = SubcomponentAdminForm + mass_update_form = SubcomponentMassUpdateForm + importer_class = SubcomponentImporter + activity_log = False + navigation_buttons = True + identifier_fields_warning = False + content_type_scope_fields = ["usage_policy"] + actions = ["set_policy"] + actions_to_remove = ["copy_to", "compare_with", "delete_selected"] + + short_description = _( + "A subcomponent relationship identifies the dependency of a Parent " + "component on another component." + ) + + def get_queryset(self, request): + qs = super().get_queryset(request) + return qs.select_related( + "parent", + "child", + "usage_policy", + ).prefetch_related( + "licenses", + ) + + def get_urls(self): + info = self.model._meta.app_label, self.model._meta.model_name + urls = [ + path( + "set_policy/", + self.admin_site.admin_view(SetSubcomponentPolicyView.as_view()), + name="{}_{}_set_policy".format(*info), + ), + ] + return urls + super().get_urls() + + @admin.display(description=_("Set usage policy from components")) + def set_policy(self, request, queryset): + return self.base_action_with_redirect(request, queryset, "set_policy") + + +class HideInlinesInPopupMixin: + """ + Mixin class hides inlines when the page is in a popup. + + When creating new objects through a popup in the admin, it can be + distracting to see inline forms. + + For example if you are on the ``Component`` change page and get to the + ``Package`` add page via the popup for adding a package to the component, you will + see an inline form for adding associated components to the package. + """ + + def get_inline_instances(self, request, obj=None): + is_popup = any( + [ + IS_POPUP_VAR in request.GET, + IS_POPUP_VAR in QueryDict(request.GET.get("_changelist_filters")), + ] + ) + + if is_popup: + return [] + + return super().get_inline_instances(request, obj) + + +@admin.register(Package, site=dejacode_site) +class PackageAdmin( + HideInlinesInPopupMixin, + AwesompleteAdminMixin, + ChangelistPopupPermissionMixin, + LicenseExpressionBuilderAdminMixin, + DataspacedAdmin, +): + short_description = ( + "A Package is a specific software code archive, identified by a combination " + "of its filename and a download URL or a package URL (purl) or both." + ) + long_description = ( + "Multiple Packages can be associated with a Component, usually to identify the " + "various source and binary formats available from the component project. " + "Packages can also be associated directly with one of your own Products." + ) + list_display = ( + "changelist_view_on_site", + AsNaturalTime("last_modified_date", short_description="Last modified"), + "identifier", + "license_expression", + AsURL("download_url"), + "filename", + AsJoinList("keywords", "
", short_description="Keywords"), + "components_links", + "cpe", + "project", + "usage_policy", + "sha1", + "md5", + "size", + "release_date", + "package_url", + "get_dataspace", + ) + list_display_links = ("identifier",) + search_fields = ("filename", "download_url", "project") + ordering = ("-last_modified_date",) + list_filter = ( + ("component", HierarchyRelatedLookupListFilter), + ("licenses", RelatedLookupListFilter), + DataspaceFilter, + ReportingQueryListFilter, + "release_date", + ("component", IsNullFieldListFilter), + ("usage_policy", LimitToDataspaceListFilter), + MissingInFilter, + ) + inlines = [ + ComponentAssignedPackageInline, + ExternalReferenceInline, + ] + fieldsets = [ + ( + None, + { + "fields": ( + "filename", + "download_url", + "size", + "release_date", + "primary_language", + "cpe", + "description", + "keywords", + "project", + "notes", + "usage_policy", + ) + }, + ), + ( + "Package URL", + { + "fields": ( + "package_url", + "type", + "namespace", + "name", + "version", + "qualifiers", + "subpath", + "inferred_url", + ) + }, + ), + ("", {"classes": ("placeholder componentassignedpackage_set-group",), "fields": ()}), + ( + "Terms", + { + "fields": ( + "license_expression", + "copyright", + "holder", + "author", + "reference_notes", + "notice_text", + "dependencies", + ) + }, + ), + ( + "URLs", + { + "fields": ( + "homepage_url", + "vcs_url", + "code_view_url", + "bug_tracking_url", + "repository_homepage_url", + "repository_download_url", + "api_data_url", + ) + }, + ), + ( + "Checksums", + { + "fields": ( + "sha1", + "sha256", + "sha512", + "md5", + ) + }, + ), + ( + "Others", + { + "fields": ( + "declared_license", + "parties", + "datasource_id", + "file_references", + ) + }, + ), + ( + "", + { + "classes": ("placeholder dje-externalreference-content_type-object_id-group",), + "fields": (), + }, + ), + get_additional_information_fieldset(), + ] + readonly_fields = DataspacedAdmin.readonly_fields + ("package_url", "inferred_url") + form = PackageAdminForm + importer_class = PackageImporter + mass_update_form = PackageMassUpdateForm + actions = [ + "copy_to", + "compare_with", + "check_updates_in_reference", + "add_to_product", + "set_policy", + "collect_data_action", + "set_purl", + ] + navigation_buttons = True + content_type_scope_fields = ["usage_policy"] + change_form_template = "admin/component_catalog/package/change_form.html" + change_list_template = "admin/component_catalog/package/change_list.html" + view_on_site = DataspacedAdmin.changeform_view_on_site + awesomplete_data = {"primary_language": PROGRAMMING_LANGUAGES} + + def get_queryset(self, request): + return ( + super() + .get_queryset(request) + .select_related( + "usage_policy", + ) + .prefetch_related( + "componentassignedpackage_set__component", + ) + ) + + def get_urls(self): + info = self.model._meta.app_label, self.model._meta.model_name + + urls = [ + path( + "add_to_product/", + self.admin_site.admin_view(PackageAddToProductAdminView.as_view()), + name="{}_{}_add_to_product".format(*info), + ), + path( + "set_policy/", + self.admin_site.admin_view(SetPackagePolicyView.as_view()), + name="{}_{}_set_policy".format(*info), + ), + ] + + return urls + super().get_urls() + + def changeform_view(self, request, object_id=None, form_url="", extra_context=None): + """ + Add the `show_save_and_collect_data` in the context. + See also dje/templates/admin/submit_line.html + """ + extra_context = extra_context or {} + extra_context.update({"show_save_and_collect_data": True}) + return super().changeform_view(request, object_id, form_url, extra_context) + + def save_model(self, request, obj, form, change): + """ + Trigger the `package_collect_data` task right after the `obj.save()` + when the "Save and collect data" button is used. + """ + super().save_model(request, obj, form, change) + + if "_collectdata" in request.POST and obj.download_url: + self.collect_data(request, obj) + + def collect_data(self, request, obj): + base_msg = "The SHA1, MD5, and Size fields collection" + if obj.download_url.startswith(("http://", "https://")): + transaction.on_commit(lambda: package_collect_data.delay(obj.id)) + if IS_POPUP_VAR not in request.POST: + msg = f"{base_msg} from {obj.download_url} is in progress." + self.message_user(request, msg, messages.SUCCESS) + else: + scheme = obj.download_url.split("://")[0] + msg = f'{base_msg} is not supported for the "{scheme}" scheme.' + self.message_user(request, msg, messages.WARNING) + + @admin.display(description=_("Add the selected packages to a product")) + def add_to_product(self, request, queryset): + """Add the selected Package object(s) to a Product.""" + return self.base_action_with_redirect(request, queryset, "add_to_product") + + @admin.display(description=_("Set usage policy from licenses")) + def set_policy(self, request, queryset): + return self.base_action_with_redirect(request, queryset, "set_policy") + + @admin.display(description=_('Set Package URL "purl" from the Download URL')) + def set_purl(self, request, queryset): + update_count = 0 + packages = queryset.exclude(download_url="") + + for package in packages.iterator(chunk_size=2000): + try: + updated = package.update_package_url( + user=request.user, + # Enforced save() in the admin context as using update() will + # trigger transaction exceptions on problematic purls. + save=True, + overwrite=False, + history=True, + ) + except Exception: + updated = False + + if updated: + update_count += 1 + + msg = f"{update_count} Package(s) updated with a Package URL." + self.message_user(request, msg, messages.SUCCESS) + + @admin.display(description=_("Collect data for selected packages")) + def collect_data_action(self, request, queryset): + count = queryset.count() + count_limit = 100 + if count > count_limit: + error_msg = f"Collect data is limited to {count_limit} objects at once" + self.message_user(request, error_msg, messages.ERROR) + return + + update_count = 0 + for package in queryset: + serialized_data = package.as_json() + # Not running as async to message user on completion. + update_fields = package.collect_data(save=False) + if update_fields: + package.last_modified_by = request.user + package.save() + message = f'Data collected for: {", ".join(update_fields)}.' + History.log_change(request.user, package, message, serialized_data) + update_count += 1 + + not_updated = count - update_count + msg = "The SHA1, MD5, and Size fields collection completed:" + if update_count: + msg += f"
{update_count} package(s) updated" + if not_updated: + msg += f"
{not_updated} package(s) NOT updated (data already set or URL unavailable)" + + self.message_user(request, format_html(msg), messages.SUCCESS) + + @admin.display( + ordering="component", + description="Components", + ) + def components_links(self, obj): + """ + Return all the Component instances related to a Package as links to their + admin edit view. + """ + component_links = [ + assigned_package.component.get_admin_link(target="_blank") + for assigned_package in obj.componentassignedpackage_set.all() + ] + return format_html("
".join(component_links)) + + @admin.display(description="Inferred URL") + def inferred_url(self, obj): + if inferred_url := obj.inferred_url: + return urlize_target_blank(inferred_url) + return "" + + def save_formset(self, request, form, formset, change): + """ + Update the completion_level on the related Component at the end of the saving process. + Addition, Edition, and Deletion are supported. + """ + super().save_formset(request, form, formset, change) + if formset.model == ComponentAssignedPackage: + for f in formset.forms: + # In the Edition case we need to update both old and new instances. + if "component" in f.changed_data: + old_instance = f.cleaned_data.get("id") + if old_instance and old_instance.component_id: + old_instance.component.update_completion_level() + + if f.instance.component_id: # Required for "Save as" cases. + f.instance.component.update_completion_level() diff --git a/component_catalog/api.py b/component_catalog/api.py new file mode 100644 index 00000000..33c2bd9c --- /dev/null +++ b/component_catalog/api.py @@ -0,0 +1,1027 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# DejaCode is a trademark of nexB Inc. +# SPDX-License-Identifier: AGPL-3.0-only +# See https://github.com/nexB/dejacode for support or download. +# See https://aboutcode.org for more information about AboutCode FOSS projects. +# + +from collections import defaultdict + +from django.db import transaction +from django.forms.widgets import HiddenInput + +import coreapi +import coreschema +import django_filters +from packageurl.contrib import url2purl +from packageurl.contrib.django.filters import PackageURLFilter +from rest_framework import serializers +from rest_framework.decorators import action +from rest_framework.fields import ListField +from rest_framework.response import Response +from rest_framework.schemas import AutoSchema + +from component_catalog.admin import ComponentAdmin +from component_catalog.admin import PackageAdmin +from component_catalog.fuzzy import FuzzyPackageNameSearch +from component_catalog.license_expression_dje import get_license_objects +from component_catalog.license_expression_dje import normalize_and_validate_expression +from component_catalog.models import Component +from component_catalog.models import ComponentKeyword +from component_catalog.models import Package +from component_catalog.models import Subcomponent +from dejacode_toolkit.download import DataCollectionException +from dejacode_toolkit.download import collect_package_data +from dejacode_toolkit.scancodeio import ScanCodeIO +from dje import tasks +from dje.api import CreateRetrieveUpdateListViewSet +from dje.api import DataspacedAPIFilterSet +from dje.api import DataspacedHyperlinkedRelatedField +from dje.api import DataspacedSerializer +from dje.api import DataspacedSlugRelatedField +from dje.api import ExternalReferenceSerializer +from dje.api import NameVersionHyperlinkedRelatedField +from dje.filters import LastModifiedDateFilter +from dje.filters import MultipleCharFilter +from dje.filters import MultipleUUIDFilter +from dje.filters import NameVersionFilter +from dje.models import History +from dje.models import external_references_prefetch +from dje.views import SendAboutFilesMixin +from license_library.models import License +from organization.api import OwnerEmbeddedSerializer + + +class LicenseSummaryMixin: + def get_licenses_summary(self, obj): + licenses = obj.licenses.all() + primary_license = obj.primary_license + + return [ + { + "key": license.key, + "short_name": license.short_name, + "name": license.name, + "category": license.category.label if license.category else None, + "type": license.category.license_type if license.category else None, + "is_primary": bool(len(licenses) == 1 or license.key == primary_license), + } + for license in licenses + ] + + +class ValidateLicenseExpressionMixin: + def validate_license_expression(self, value): + """ + Validate and return a normalized license expression string. + Raise a Django ValidationError exception on errors. + The expression is validated against all the dataspace licenses. + """ + if not value: + return value # Prevent from converting empty string '' into `None` + + licenses = License.objects.scope(self.dataspace).for_expression() + return normalize_and_validate_expression( + value, licenses, validate_known=True, include_available=False + ) + + +class LicenseChoicesExpressionMixin: + def get_license_choices_expression(self, obj): + if obj.has_license_choices: + return obj.license_choices_expression + + def get_license_choices(self, obj): + if not obj.has_license_choices: + return [] + + all_licenses = License.objects.scope(obj.dataspace) + choice_licenses = get_license_objects(obj.license_choices_expression, all_licenses) + + return [ + {"key": license.key, "short_name": license.short_name} for license in choice_licenses + ] + + +class PackageEmbeddedSerializer(DataspacedSerializer): + """ + Warning: Ideally, we should extend from `PackageSerializer` to avoid + code duplication, but we cannot here because of circular import issues. + """ + + absolute_url = serializers.SerializerMethodField() + + class Meta: + model = Package + fields = ( + "api_url", + "absolute_url", + "download_url", + "uuid", + "filename", + "md5", + "sha1", + "sha256", + "sha512", + "size", + "release_date", + "primary_language", + "cpe", + "description", + "keywords", + "project", + "notes", + "dependencies", + "copyright", + "holder", + "author", + "license_expression", + "reference_notes", + "homepage_url", + "vcs_url", + "code_view_url", + "bug_tracking_url", + "repository_homepage_url", + "repository_download_url", + "api_data_url", + "notice_text", + "package_url", + "type", + "namespace", + "name", + "version", + "qualifiers", + "subpath", + "created_date", + "last_modified_date", + ) + extra_kwargs = { + "api_url": { + "view_name": "api_v2:package-detail", + "lookup_field": "uuid", + }, + } + + +class KeywordsField(ListField): + """Create the provided Keyword value if non existing.""" + + def create_missing_keywords(self, keywords): + user = self.context["request"].user + dataspace = user.dataspace + + qs = ComponentKeyword.objects.scope(dataspace).filter(label__in=keywords) + existing_labels = qs.values_list("label", flat=True) + + for label in keywords: + if label not in existing_labels: + keyword = ComponentKeyword.objects.create( + label=label, + dataspace=dataspace, + ) + History.log_addition(user, keyword) + + def run_child_validation(self, data): + result = super().run_child_validation(data) + if result: + result = [value for value in result if value != ""] # Clean empty string + self.create_missing_keywords(keywords=result) + return result + + +class ComponentSerializer( + LicenseSummaryMixin, + LicenseChoicesExpressionMixin, + ValidateLicenseExpressionMixin, + DataspacedSerializer, +): + display_name = serializers.ReadOnlyField(source="__str__") + absolute_url = serializers.SerializerMethodField() + owner = DataspacedHyperlinkedRelatedField( + view_name="api_v2:owner-detail", + lookup_field="uuid", + allow_null=True, + required=False, + html_cutoff=10, + slug_field="name", + ) + owner_name = serializers.SerializerMethodField() + owner_abcd = OwnerEmbeddedSerializer( + source="owner", + read_only=True, + ) + type = DataspacedSlugRelatedField( + slug_field="label", + allow_null=True, + required=False, + ) + configuration_status = DataspacedSlugRelatedField( + slug_field="label", + allow_null=True, + required=False, + ) + usage_policy = DataspacedSlugRelatedField( + slug_field="label", + allow_null=True, + required=False, + scope_content_type=True, + ) + keywords = KeywordsField( + required=False, + ) + packages = DataspacedHyperlinkedRelatedField( + many=True, + view_name="api_v2:package-detail", + lookup_field="uuid", + required=False, + slug_field="uuid", + ) + packages_abcd = PackageEmbeddedSerializer( + source="packages", + many=True, + read_only=True, + ) + external_references = ExternalReferenceSerializer( + many=True, + read_only=True, + ) + licenses_summary = serializers.SerializerMethodField(source="get_licenses_summary") + license_choices_expression = serializers.SerializerMethodField( + source="get_license_choices_expression" + ) + license_choices = serializers.SerializerMethodField(source="get_license_choices") + + class Meta: + model = Component + fields = ( + "display_name", + "api_url", + "absolute_url", + "id", + "uuid", + "name", + "version", + "owner", + "owner_name", + "owner_abcd", + "copyright", + "holder", + "license_expression", + "reference_notes", + "release_date", + "description", + "homepage_url", + "vcs_url", + "code_view_url", + "bug_tracking_url", + "primary_language", + "cpe", + "project", + "codescan_identifier", + "type", + "notice_text", + "is_license_notice", + "is_copyright_notice", + "is_notice_in_codebase", + "notice_filename", + "notice_url", + "website_terms_of_use", + "dependencies", + "configuration_status", + "is_active", + "usage_policy", + "curation_level", + "completion_level", + "guidance", + "admin_notes", + "keywords", + "packages", + "packages_abcd", + "external_references", + "ip_sensitivity_approved", + "affiliate_obligations", + "affiliate_obligation_triggers", + "concluded_license", + "legal_comments", + "sublicense_allowed", + "express_patent_grant", + "covenant_not_to_assert", + "indemnification", + "legal_reviewed", + "approval_reference", + "distribution_formats_allowed", + "acceptable_linkages", + "export_restrictions", + "approved_download_location", + "approved_community_interaction", + "urn", + "licenses", + "licenses_summary", + "license_choices_expression", + "license_choices", + "created_date", + "last_modified_date", + ) + extra_kwargs = { + # The `default` value set on the model field is not accounted by DRF + # https://github.com/encode/django-rest-framework/issues/7469 + "is_active": {"default": True}, + "api_url": { + "view_name": "api_v2:component-detail", + "lookup_field": "uuid", + }, + "owner": { + "view_name": "api_v2:owner-detail", + "lookup_field": "uuid", + }, + "licenses": { + "view_name": "api_v2:license-detail", + "lookup_field": "uuid", + }, + "packages": { + "view_name": "api_v2:package-detail", + "lookup_field": "uuid", + }, + } + + def get_fields(self): + fields = super().get_fields() + if "completion_level" in fields: + fields["completion_level"].read_only = True + return fields + + def save(self, **kwargs): + instance = super().save(**kwargs) + instance.update_completion_level() + return instance + + def get_owner_name(self, obj): + if obj.owner: + return obj.owner.name + + +class ComponentFilterSet(DataspacedAPIFilterSet): + id = django_filters.NumberFilter( + help_text="Exact id.", + ) + uuid = MultipleUUIDFilter() + name = MultipleCharFilter( + help_text="Exact name. Multi-value supported.", + ) + version = django_filters.CharFilter( + help_text="Exact version.", + ) + version__lt = django_filters.CharFilter( + field_name="version", + lookup_expr="lt", + help_text="Version is lower than.", + ) + version__gt = django_filters.CharFilter( + field_name="version", + lookup_expr="gt", + help_text="Version is greater than.", + ) + primary_language = django_filters.CharFilter( + help_text="Exact primary language.", + ) + project = django_filters.CharFilter( + help_text="Exact project.", + ) + owner = MultipleCharFilter( + field_name="owner__name", + help_text="Exact owner name. Multi-value supported.", + ) + type = django_filters.CharFilter( + field_name="type__label", + help_text="Exact type label.", + ) + configuration_status = django_filters.CharFilter( + field_name="configuration_status__label", + help_text="Exact configuration status label.", + ) + usage_policy = django_filters.CharFilter( + field_name="usage_policy__label", + help_text="Exact usage policy label.", + ) + curation_level = django_filters.NumberFilter( + lookup_expr="gte", + help_text="Curation level is greater than or equal to", + ) + license_expression = django_filters.CharFilter( + lookup_expr="icontains", + help_text="License expression contains (case-insensitive).", + ) + keywords = django_filters.CharFilter( + lookup_expr="icontains", + help_text="Keyword label contains (case-insensitive)", + ) + last_modified_date = LastModifiedDateFilter() + name_version = NameVersionFilter( + label="Name:Version", + ) + + class Meta: + model = Component + fields = ( + # id is required for the add_to_product and license_expression builder features + "id", + "uuid", + "name", + "version", + "version__lt", + "version__gt", + "primary_language", + "project", + "owner", + "type", + "configuration_status", + "usage_policy", + "license_expression", + "is_active", + "legal_reviewed", + "curation_level", + "last_modified_date", + "name_version", + "keywords", + ) + + +class ComponentViewSet(CreateRetrieveUpdateListViewSet): + queryset = Component.objects.all() + serializer_class = ComponentSerializer + filterset_class = ComponentFilterSet + lookup_field = "uuid" + search_fields = ( + "name", + "version", + "copyright", + "homepage_url", + "project", + ) + search_fields_autocomplete = ( + "name", + "version", + ) + ordering_fields = ( + "name", + "version", + "copyright", + "license_expression", + "primary_language", + "project", + "codescan_identifier", + "type", + "configuration_status", + "usage_policy", + "curation_level", + "completion_level", + "created_date", + "last_modified_date", + ) + email_notification_on = ComponentAdmin.email_notification_on + allow_reference_access = True + + def get_queryset(self): + return ( + super() + .get_queryset() + .select_related( + "type", + "owner__dataspace", + "configuration_status", + ) + .prefetch_related( + "licenses__category", + "packages", + external_references_prefetch, + ) + ) + + +class ComponentEmbeddedSerializer(ComponentSerializer): + """ + All Component fields without the relation ones, + except for Owner that is included in PackageViewSet.queryset + prefetch_related for this purpose. + """ + + class Meta(ComponentSerializer.Meta): + fields = ( + "display_name", + "api_url", + "absolute_url", + "uuid", + "name", + "version", + "owner", + "owner_name", + "copyright", + "holder", + "license_expression", + "reference_notes", + "release_date", + "description", + "homepage_url", + "vcs_url", + "code_view_url", + "bug_tracking_url", + "primary_language", + "cpe", + "project", + "codescan_identifier", + "notice_text", + "is_license_notice", + "is_copyright_notice", + "is_notice_in_codebase", + "notice_filename", + "notice_url", + "website_terms_of_use", + "dependencies", + "is_active", + "curation_level", + "completion_level", + "guidance", + "admin_notes", + "ip_sensitivity_approved", + "affiliate_obligations", + "affiliate_obligation_triggers", + "concluded_license", + "legal_comments", + "sublicense_allowed", + "express_patent_grant", + "covenant_not_to_assert", + "indemnification", + "legal_reviewed", + "approval_reference", + "distribution_formats_allowed", + "acceptable_linkages", + "export_restrictions", + "approved_download_location", + "approved_community_interaction", + "urn", + "created_date", + "last_modified_date", + ) + + +class PackageSerializer( + LicenseSummaryMixin, + ValidateLicenseExpressionMixin, + LicenseChoicesExpressionMixin, + DataspacedSerializer, +): + display_name = serializers.ReadOnlyField(source="__str__") + absolute_url = serializers.SerializerMethodField() + components = ComponentEmbeddedSerializer( + source="component_set", + many=True, + read_only=True, + ) + external_references = ExternalReferenceSerializer( + many=True, + read_only=True, + ) + keywords = KeywordsField( + required=False, + ) + licenses_summary = serializers.SerializerMethodField(source="get_licenses_summary") + license_choices_expression = serializers.SerializerMethodField( + source="get_license_choices_expression" + ) + license_choices = serializers.SerializerMethodField(source="get_license_choices") + usage_policy = DataspacedSlugRelatedField( + slug_field="label", + allow_null=True, + required=False, + scope_content_type=True, + ) + collect_data = serializers.BooleanField( + write_only=True, + required=False, + allow_null=True, + ) + + class Meta: + model = Package + fields = ( + "display_name", + "api_url", + "absolute_url", + "id", + "download_url", + "uuid", + "filename", + "md5", + "sha1", + "sha256", + "sha512", + "size", + "release_date", + "primary_language", + "cpe", + "description", + "keywords", + "project", + "notes", + "usage_policy", + "dependencies", + "copyright", + "holder", + "author", + "license_expression", + "licenses", + "licenses_summary", + "license_choices_expression", + "license_choices", + "reference_notes", + "homepage_url", + "vcs_url", + "code_view_url", + "bug_tracking_url", + "repository_homepage_url", + "repository_download_url", + "api_data_url", + "notice_text", + "components", + "package_url", + "type", + "namespace", + "name", + "version", + "qualifiers", + "subpath", + "declared_license", + "parties", + "datasource_id", + "file_references", + "external_references", + "created_date", + "last_modified_date", + "collect_data", + ) + extra_kwargs = { + "api_url": { + "view_name": "api_v2:package-detail", + "lookup_field": "uuid", + }, + "licenses": { + "view_name": "api_v2:license-detail", + "lookup_field": "uuid", + }, + } + exclude_from_validate = [ + "collect_data", + ] + + def create(self, validated_data): + """Collect data, purl, and submit scan if `collect_data` is provided.""" + user = self.context["request"].user + + collect_data = validated_data.pop("collect_data", None) + download_url = validated_data.get("download_url") + + if collect_data and download_url: + try: + collected_data = collect_package_data(download_url) + except DataCollectionException: + collected_data = {} + + package_url = url2purl.get_purl(download_url) + if package_url: + collected_data.update(package_url.to_dict(encode=True, empty="")) + + validated_data.update(collected_data) + + package = super().create(validated_data) + + # Submit the scan if Package was properly created + scancodeio = ScanCodeIO(user) + if scancodeio.is_configured() and user.dataspace.enable_package_scanning: + # Ensure the task is executed after the transaction is successfully committed + transaction.on_commit( + lambda: tasks.scancodeio_submit_scan.delay( + uris=download_url, + user_uuid=user.uuid, + dataspace_uuid=user.dataspace.uuid, + ) + ) + + return package + + +class PackageAPIFilterSet(DataspacedAPIFilterSet): + id = django_filters.NumberFilter( + help_text="Exact id.", + ) + uuid = MultipleUUIDFilter() + download_url = django_filters.CharFilter( + help_text="Exact Download URL.", + ) + filename = MultipleCharFilter( + help_text="Exact filename. Multi-value supported.", + ) + type = django_filters.CharFilter( + lookup_expr="iexact", + help_text="Exact type. (case-insensitive)", + ) + namespace = django_filters.CharFilter( + lookup_expr="iexact", + help_text="Exact namespace. (case-insensitive)", + ) + name = MultipleCharFilter( + lookup_expr="iexact", + help_text="Exact name. Multi-value supported. (case-insensitive)", + ) + version = MultipleCharFilter( + help_text="Exact version. Multi-value supported.", + ) + md5 = MultipleCharFilter( + help_text="Exact MD5. Multi-value supported.", + ) + sha1 = MultipleCharFilter( + help_text="Exact SHA1. Multi-value supported.", + ) + size = django_filters.NumberFilter( + help_text="Exact size in bytes.", + ) + primary_language = django_filters.CharFilter( + help_text="Exact primary language.", + ) + license_expression = django_filters.CharFilter( + lookup_expr="icontains", + help_text="License expression contains (case-insensitive).", + ) + keywords = django_filters.CharFilter( + lookup_expr="icontains", + help_text="Keyword label contains (case-insensitive)", + ) + project = django_filters.CharFilter( + help_text="Exact project.", + ) + usage_policy = django_filters.CharFilter( + field_name="usage_policy__label", + help_text="Exact usage policy label.", + ) + last_modified_date = LastModifiedDateFilter() + fuzzy = FuzzyPackageNameSearch(widget=HiddenInput) + purl = PackageURLFilter(label="Package URL") + + class Meta: + model = Package + fields = ( + # id is required for the add_to_product and license_expression builder features + "id", + "uuid", + "download_url", + "filename", + "type", + "namespace", + "name", + "version", + "sha1", + "md5", + "size", + "primary_language", + "keywords", + "project", + "license_expression", + "usage_policy", + "last_modified_date", + "fuzzy", + "purl", + ) + + +def collect_create_scan(download_url, user): + package_qs = Package.objects.filter(download_url=download_url, dataspace=user.dataspace) + if package_qs.exists(): + return False + + try: + package_data = collect_package_data(download_url) + except DataCollectionException: + return False + + package_url = url2purl.get_purl(download_url) + if package_url: + package_data.update(package_url.to_dict(encode=True, empty="")) + + package = Package.create_from_data(user, package_data) + + scancodeio = ScanCodeIO(user) + if scancodeio.is_configured() and user.dataspace.enable_package_scanning: + tasks.scancodeio_submit_scan.delay( + uris=download_url, + user_uuid=user.uuid, + dataspace_uuid=user.dataspace.uuid, + ) + + return package + + +class PackageViewSet(SendAboutFilesMixin, CreateRetrieveUpdateListViewSet): + queryset = Package.objects.all() + serializer_class = PackageSerializer + filterset_class = PackageAPIFilterSet + lookup_field = "uuid" + search_fields = ( + "filename", + "project", + ) + search_fields_autocomplete = ( + "type", + "namespace", + "name", + "version", + "filename", + ) + ordering_fields = ( + "download_url", + "filename", + "size", + "release_date", + "primary_language", + "project", + "copyright", + "license_expression", + "usage_policy", + "created_date", + "last_modified_date", + ) + email_notification_on = PackageAdmin.email_notification_on + allow_reference_access = True + + def get_queryset(self): + return ( + super() + .get_queryset() + .prefetch_related( + "component_set__owner", + "licenses__category", + external_references_prefetch, + ) + ) + + @action(detail=True) + def about(self, request, uuid): + package = self.get_object() + return Response({"about_data": package.as_about_yaml()}) + + @action(detail=True) + def about_files(self, request, uuid): + package = self.get_object() + about_files = package.get_about_files() + filename = self.get_filename(package) + return self.get_zipped_response(about_files, filename) + + download_url_description = ( + "A single, or list of, Download URL(s).

" + 'cURL style: -d "download_url=url1&download_url=url2"

' + 'Python: data = {"download_url": ["url1", "url2"]}' + ) + + add_action_schema = AutoSchema( + manual_fields=[ + coreapi.Field( + "download_url", + required=True, + location="body", + schema=coreschema.String(description=download_url_description), + ), + ] + ) + + @action(detail=False, methods=["post"], name="Package Add", schema=add_action_schema) + def add(self, request): + """ + Alternative way to add a package providing only its `download_url`. + + Multiple URLs can be submitted through a single request. + + Note that this feature is intended only for publicly available open + source packages, not your private code. + + DejaCode will automatically collect the `filename`, `sha1`, `md5`, and + `size` and apply them to the package definition. + The `package_url` will also be generated when possible. + + If package scanning is enabled in your dataspace, DejaCode will also + submit the package to ScanCode.io and the results will be returned to + the "Scan" detail tab of the package when that scan is complete. + """ + download_urls = request.POST.getlist("download_url") + if not download_urls: + error = {"download_url": "This field is required."} + return Response(error, status=400) + + results = defaultdict(list) + for url in download_urls: + url = url.strip() + package = collect_create_scan(url, request.user) + if package: + results["added"].append(url) + else: + results["failed"].append(url) + + return Response(results) + + +class SubcomponentSerializer( + ValidateLicenseExpressionMixin, + DataspacedSerializer, +): + parent = NameVersionHyperlinkedRelatedField( + view_name="api_v2:component-detail", + lookup_field="uuid", + allow_null=False, + ) + child = NameVersionHyperlinkedRelatedField( + view_name="api_v2:component-detail", + lookup_field="uuid", + allow_null=False, + ) + usage_policy = DataspacedSlugRelatedField( + slug_field="label", + allow_null=True, + required=False, + scope_content_type=True, + ) + + class Meta: + model = Subcomponent + fields = ( + "api_url", + "uuid", + "parent", + "child", + "license_expression", + "reference_notes", + "usage_policy", + "purpose", + "notes", + "is_deployed", + "is_modified", + "extra_attribution_text", + "package_paths", + "created_date", + "last_modified_date", + ) + extra_kwargs = { + # The `default` value set on the model field is not accounted by DRF + # https://github.com/encode/django-rest-framework/issues/7469 + "is_deployed": {"default": True}, + "api_url": { + "view_name": "api_v2:subcomponent-detail", + "lookup_field": "uuid", + }, + "component": { + "view_name": "api_v2:component-detail", + "lookup_field": "uuid", + }, + } + + +class SubcomponentFilterSet(DataspacedAPIFilterSet): + uuid = MultipleUUIDFilter() + parent = NameVersionFilter( + name_field_name="parent__name", + version_field_name="parent__version", + ) + child = NameVersionFilter( + name_field_name="child__name", + version_field_name="child__version", + ) + last_modified_date = LastModifiedDateFilter() + + class Meta: + model = Subcomponent + fields = ( + "uuid", + "parent", + "child", + "last_modified_date", + ) + + +class SubcomponentViewSet(CreateRetrieveUpdateListViewSet): + queryset = Subcomponent.objects.all() + serializer_class = SubcomponentSerializer + filterset_class = SubcomponentFilterSet + lookup_field = "uuid" + search_fields = ("notes",) + ordering_fields = ( + "license_expression", + "created_date", + "last_modified_date", + ) + + def get_queryset(self): + return ( + super() + .get_queryset() + .select_related( + "parent", + "child", + ) + ) diff --git a/component_catalog/apps.py b/component_catalog/apps.py new file mode 100644 index 00000000..5c1f2e8e --- /dev/null +++ b/component_catalog/apps.py @@ -0,0 +1,15 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# DejaCode is a trademark of nexB Inc. +# SPDX-License-Identifier: AGPL-3.0-only +# See https://github.com/nexB/dejacode for support or download. +# See https://aboutcode.org for more information about AboutCode FOSS projects. +# + +from django.apps import AppConfig +from django.utils.translation import gettext_lazy as _ + + +class ComponentCatalogConfig(AppConfig): + name = "component_catalog" + verbose_name = _("Component Catalog") diff --git a/component_catalog/filters.py b/component_catalog/filters.py new file mode 100644 index 00000000..f8eb067c --- /dev/null +++ b/component_catalog/filters.py @@ -0,0 +1,242 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# DejaCode is a trademark of nexB Inc. +# SPDX-License-Identifier: AGPL-3.0-only +# See https://github.com/nexB/dejacode for support or download. +# See https://aboutcode.org for more information about AboutCode FOSS projects. +# + +from django import forms +from django.contrib.admin.options import IncorrectLookupParameters +from django.utils.functional import cached_property +from django.utils.translation import gettext_lazy as _ + +import django_filters + +from component_catalog.models import Component +from component_catalog.models import ComponentKeyword +from component_catalog.models import Package +from component_catalog.programming_languages import PROGRAMMING_LANGUAGES +from dje.filters import DataspacedFilterSet +from dje.filters import DefaultOrderingFilter +from dje.filters import HasRelationFilter +from dje.filters import MatchOrderedSearchFilter +from dje.filters import RelatedLookupListFilter +from dje.widgets import BootstrapSelectMultipleWidget +from dje.widgets import DropDownRightWidget +from dje.widgets import SortDropDownWidget +from license_library.models import License + + +class ComponentFilterSet(DataspacedFilterSet): + related_only = [ + "licenses", + "primary_language", + "usage_policy", + ] + q = MatchOrderedSearchFilter( + label=_("Search"), + match_order_fields=["name"], + search_fields=[ + "name", + "version", + ], + widget=forms.widgets.HiddenInput, + ) + sort = DefaultOrderingFilter( + label=_("Sort"), + fields=[ + "name", + "version", + "license_expression", + "primary_language", + "owner", + "created_date", + "last_modified_date", + ], + field_labels={ + "primary_language": "Language", + }, + empty_label="Last modified (default)", + widget=SortDropDownWidget, + ) + primary_language = django_filters.MultipleChoiceFilter( + label=_("Language"), + choices=[(language, language) for language in PROGRAMMING_LANGUAGES], + widget=BootstrapSelectMultipleWidget( + search_placeholder="Search languages", + ), + ) + licenses = django_filters.ModelMultipleChoiceFilter( + label=_("License"), + field_name="licenses__key", + to_field_name="key", + queryset=License.objects.all().only("key", "short_name", "dataspace"), + widget=BootstrapSelectMultipleWidget( + search_placeholder="Search licenses", + ), + ) + keywords = django_filters.ModelMultipleChoiceFilter( + label=_("Keyword"), + to_field_name="label", + lookup_expr="contains", + queryset=ComponentKeyword.objects.all().only("label", "dataspace"), + widget=BootstrapSelectMultipleWidget( + search_placeholder="Search keywords", + ), + ) + + class Meta: + model = Component + fields = [ + "usage_policy", + "type", + "primary_language", + "licenses", + "keywords", + ] + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.filters["usage_policy"].extra["to_field_name"] = "label" + self.filters["usage_policy"].extra["widget"] = DropDownRightWidget() + self.filters["type"].extra["to_field_name"] = "label" + self.filters["type"].extra["widget"] = DropDownRightWidget() + + @cached_property + def sort_value(self): + return self.form["sort"].value() + + def has_sort_by(self, field_name): + sort_value = self.sort_value + if sort_value and (field_name in sort_value or f"-{field_name}" in sort_value): + return True + + @cached_property + def show_created_date(self): + return self.has_sort_by("created_date") + + @cached_property + def show_last_modified_date(self): + return not self.sort_value or self.has_sort_by("last_modified_date") + + +class HierarchyRelatedLookupListFilter(RelatedLookupListFilter): + """Limit the QuerySet to the current Component and all his direct children.""" + + def queryset(self, request, queryset): + if not self.lookup_val: + return queryset + + try: + instance = Component.objects.get(id=self.lookup_val) + except Component.DoesNotExist as e: + raise IncorrectLookupParameters(e) + + ids = list(instance.children.values_list("id", flat=True)) + [instance.id] + return queryset.filter(component__id__in=ids) + + +class ParentRelatedLookupListFilter(RelatedLookupListFilter): + """Filter the changelist in the pop-up to only Components that have children.""" + + lookup_filters = ["children__isnull=False"] + + +class PackageSearchFilter(MatchOrderedSearchFilter): + def filter(self, qs, value): + """Add searching on provided PackageURL identifier.""" + if not value: + return qs + + is_purl = "/" in value + if is_purl: + return qs.for_package_url(value) + + return super().filter(qs, value) + + +class PackageFilterSet(DataspacedFilterSet): + q = PackageSearchFilter( + label=_("Search"), + match_order_fields=["filename"], + search_fields=[ + "filename", + "type", + "namespace", + "name", + "version", + "download_url", + "sha1", + "md5", + ], + widget=forms.widgets.HiddenInput, + ) + primary_language = django_filters.MultipleChoiceFilter( + label=_("Language"), + choices=[(language, language) for language in PROGRAMMING_LANGUAGES], + widget=BootstrapSelectMultipleWidget( + search_placeholder="Search languages", + ), + ) + licenses = django_filters.ModelMultipleChoiceFilter( + label=_("License"), + field_name="licenses__key", + to_field_name="key", + queryset=License.objects.all().only("key", "short_name", "dataspace"), + widget=BootstrapSelectMultipleWidget( + search_placeholder="Search licenses", + ), + ) + component = HasRelationFilter( + label=_("Components"), + widget=DropDownRightWidget, + ) + sort = DefaultOrderingFilter( + label=_("Sort"), + fields=[ + "sortable_identifier", + "filename", + "download_url", + "license_expression", + "primary_language", + "created_date", + "last_modified_date", + ], + field_labels={ + "sortable_identifier": "Identifier", + "primary_language": "Language", + }, + empty_label="Last modified (default)", + widget=SortDropDownWidget, + ) + + class Meta: + model = Package + fields = [ + "q", + "component", + "usage_policy", + ] + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.filters["usage_policy"].extra["to_field_name"] = "label" + self.filters["usage_policy"].extra["widget"] = DropDownRightWidget() + + @cached_property + def sort_value(self): + return self.form["sort"].value() + + def has_sort_by(self, field_name): + sort_value = self.sort_value + if sort_value and (field_name in sort_value or f"-{field_name}" in sort_value): + return True + + @cached_property + def show_created_date(self): + return self.has_sort_by("created_date") + + @cached_property + def show_last_modified_date(self): + return not self.sort_value or self.has_sort_by("last_modified_date") diff --git a/component_catalog/forms.py b/component_catalog/forms.py new file mode 100644 index 00000000..f5304fc7 --- /dev/null +++ b/component_catalog/forms.py @@ -0,0 +1,1124 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# DejaCode is a trademark of nexB Inc. +# SPDX-License-Identifier: AGPL-3.0-only +# See https://github.com/nexB/dejacode for support or download. +# See https://aboutcode.org for more information about AboutCode FOSS projects. +# + +from django import forms +from django.contrib import messages +from django.contrib.admin.widgets import AdminURLFieldWidget +from django.db import transaction +from django.forms import modelform_factory +from django.urls import reverse +from django.urls import reverse_lazy +from django.utils.functional import cached_property + +import packageurl +from crispy_forms.helper import FormHelper +from crispy_forms.layout import HTML +from crispy_forms.layout import Field +from crispy_forms.layout import Fieldset +from crispy_forms.layout import Layout +from crispy_forms.layout import Submit + +from component_catalog.license_expression_dje import LicenseExpressionFormMixin +from component_catalog.models import PACKAGE_URL_FIELDS +from component_catalog.models import AcceptableLinkage +from component_catalog.models import Component +from component_catalog.models import ComponentAssignedPackage +from component_catalog.models import ComponentKeyword +from component_catalog.models import Package +from component_catalog.models import Subcomponent +from component_catalog.programming_languages import PROGRAMMING_LANGUAGES +from component_catalog.widgets import UsagePolicyWidgetWrapper +from dejacode_toolkit.scancodeio import ScanCodeIO +from dje import tasks +from dje.forms import DataspacedAdminForm +from dje.forms import DataspacedModelForm +from dje.forms import DefaultOnAdditionLabelMixin +from dje.forms import Group +from dje.forms import JSONListChoiceField +from dje.forms import OwnerChoiceField +from dje.forms import autocomplete_placeholder +from dje.mass_update import DejacodeMassUpdateForm +from dje.models import History +from dje.widgets import AdminAwesompleteInputWidget +from dje.widgets import AutocompleteInput +from dje.widgets import AwesompleteInputWidget +from dje.widgets import DatePicker +from product_portfolio.models import Product +from product_portfolio.models import ProductComponent +from product_portfolio.models import ProductPackage + + +class SetKeywordsChoicesFormMixin: + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + keywords_field = self.fields.get("keywords") + if keywords_field: + keywords_qs = ComponentKeyword.objects.scope(self.dataspace) + labels = keywords_qs.values_list("label", flat=True) + keywords_field.choices = [(label, label) for label in labels] + keywords_field.widget.attrs.update( + { + "data-list": ", ".join(labels), + } + ) + + +class ComponentForm( + LicenseExpressionFormMixin, + DefaultOnAdditionLabelMixin, + SetKeywordsChoicesFormMixin, + DataspacedModelForm, +): + default_on_addition_fields = ["configuration_status"] + save_as = True + clone_m2m_classes = [ + ComponentAssignedPackage, + Subcomponent, + ] + color_initial = True + + keywords = JSONListChoiceField( + required=False, + widget=AwesompleteInputWidget(attrs=autocomplete_placeholder), + ) + + packages_ids = forms.CharField( + widget=forms.HiddenInput, + required=False, + ) + + class Meta: + model = Component + fields = [ + "name", + "version", + "owner", + "copyright", + "holder", + "notice_text", + "license_expression", + "release_date", + "description", + "homepage_url", + "bug_tracking_url", + "code_view_url", + "vcs_url", + "primary_language", + "cpe", + "configuration_status", + "keywords", + "notice_text", + "is_license_notice", + "is_copyright_notice", + "is_notice_in_codebase", + "notice_filename", + "notice_url", + "dependencies", + "usage_policy", + "packages_ids", + ] + field_classes = { + "owner": OwnerChoiceField, + } + widgets = { + "copyright": forms.Textarea(attrs={"rows": 2}), + "notice_text": forms.Textarea(attrs={"rows": 2}), + "description": forms.Textarea(attrs={"rows": 2}), + "holder": forms.Textarea(attrs={"rows": 2}), + "owner": AutocompleteInput( + attrs={ + "data-api_url": reverse_lazy("api_v2:owner-list"), + }, + display_link=False, + display_attribute="name", + ), + "release_date": DatePicker, + "primary_language": AwesompleteInputWidget( + attrs=autocomplete_placeholder, + data_list=",".join(PROGRAMMING_LANGUAGES), + ), + "dependencies": forms.Textarea(attrs={"rows": 2}), + } + + def clean_packages_ids(self): + packages_ids = self.cleaned_data.get("packages_ids") + if packages_ids: + packages_ids = packages_ids.split(",") + for package_id in packages_ids: + try: + int(package_id) + except ValueError: + raise forms.ValidationError(f"Wrong value type for {package_id}") + return packages_ids + + def save(self, *args, **kwargs): + instance = super().save(*args, **kwargs) + + packages_ids = self.cleaned_data.get("packages_ids") + if packages_ids: + packages = Package.objects.scope(self.user.dataspace).filter(id__in=packages_ids) + for package in packages: + ComponentAssignedPackage.objects.create( + component=instance, + package=package, + dataspace=instance.dataspace, + ) + + # Update the completion_level once everything including m2m were saved + instance.update_completion_level() + + return instance + + @property + def helper(self): + helper = super().helper + + helper.layout = Layout( + Fieldset( + None, + Group("name", "version", "owner"), + HTML("
"), + "license_expression", + Group("copyright", "holder"), + "notice_text", + Group("notice_filename", "notice_url"), + Group("is_license_notice", "is_copyright_notice", "is_notice_in_codebase"), + HTML("
"), + Group("description", "keywords"), + Group("primary_language", "cpe"), + Group("dependencies", "release_date"), + HTML("
"), + Group("homepage_url", "code_view_url"), + Group("bug_tracking_url", "vcs_url"), + HTML("
"), + Group("usage_policy", "configuration_status"), + HTML("
"), + "packages_ids", + Submit("submit", self.submit_label, css_class="btn-success"), + self.save_as_new_submit, + ), + ) + + return helper + + +BaseComponentAjaxForm = modelform_factory( + Component, + form=ComponentForm, + fields=[ + "name", + "version", + "owner", + "license_expression", + "homepage_url", + "description", + ], +) + + +class ComponentAjaxForm(BaseComponentAjaxForm): + @property + def helper(self): + helper = super().helper + helper.form_tag = False + + helper.layout = Layout( + Fieldset( + None, + Group("name", "version", "owner"), + "license_expression", + Group("description", "homepage_url"), + ), + ) + + return helper + + +class PackageFieldsValidationMixin: + """Enforce the Filename or Package URL requirement.""" + + def clean(self): + cleaned_data = super().clean() + + purl_values = [cleaned_data.get(field_name) for field_name in PACKAGE_URL_FIELDS] + + if any(purl_values): + try: + packageurl.PackageURL(*purl_values) + except ValueError as e: + raise forms.ValidationError(e) + elif not cleaned_data.get("filename"): + raise forms.ValidationError("A Filename or a Package URL (type + name) is required.") + + return cleaned_data + + +class PackageForm( + LicenseExpressionFormMixin, + PackageFieldsValidationMixin, + SetKeywordsChoicesFormMixin, + DataspacedModelForm, +): + save_as = True + color_initial = True + + keywords = JSONListChoiceField( + required=False, + widget=AwesompleteInputWidget(attrs=autocomplete_placeholder), + ) + + collect_data = forms.BooleanField( + required=False, + initial=True, + label=( + "Automatically collect the SHA1, MD5, and Size using the " + "Download URL and apply them to the package definition." + ), + ) + + class Meta: + model = Package + fields = [ + "filename", + "download_url", + "md5", + "sha1", + "sha256", + "sha512", + "size", + "release_date", + "primary_language", + "cpe", + "description", + "keywords", + "notes", + "usage_policy", + "license_expression", + "copyright", + "holder", + "author", + "homepage_url", + "vcs_url", + "code_view_url", + "bug_tracking_url", + "repository_homepage_url", + "repository_download_url", + "api_data_url", + "notice_text", + "dependencies", + "type", + "namespace", + "name", + "version", + "qualifiers", + "subpath", + "collect_data", + ] + widgets = { + "description": forms.Textarea(attrs={"rows": 2}), + "notes": forms.Textarea(attrs={"rows": 2}), + "copyright": forms.Textarea(attrs={"rows": 2}), + "notice_text": forms.Textarea(attrs={"rows": 2}), + "holder": forms.Textarea(attrs={"rows": 1}), + "author": forms.Textarea(attrs={"rows": 1}), + "dependencies": forms.Textarea(attrs={"rows": 2}), + "release_date": DatePicker, + "primary_language": AwesompleteInputWidget( + attrs=autocomplete_placeholder, + data_list=",".join(PROGRAMMING_LANGUAGES), + ), + } + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + scancodeio = ScanCodeIO(self.user) + self.submit_scan_enabled = all( + [ + self.is_addition, + scancodeio.is_configured(), + self.dataspace.enable_package_scanning, + ] + ) + + @property + def helper(self): + helper = super().helper + + scancode_notice = None + if self.submit_scan_enabled: + scancode_notice = HTML( + '
' + "Package scanning is enabled in your Dataspace, " + "DejaCode will also submit the package to ScanCode.io and the " + 'results will be returned to the "Scan" detail tab of the package ' + "when that scan is complete." + "
" + ) + + package_url = HTML( + '
' + ' Package URL:' + "
" + ) + + helper.layout = Layout( + Fieldset( + None, + Group("filename", "download_url"), + HTML("
"), + package_url, + Group("type", "namespace", "name"), + Group("version", "qualifiers", "subpath"), + HTML("
"), + "license_expression", + Group("copyright", "notice_text"), + Group("holder", "author"), + HTML("
"), + Group("description", "keywords"), + Group("primary_language", "cpe"), + Group("size", "release_date"), + Group("dependencies", "notes"), + HTML("
"), + Group("homepage_url", "code_view_url"), + Group("bug_tracking_url", "vcs_url"), + HTML("
"), + Group("md5", "sha1"), + Group("sha256", "sha512"), + HTML("
"), + Group("usage_policy", None), + HTML("
"), + Field("collect_data", wrapper_class="text-muted"), + scancode_notice, + Submit("submit", self.submit_label, css_class="btn-success"), + self.save_as_new_submit, + ), + ) + + return helper + + def save(self, *args, **kwargs): + """ + Add data collection and ScanCode.io scanning if enabled and available. + Set `data_collected` and `scan_submitted` in the `cleaned_data` to be + used for crafting the proper message in `get_success_message`. + """ + instance = super().save(*args, **kwargs) + + download_url = self.cleaned_data.get("download_url") + collect_data = self.cleaned_data.get("collect_data") + + if collect_data and download_url: + transaction.on_commit(lambda: tasks.package_collect_data.delay(instance.id)) + self.cleaned_data["data_collected"] = True + + if self.submit_scan_enabled and download_url: + tasks.scancodeio_submit_scan.delay( + uris=download_url, + user_uuid=self.user.uuid, + dataspace_uuid=self.user.dataspace.uuid, + ) + self.cleaned_data["scan_submitted"] = True + + return instance + + +class BaseScanToPackageForm(LicenseExpressionFormMixin, DataspacedModelForm): + @property + def helper(self): + helper = FormHelper() + helper.form_method = "post" + helper.form_id = f"{self.prefix}-form" + helper.form_tag = False + return helper + + +class ScanToPackageForm(BaseScanToPackageForm): + prefix = "scan-to-package" + + package_url = forms.CharField( + label="Package URL", + required=False, + ) + + class Meta: + model = Package + fields = [ + "package_url", + "license_expression", + "copyright", + "primary_language", + "description", + "homepage_url", + "release_date", + "notice_text", + "dependencies", + ] + widgets = { + "copyright": forms.Textarea(attrs={"rows": 2}), + "description": forms.Textarea(attrs={"rows": 2}), + "notice_text": forms.Textarea(attrs={"rows": 2}), + "dependencies": forms.Textarea(attrs={"rows": 5}), + } + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + # Store the original value from the instance for usage in changed_data() + self.original_package_url = self.instance.package_url + + # Do not include the package_url field if a value is already set on the instance + if self.instance.package_url: + del self.fields["package_url"] + + if not kwargs.get("data"): + self.fields = self.fields_with_initial_value() + + def fields_with_initial_value(self): + kept_fields = {} + + for field_name, field in self.fields.items(): + if not self.initial.get(field_name): + continue + + instance_value = getattr(self.instance, field_name, None) + help_text = "No current value" + if instance_value: + help_text = f"Current value: {instance_value}" + field.help_text = help_text + + kept_fields[field_name] = field + + return kept_fields + + def clean_package_url(self): + package_url = self.cleaned_data.get("package_url") + if package_url: + self.instance.set_package_url(package_url) + return package_url + + @cached_property + def changed_data(self): + """ + Workaround to make sure the `package_url` was really changed + since it's not a bound field on the ModelForm. + """ + changed_data = super().changed_data + + if "package_url" in changed_data: + if self.instance.package_url == self.original_package_url: + changed_data.remove("package_url") + + return changed_data + + +class ScanSummaryToPackageForm(BaseScanToPackageForm): + prefix = "scan-summary-to-package" + + class Meta: + model = Package + fields = [ + "license_expression", + "primary_language", + "holder", + ] + widgets = { + "holder": forms.Textarea(attrs={"rows": 2}), + } + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + if not kwargs.get("data"): + self.set_help_text_with_initial_value() + + def set_help_text_with_initial_value(self): + for field_name, field in self.fields.items(): + instance_value = getattr(self.instance, field_name, None) + help_text = "No current value" + if instance_value: + help_text = f"Current value: {instance_value}" + field.help_text = help_text + + +class BaseAddToProductForm( + LicenseExpressionFormMixin, DefaultOnAdditionLabelMixin, DataspacedModelForm +): + relation_fk_field = None + default_on_addition_fields = ["review_status", "purpose"] + + class Meta: + fields = [ + "product", + "license_expression", + "review_status", + "purpose", + "notes", + "is_deployed", + "is_modified", + "extra_attribution_text", + "feature", + "issue_ref", + ] + widgets = { + "package": forms.widgets.HiddenInput, + "component": forms.widgets.HiddenInput, + "notes": forms.Textarea(attrs={"rows": 2}), + "extra_attribution_text": forms.Textarea(attrs={"rows": 2}), + "copyright": forms.Textarea(attrs={"rows": 2}), + "reference_notes": forms.Textarea(attrs={"rows": 2}), + } + + def __init__(self, user, *args, **kwargs): + relation_instance = kwargs.pop(self.relation_fk_field, None) + + super().__init__(user, *args, **kwargs) + + product_field = self.fields["product"] + perms = ["view_product", "change_product"] + product_field.queryset = Product.objects.get_queryset(user, perms=perms) + + if relation_instance: + help_text = f'"{relation_instance}" will be assigned to the selected product.' + product_field.help_text = help_text + + @property + def helper(self): + helper = FormHelper() + helper.form_method = "post" + helper.form_id = "add-to-product-form" + helper.form_tag = False + helper.modal_title = "Add to Product" + helper.modal_id = "add-to-product-modal" + return helper + + def save(self, *args, **kwargs): + """ + Log a change in the Product History for Product relationship addition + and edition. + """ + instance = super().save(*args, **kwargs) + product = instance.product + + if getattr(instance, "is_custom_component", False): + relation_model_name = "custom component" + else: + relation_model_name = self.relation_fk_field + + if self.is_addition: + message = f'Added {relation_model_name} "{self.instance}"' + else: + fields = ", ".join(self.changed_data) + message = f'Changed {relation_model_name} "{self.instance}" {fields}' + + History.log_change(self.user, product, message) + product.last_modified_by = self.user + product.save() + + return instance + + +class PackageAddToProductForm(BaseAddToProductForm): + relation_fk_field = "package" + + class Meta(BaseAddToProductForm.Meta): + model = ProductPackage + fields = ["package"] + BaseAddToProductForm.Meta.fields + + +class ComponentAddToProductForm(BaseAddToProductForm): + relation_fk_field = "component" + + class Meta(BaseAddToProductForm.Meta): + model = ProductComponent + fields = ["component"] + BaseAddToProductForm.Meta.fields + + +class AddToProductAdminForm(forms.Form): + use_required_attribute = False + product = forms.ModelChoiceField( + required=True, + queryset=Product.objects.none(), + ) + ids = forms.CharField(widget=forms.widgets.HiddenInput) + + def __init__(self, request, model, relation_model, *args, **kwargs): + super().__init__(*args, **kwargs) + self.request = request + self.model = model + self.relation_model = relation_model + self.dataspace = request.user.dataspace + self.fields["product"].queryset = Product.objects.get_queryset( + request.user, perms=["view_product", "change_product"] + ) + + def get_selected_objects(self): + ids = self.initial.get("ids") or self.cleaned_data["ids"] + return self.model.objects.scope(self.dataspace).filter(pk__in=ids.split(",")) + + def save(self): + product = self.cleaned_data["product"] + return product.assign_objects( + related_objects=self.get_selected_objects(), + user=self.request.user, + ) + + +class AddToProductMultipleForm(AddToProductAdminForm): + use_required_attribute = True + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + verbose_name_plural = self.model._meta.verbose_name_plural + self.fields["product"].help_text = ( + f"The {verbose_name_plural} that you selected will be assigned " + f"to the product that you select here.
" + f"You will then be presented with the updated product and will have " + f"the option to edit those assignments to provide more details about " + f"how those {verbose_name_plural} are actually used in the product." + ) + + @property + def helper(self): + helper = FormHelper() + helper.form_method = "post" + helper.form_id = "add-to-product-form" + helper.form_tag = False + opts = self.model._meta + viewname = f"{opts.app_label}:{opts.model_name}_list" + helper.form_action = reverse(viewname) + helper.modal_title = "Add to Product" + helper.modal_id = "add-to-product-modal" + return helper + + +class AddToComponentFormMixin(forms.Form): + object_id = forms.CharField(widget=forms.HiddenInput, required=False) + component = forms.CharField( + required=True, + widget=AutocompleteInput( + attrs={ + "style": "width: 400px !important;", + "data-api_url": reverse_lazy("api_v2:component-list"), + }, + ), + ) + + def new_component_from_package_link(self): + if self.user.has_perm("component_catalog.add_component"): + component_add_url = reverse("component_catalog:component_add") + + href = "#" + package = (self.initial or {}).get("package") + if package: + href = f"{component_add_url}?package_ids={package.id}" + + return HTML( + f'" + f"
" + ) + + def clean_component(self): + object_id = self.cleaned_data.get("object_id") + try: + component = Component.objects.scope(self.dataspace).get(uuid=object_id) + except Component.DoesNotExist: + raise forms.ValidationError("Invalid Component.") + return component + + @property + def helper(self): + helper = FormHelper() + helper.form_method = "post" + helper.form_id = "add-to-component-form" + helper.form_tag = False + helper.modal_title = "Add to Component" + helper.modal_id = "add-to-component-modal" + helper.layout = Layout( + Fieldset( + None, + self.new_component_from_package_link(), + "object_id", + "component", + ), + ) + return helper + + +class AddToComponentForm(AddToComponentFormMixin, DataspacedModelForm): + class Meta: + model = ComponentAssignedPackage + fields = [ + "object_id", + "component", + "package", + ] + widgets = { + "package": forms.widgets.HiddenInput, + } + + @property + def helper(self): + helper = super().helper + helper.layout = Layout( + helper.layout, + "package", + ) + return helper + + +class AddMultipleToComponentForm(AddToComponentFormMixin): + use_required_attribute = True + ids = forms.CharField(widget=forms.widgets.HiddenInput) + + def __init__(self, request, *args, **kwargs): + super().__init__(*args, **kwargs) + self.request = request + self.user = request.user + self.dataspace = request.user.dataspace + self.model = Package + + def get_selected_objects(self): + if "ids" in self.initial: + ids = self.initial["ids"].split(",") + else: + ids = self.cleaned_data["ids"].split(",") + return self.model.objects.scope(self.dataspace).filter(pk__in=ids) + + def save(self): + component = self.cleaned_data["component"] + user = self.request.user + created_count = 0 + unchanged_count = 0 + + selected_objects = self.get_selected_objects() + for obj in selected_objects: + filters = { + "component": component, + "package": obj, + "dataspace": obj.dataspace, + } + relation_obj, created = ComponentAssignedPackage.objects.get_or_create(**filters) + if created: + History.log_addition(user, relation_obj) + History.log_change(user, component, f'Added package "{obj}"') + created_count += 1 + else: + unchanged_count += 1 + + if created_count: + component.last_modified_by = user + component.save() + + return created_count, unchanged_count + + @property + def helper(self): + helper = super().helper + opts = self.model._meta + viewname = f"{opts.app_label}:{opts.model_name}_list" + helper.form_action = reverse(viewname) + helper.layout = Layout( + helper.layout, + "ids", + ) + return helper + + +class UsagePolicyAdminFormMixin: + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + usage_policy_field = self.fields.get("usage_policy") + if usage_policy_field: + rel = self._meta.model._meta.get_field("usage_policy").remote_field + base_widget = usage_policy_field.widget.widget + usage_policy_field.widget = UsagePolicyWidgetWrapper( + base_widget, rel, self.admin_site, object_instance=self.instance + ) + + def clean(self): + """ + Raise a warning when a license_expression is changed and the usage_policy from the + primary license differs from the package usage_policy. + """ + cleaned_data = super().clean() + + license_expression = self.cleaned_data.get("license_expression") + conditions = [ + self.instance, + self.instance.usage_policy, + "license_expression" in self.changed_data, + license_expression, + ] + + if all(conditions): + fake_instance = self._meta.model( + license_expression=license_expression, + dataspace=self.instance.dataspace, + ) + policy_from_license = fake_instance.get_policy_from_primary_license() + if policy_from_license and policy_from_license != self.instance.usage_policy: + msg = ( + f"The changed license assignment does not match the currently assigned " + f'usage policy: "{self.instance.usage_policy}" != "{policy_from_license}" ' + f"from {fake_instance.primary_license}" + ) + messages.warning(self.request, msg) + + return cleaned_data + + +class AcceptableLinkagesFormMixin(forms.Form): + acceptable_linkages = forms.MultipleChoiceField( + widget=forms.CheckboxSelectMultiple, + required=False, + ) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + dataspace = getattr(self, "dataspace", None) or getattr( # MassUpdate + self.instance, "dataspace", None + ) # AdminForm + + if dataspace: + qs = AcceptableLinkage.objects.scope(dataspace) + choices = [(choice.label, choice.label) for choice in qs] + acceptable_linkages_field = self.fields["acceptable_linkages"] + acceptable_linkages_field.choices = choices + help_text = Component._meta.get_field("acceptable_linkages").help_text + acceptable_linkages_field.help_text = help_text + + +class ComponentAdminForm( + LicenseExpressionFormMixin, + UsagePolicyAdminFormMixin, + AcceptableLinkagesFormMixin, + SetKeywordsChoicesFormMixin, + DataspacedAdminForm, +): + keywords = JSONListChoiceField( + required=False, + widget=AdminAwesompleteInputWidget(attrs=autocomplete_placeholder), + ) + + def __init__(self, *args, **kwargs): + # Required for SetKeywordsChoicesFormMixin + self.dataspace = self.request.user.dataspace + super().__init__(*args, **kwargs) + + +class SubcomponentLicenseExpressionFormMixin(LicenseExpressionFormMixin): + relation_fk_field = "child" + + +class SubcomponentAdminForm(SubcomponentLicenseExpressionFormMixin, DataspacedAdminForm): + def clean_reference_notes(self): + """Propagate the reference_notes from the child Component if available and not provided.""" + reference_notes = self.cleaned_data.get("reference_notes") + child = self.cleaned_data.get("child") + + if not reference_notes and child and child.reference_notes: + return child.reference_notes + + return reference_notes + + +class PackageAdminForm( + LicenseExpressionFormMixin, + UsagePolicyAdminFormMixin, + PackageFieldsValidationMixin, + SetKeywordsChoicesFormMixin, + DataspacedAdminForm, +): + keywords = JSONListChoiceField( + required=False, + widget=AdminAwesompleteInputWidget(attrs=autocomplete_placeholder), + ) + + class Meta: + widgets = { + "download_url": AdminURLFieldWidget, + } + + def _get_purldb_uuid(self): + """Return the `purldb_uuid` value if available in the `request`.""" + request = getattr(self, "request", None) + if request: + return request.GET.get("purldb_uuid", None) + + def _set_purldb_uuid_on_instance(self): + """Force the `purldb_uuid`, if available, on the `self.instance`""" + purldb_uuid = self._get_purldb_uuid() + if purldb_uuid: + self.instance.uuid = purldb_uuid + + def __init__(self, *args, **kwargs): + # Required for SetKeywordsChoicesFormMixin + self.dataspace = self.request.user.dataspace + + super().__init__(*args, **kwargs) + # Override the readonly value for the UUID. + # This is only for the visual value in the form and has no impact on the saved value. + # A new value is always re-generated at save time so we have to + # override it in `self.save()` as well. + self._set_purldb_uuid_on_instance() + + def save(self, commit=True): + # Replaces the auto-generated UUID but the purldb_uuid if available. + self._set_purldb_uuid_on_instance() + return super().save(commit) + + +class ComponentMassUpdateForm( + LicenseExpressionFormMixin, + AcceptableLinkagesFormMixin, + SetKeywordsChoicesFormMixin, + DejacodeMassUpdateForm, +): + raw_id_fields = ["owner"] + keywords = JSONListChoiceField( + required=False, + widget=AwesompleteInputWidget(attrs=autocomplete_placeholder), + ) + + class Meta: + fields = [ + "version", + "owner", + "copyright", + "holder", + "license_expression", + "reference_notes", + "description", + "homepage_url", + "vcs_url", + "code_view_url", + "bug_tracking_url", + "primary_language", + "project", + "codescan_identifier", + "type", + "notice_text", + "is_license_notice", + "is_copyright_notice", + "is_notice_in_codebase", + "notice_filename", + "notice_url", + "website_terms_of_use", + "dependencies", + "configuration_status", + "is_active", + "usage_policy", + "curation_level", + "guidance", + "admin_notes", + "keywords", + "approval_reference", + "ip_sensitivity_approved", + "affiliate_obligations", + "affiliate_obligation_triggers", + "concluded_license", + "legal_comments", + "sublicense_allowed", + "express_patent_grant", + "covenant_not_to_assert", + "indemnification", + "legal_reviewed", + "distribution_formats_allowed", + "acceptable_linkages", + "export_restrictions", + "approved_download_location", + "approved_community_interaction", + ] + + +class SubcomponentMassUpdateForm(DejacodeMassUpdateForm): + class Meta: + fields = [ + "reference_notes", + "usage_policy", + "purpose", + "notes", + "is_deployed", + "is_modified", + "extra_attribution_text", + "package_paths", + ] + + +class PackageMassUpdateForm( + LicenseExpressionFormMixin, SetKeywordsChoicesFormMixin, DejacodeMassUpdateForm +): + keywords = JSONListChoiceField( + required=False, + widget=AwesompleteInputWidget(attrs=autocomplete_placeholder), + ) + + class Meta: + fields = [ + "release_date", + "primary_language", + "description", + "project", + "notes", + "dependencies", + "copyright", + "holder", + "author", + "license_expression", + "reference_notes", + "usage_policy", + "homepage_url", + "notice_text", + "type", + "namespace", + "name", + "version", + "qualifiers", + "subpath", + ] + + +class SetPolicyForm(forms.Form): + def __init__(self, request, model_class, policy_attr, *args, **kwargs): + super().__init__(*args, **kwargs) + self.request = request + self.dataspace = request.user.dataspace + self.model_class = model_class + self.policy_attr = policy_attr + + def save(self): + checked_ids = [ + key.replace("checked_id_", "") + for key, value in self.data.items() + if key.startswith("checked_id_") and value == "on" + ] + + qs = self.get_objects().filter(pk__in=checked_ids) + for obj in qs: + obj.usage_policy = getattr(obj, self.policy_attr) + obj.save() + + return len(qs) + + def get_objects(self): + ids = self.initial["ids"].split(",") + return ( + self.model_class.objects.scope(self.dataspace) + .filter(pk__in=ids) + .select_related("usage_policy") + ) diff --git a/component_catalog/fuzzy.py b/component_catalog/fuzzy.py new file mode 100644 index 00000000..33e07ac5 --- /dev/null +++ b/component_catalog/fuzzy.py @@ -0,0 +1,72 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# DejaCode is a trademark of nexB Inc. +# SPDX-License-Identifier: AGPL-3.0-only +# See https://github.com/nexB/dejacode for support or download. +# See https://aboutcode.org for more information about AboutCode FOSS projects. +# + +import operator +import re +from difflib import SequenceMatcher +from functools import reduce + +from django.db import models + +import django_filters + + +class FuzzyPackageNameSearch(django_filters.CharFilter): + threshold = 0.4 + help_text = "Fuzzy matching on the name." + + def __init__(self, *args, **kwargs): + kwargs.setdefault("help_text", self.help_text) + super().__init__(*args, **kwargs) + + def filter(self, qs, value): + if not value: + return qs + + search_values = self.create_search_names(value) + or_queries = [models.Q(filename__icontains=value) for value in search_values] + if or_queries: + potential_packages = qs.filter(reduce(operator.or_, or_queries)) + pks = list(self.fuzzy_match_packages(value, potential_packages, self.threshold)) + # Duplicated query since the output need to be a QuerySet instance while + # fuzzy_match_packages() function return an iterable. + qs = qs.filter(pk__in=pks) + + return qs + + @staticmethod + def create_search_names(filename): + version_extension_pattern = r"[-_](\d.+)\.\w+|\.\w+" + split_filename = re.split(version_extension_pattern, filename) + basename = split_filename[0] + # We are assuming that the regex pattern did its job and the second + # element in the list is either a version number or None. None is a + # possible value because when there is no version number in the package + # name, the regex splits on the file extension but does not capture it, + # so the second element in the list is None. + + if len(split_filename) > 2 and split_filename[1]: + version = split_filename[1] + basename_version = basename + " " + version + return filename, basename_version, basename + else: + return filename, basename + + @staticmethod + def fuzzy_match_packages(filename, potential_packages, threshold): + """ + For every Package that may be a potential match, the Package filename is + compared to the initial package filename that the user queried DejaCode with. + + If the ratio of similarity of a Package's filename when compared to the initial + package name is above the user provided threshold, that Package is yielded. + """ + for package in potential_packages: + ratio = SequenceMatcher(None, filename, package.filename).ratio() + if ratio > threshold: + yield package.pk diff --git a/component_catalog/importers.py b/component_catalog/importers.py new file mode 100644 index 00000000..70153e72 --- /dev/null +++ b/component_catalog/importers.py @@ -0,0 +1,467 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# DejaCode is a trademark of nexB Inc. +# SPDX-License-Identifier: AGPL-3.0-only +# See https://github.com/nexB/dejacode for support or download. +# See https://aboutcode.org for more information about AboutCode FOSS projects. +# +import json +import os +from urllib.parse import urlparse + +from django import forms +from django.core.exceptions import FieldDoesNotExist +from django.core.exceptions import ValidationError +from django.core.validators import EMPTY_VALUES +from django.db import models +from django.utils.dateparse import parse_date + +import saneyaml + +from component_catalog.forms import AcceptableLinkagesFormMixin +from component_catalog.forms import SetKeywordsChoicesFormMixin +from component_catalog.forms import SubcomponentLicenseExpressionFormMixin +from component_catalog.license_expression_dje import LicenseExpressionFormMixin +from component_catalog.models import Component +from component_catalog.models import ComponentAssignedPackage +from component_catalog.models import ComponentStatus +from component_catalog.models import ComponentType +from component_catalog.models import Package +from component_catalog.models import Subcomponent +from component_catalog.programming_languages import PROGRAMMING_LANGUAGES +from dje.forms import JSONListChoiceField +from dje.importers import BaseImporter +from dje.importers import BaseImportModelForm +from dje.importers import ComponentRelatedFieldImportMixin +from dje.importers import ModelChoiceFieldForImport +from dje.utils import get_help_text +from organization.models import Owner +from policy.models import UsagePolicy +from product_portfolio.models import ProductComponent +from product_portfolio.models import ProductPackage + + +class OwnerChoiceField(ModelChoiceFieldForImport): + def get_suggestions(self, value, limit=5): + """ + Return a QuerySet of similar match, using the name__icontains filter, + for a given value. + """ + if self.queryset: + dataspace = self.queryset.first().dataspace + else: + return + + owners = Owner.objects.scope(dataspace).filter( + models.Q(name__icontains=value) | models.Q(alias__icontains=value) + ) + + return owners[:limit] + + def get_suggestion_message(self, value): + """Return a message including the suggestions if any.""" + suggestions = self.get_suggestions(value) + if not suggestions: + return "No suggestion." + + names = [owner.name for owner in suggestions] + return "Suggestion(s): {}.".format(", ".join(names)) + + def to_python(self, value): + """ + Wrap the original code to catch the ValidationError and add the + suggestion in the error message. + """ + try: + value = super().to_python(value) + except ValidationError: + msg = "{} {}".format( + self.error_messages["invalid_choice"], self.get_suggestion_message(value) + ) + raise ValidationError(msg) + return value + + +class CleanPrimaryLanguageFormMixin: + def clean_primary_language(self): + primary_language = self.cleaned_data["primary_language"] + if primary_language and primary_language not in PROGRAMMING_LANGUAGES: + language_mapping = {language.lower(): language for language in PROGRAMMING_LANGUAGES} + proper_case = language_mapping.get(primary_language.lower(), None) + if proper_case: + msg = f'Language will be imported with proper case: "{proper_case}"' + self.add_warning("primary_language", msg) + return proper_case + + msg = f'"{primary_language}" is not in standard languages list.' + suggestions = [ + language + for language in PROGRAMMING_LANGUAGES + if language.lower().startswith(str(primary_language[0]).lower()) + ] + if suggestions: + msg += "\nSuggestion(s): {}.".format(", ".join(suggestions)) + self.add_warning("primary_language", msg) + + return primary_language + + +class ImportMultipleChoiceField(forms.fields.MultipleChoiceField): + def to_python(self, value): + if not value: + return [] + elif isinstance(value, str): + return [label.strip() for label in value.split(",")] + return [str(val) for val in value] + + +class ComponentImportForm( + LicenseExpressionFormMixin, + CleanPrimaryLanguageFormMixin, + AcceptableLinkagesFormMixin, + SetKeywordsChoicesFormMixin, + BaseImportModelForm, +): + """ + For now, each FK field of the Model needs to be redefined on this Form as a + ModelChoiceFieldForImport field. + """ + + owner = OwnerChoiceField( + queryset=Owner.objects.none(), + required=False, + # Using a TextInput as widget so the value is in the input + widget=forms.TextInput, + help_text=get_help_text(Component, "owner"), + error_messages={"invalid_choice": "This Owner does not exists."}, + ) + + type = ModelChoiceFieldForImport( + queryset=ComponentType.objects.none(), + required=False, + help_text=get_help_text(Component, "type"), + identifier_field="label", + ) + + configuration_status = ModelChoiceFieldForImport( + queryset=ComponentStatus.objects.none(), + required=False, + help_text=get_help_text(Component, "configuration_status"), + identifier_field="label", + ) + + usage_policy = ModelChoiceFieldForImport( + queryset=UsagePolicy.objects.none(), + required=False, + help_text=get_help_text(Component, "usage_policy"), + identifier_field="label", + ) + + keywords = JSONListChoiceField( + required=False, + ) + + acceptable_linkages = ImportMultipleChoiceField( + required=False, + ) + + class Meta: + model = Component + exclude = ( + "licenses", + "children", + "packages", + "completion_level", + # JSONField not supported + "dependencies", + ) + + def pre_process_form(self, data, **kwargs): + instance = kwargs.pop("instance", None) + self.prefix = kwargs.get("prefix") + version = self.normalize_version(data.get(self.add_prefix("version"), "").strip()) + + if not instance: # No instance given, let's match our component + try: + instance = Component.objects.get( + name=data.get(self.add_prefix("name")).strip(), + version=version, + dataspace=self.dataspace, + ) + except Component.DoesNotExist: + pass + + return data, instance + + def normalize_version(self, version): + if version.startswith("v"): + version = version[1:].strip() + return version + + def clean_version(self): + original_version = self.cleaned_data["version"] + normalized_version = self.normalize_version(original_version) + if original_version != normalized_version: + self.add_warning("version", f"Version will been cleaned to {normalized_version}") + return normalized_version + + def save(self, commit=True): + """Update the completion_level once everything including m2m were saved.""" + instance = super().save(commit) + instance.update_completion_level() + return instance + + +class ComponentImporter(BaseImporter): + model_form = ComponentImportForm + add_to_product_perm = "product_portfolio.add_productcomponent" + relation_model = ProductComponent + + +class PackageImportForm( + ComponentRelatedFieldImportMixin, + LicenseExpressionFormMixin, + CleanPrimaryLanguageFormMixin, + SetKeywordsChoicesFormMixin, + BaseImportModelForm, +): + component = forms.CharField( + required=False, # We are allowing import without a Component attached + help_text='":" of the Component to be associated with this Package.', + ) + + usage_policy = ModelChoiceFieldForImport( + queryset=UsagePolicy.objects.none(), + required=False, + help_text=get_help_text(Package, "usage_policy"), + identifier_field="label", + ) + + keywords = JSONListChoiceField( + required=False, + ) + + class Meta: + model = Package + exclude = [ + "licenses", + # JSONField not supported + "dependencies", + "file_references", + "parties", + ] + + def __init__(self, *args, **kwargs): + self.is_from_scancode = kwargs.pop("is_from_scancode", False) + super().__init__(*args, **kwargs) + + def clean_component(self): + return self._clean_name_version_related_field("component", Component) + + def errors_to_warnings(self): + """ + Convert the errors (blocking) list into warnings (not blocking). + This is useful when the input is not meant to be edited by the user. + Note that if the cleaning process raised an error for a given field, + the value will not be included in the `cleaned_data`, thus not imported. + """ + remaining_errors = {} + for field_name, value in self._errors.items(): + if field_name == "filename": + remaining_errors[field_name] = value + else: + self.add_warning(field_name, "Value is not valid, it will not be imported.") + self._errors = remaining_errors + + def clean(self): + cleaned_data = super().clean() + if self.is_from_scancode: + self.errors_to_warnings() + return cleaned_data + + def save(self, commit=True): + """Create the ComponentAssignedPackage after the Package instance is saved.""" + package = super().save(commit) + component = self.cleaned_data.get("component") + + if component: + ComponentAssignedPackage.objects.create( + package=package, component=component, dataspace=package.dataspace + ) + # Update the completion_level once the ComponentAssignedPackage was created + component.update_completion_level() + + return package + + +class PackageImporter(BaseImporter): + model_form = PackageImportForm + add_to_product_perm = "product_portfolio.add_productpackage" + relation_model = ProductPackage + + def prepare_data_json(self, data): + """ + Look for summary data (`--summary` ScanCode option) first as a + shortcut to get all the detected packages. + Iterate the whole scan results when the summary is not available. + """ + if "summary" in data: + packages = data.get("summary", {}).get("packages", []) + else: + packages = [ + package for file in data.get("files", []) for package in file.get("packages", []) + ] + + if not packages: + self.fatal_errors.append("No package data to import in input file.") + return + + self.is_from_scancode = True + packages = [self.prepare_package(package) for package in packages] + input_as_list_of_dict = packages + + # Using a dict comprehension to keep the original key order and + # ensure we have all possibile headers. + header_row = {key: None for package in packages for key in package.keys()} + header_row = list(header_row.keys()) + + self.build_headers(header_row) + + return input_as_list_of_dict + + @staticmethod + def prepare_package(package, path=None): + """ + Prepare the package data during the ScanCode data import. + WARNING: Do not return None, always return a package data. + """ + # Warning: Files may have more than 1 entry in the future + if not path: + files = package.get("files") + if files: + path = files[0].get("path") + + map_if_none = { + "homepage_url": "repository_homepage_url", + "download_url": "repository_download_url", + } + for field, mapped_field in map_if_none.items(): + if not package.get(field): + package[field] = package.get(mapped_field, "") + + filename = None + download_url = package.get("download_url") + if download_url: + filename = os.path.basename(urlparse(download_url).path) + + if not filename and path: + filename = path.strip("/").split("/")[-1] + + package["filename"] = filename + + field_to_notes = [ + "bug_tracking_url", + "code_view_url", + "vcs_url", + "parties", + ] + notes = {} + for field in field_to_notes: + value = package.pop(field, None) + if value: + notes[field] = value + if notes: + package["notes"] = saneyaml.dump(notes) + + prepared_data = {} + for field, value in package.items(): + if value in EMPTY_VALUES: + continue + + try: + model_field = Package._meta.get_field(field) + except FieldDoesNotExist: + continue + + if isinstance(model_field, models.DateField): + value = parse_date(value) + elif field == "dependencies": + value = json.dumps(value, indent=2) + elif field == "keywords": + pass + else: + value = str(value) + + prepared_data[field] = value + + return prepared_data + + def get_form_kwargs(self): + kwargs = super().get_form_kwargs() + kwargs["is_from_scancode"] = getattr(self, "is_from_scancode", False) + return kwargs + + +class SubcomponentImportForm( + ComponentRelatedFieldImportMixin, + SubcomponentLicenseExpressionFormMixin, + BaseImportModelForm, +): + parent = forms.CharField(help_text='":" of the parent Component.') + + child = forms.CharField(help_text='":" of the child Component.') + + usage_policy = ModelChoiceFieldForImport( + queryset=UsagePolicy.objects.none(), + required=False, + help_text=get_help_text(Subcomponent, "usage_policy"), + identifier_field="label", + ) + + class Meta: + model = Subcomponent + exclude = [ + "licenses", + ] + + def pre_process_form(self, data, **kwargs): + instance = kwargs.pop("instance", None) + self.prefix = kwargs.get("prefix") + parent = data.get(self.add_prefix("parent")) + child = data.get(self.add_prefix("child")) + + try: + parent_name, parent_version = self.get_name_version(parent) + child_name, child_version = self.get_name_version(child) + parent = Component.objects.scope(self.dataspace).get( + name=parent_name, version=parent_version + ) + child = Component.objects.scope(self.dataspace).get( + name=child_name, version=child_version + ) + except (models.ObjectDoesNotExist, forms.ValidationError): + parent = None + child = None + + # No instance given, let's find a possible matching Subcomponent + if not instance and (parent and child): + try: + instance = Subcomponent.objects.get( + parent=parent, + child=child, + dataspace=self.dataspace, + ) + except Subcomponent.DoesNotExist: + pass + + return data, instance + + def clean_parent(self): + return self._clean_name_version_related_field("parent", Component) + + def clean_child(self): + return self._clean_name_version_related_field("child", Component) + + +class SubcomponentImporter(BaseImporter): + model_form = SubcomponentImportForm diff --git a/component_catalog/inlines.py b/component_catalog/inlines.py new file mode 100644 index 00000000..dbd5a9d6 --- /dev/null +++ b/component_catalog/inlines.py @@ -0,0 +1,86 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# DejaCode is a trademark of nexB Inc. +# SPDX-License-Identifier: AGPL-3.0-only +# See https://github.com/nexB/dejacode for support or download. +# See https://aboutcode.org for more information about AboutCode FOSS projects. +# + +from django.contrib import admin +from django.contrib.admin import StackedInline +from django.contrib.admin import TabularInline +from django.utils.translation import gettext_lazy as _ + +from component_catalog.forms import SubcomponentAdminForm +from component_catalog.models import ComponentAssignedPackage +from component_catalog.models import Subcomponent +from dje.admin import ChildRelationshipInline +from dje.admin import DataspacedFKMixin +from dje.admin import ProtectedFieldsMixin +from dje.templatetags.dje_tags import urlize_target_blank + + +class SubcomponentChildInline( + StackedInline, + ProtectedFieldsMixin, + ChildRelationshipInline, +): + model = Subcomponent + form = SubcomponentAdminForm + verbose_name = "" + verbose_name_plural = _("Child Components") + fieldsets = ( + ( + None, + { + "fields": ( + "child", + "license_expression", + "reference_notes", + "usage_policy", + "purpose", + "notes", + "is_deployed", + "is_modified", + "extra_attribution_text", + "package_paths", + ) + }, + ), + ) + raw_id_fields = ("child",) + autocomplete_lookup_fields = {"fk": ["child"]} + content_type_scope_fields = ["usage_policy"] + + +class ComponentAssignedPackageInline(DataspacedFKMixin, TabularInline): + model = ComponentAssignedPackage + extra = 0 + classes = ("grp-collapse grp-open",) + raw_id_fields = ("component",) + autocomplete_lookup_fields = {"fk": ["component"]} + verbose_name_plural = _("Associated Components") + verbose_name = _("Component") + + +class ComponentAssignedPackageInline2(ComponentAssignedPackageInline): + raw_id_fields = ("package",) + autocomplete_lookup_fields = {"fk": ["package"]} + verbose_name_plural = _("Associated Packages") + verbose_name = _("Package") + fieldsets = ( + ( + None, + { + "fields": ( + "package", + "get_download_url", + ), + }, + ), + ) + readonly_fields = ("get_download_url",) + + @admin.display(description="Download URL") + def get_download_url(self, obj): + return urlize_target_blank(obj.package.download_url) diff --git a/component_catalog/license_expression_dje.py b/component_catalog/license_expression_dje.py new file mode 100644 index 00000000..386e0e92 --- /dev/null +++ b/component_catalog/license_expression_dje.py @@ -0,0 +1,388 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# DejaCode is a trademark of nexB Inc. +# SPDX-License-Identifier: AGPL-3.0-only +# See https://github.com/nexB/dejacode for support or download. +# See https://aboutcode.org for more information about AboutCode FOSS projects. +# + +from collections import defaultdict +from itertools import chain + +from django.core.exceptions import ValidationError +from django.forms import widgets +from django.urls import reverse +from django.utils.html import format_html + +from boolean.boolean import PARSE_ERRORS +from license_expression import ExpressionError +from license_expression import LicenseSymbolLike +from license_expression import Licensing +from license_expression import ParseError + +from dje.widgets import AwesompleteInputWidgetMixin +from license_library.models import License + + +def build_licensing(licenses=None): + """ + Return a Licensing from `licenses`: either a License QuerySet or a + pre-built Licensing object (which is returned as-is). + """ + if isinstance(licenses, Licensing): + return licenses + return Licensing(licenses) + + +def parse_expression( + expression, licenses=None, validate_known=True, validate_strict=False, simple=False +): + """ + Return a parsed expression object given an expression string. + Raise Exceptions on parsing errors + + Check and parse the expression license symbols against an optional + `licenses` object that can be either a License QuerySet or a pre- + built Licensing object. + + If `validate_known` is True, raise a ValidationError if a license + symbol is unknown. Also include in exception message information + about the available licenses. + + If `validate_strict` is True, raise a ValidationError if license + symbol in a "WITH" exception expression is invalid e.g. in "a WITH + b" either: "a" is an exception or "b" is not an exception. + """ + licensing = build_licensing(licenses) + return licensing.parse( + expression, validate=validate_known, strict=validate_strict, simple=simple + ) + + +def get_license_objects(expression, licenses=None): + """ + Return a list of unique License instances from an expression string. + Raise Exceptions on parsing errors. + + Check and parse the expression license symbols against an optional + `licenses` object that can be either a License QuerySet or a pre- + built Licensing object. + + The expression is assumed to: + - be composed only from license keys (and not from license names) + - contain ONLY known license keys + + Furthermore, the validity of "WITH" expression is not checked + (e.g. `validate_strict` is not used when parsing then expression). + """ + licensing = build_licensing(licenses) + # note: we use the simple tokenizer since we support only keys here. + parsed = licensing.parse(expression, validate=False, strict=False, simple=True) + symbols = licensing.license_symbols(parsed, unique=True, decompose=True) + return [symbol.wrapped for symbol in symbols if isinstance(symbol, LicenseSymbolLike)] + + +def normalize_and_validate_expression( + expression, + licenses=None, + validate_known=True, + validate_strict=False, + include_available=False, + simple=False, +): + """ + Return a normalized and validated license expression. + Raise Django ValidationErrors exception on errors. + + If `validate_known` is True and `include_available` is True, the + exception message will contain extra information listing available + licenses when the expression uses an unknown license. + + See `parse_expression` for other arguments. + """ + include_available = validate_known and include_available + licensing = build_licensing(licenses) + + try: + parsed = parse_expression( + expression, licensing, validate_known, validate_strict, simple=simple + ) + + except ExpressionError as ee: + msg = str(ee) + if include_available: + msg += available_licenses_message(licensing) + raise ValidationError(format_html(msg), code="invalid") + + except ParseError as pe: + msg = PARSE_ERRORS[pe.error_code] + if pe.token_string: + msg += ": " + pe.token_string + if include_available: + msg += available_licenses_message(licensing) + raise ValidationError(format_html(msg), code="invalid") + + except (ValueError, TypeError) as ve: + msg = "Invalid reference licenses data.\n" + str(ve) + raise ValidationError(format_html(msg), code="invalid") + + except Exception as e: + msg = "Invalid license expression.\n" + str(e) + raise ValidationError(format_html(msg), code="invalid") + + # NOTE: we test for None because an expression cannot be resolved to + # a boolean and a plain "if parsed" would attempt to resolve the + # expression to a boolean. + if parsed is not None: + return parsed.render(template="{symbol.key}") + + +def clean_related_expression(expression, related_object): + """ + Return a normalized license expression string validated against + the list of known licenses in a related component/package. + + If the expression is empty, return the related component/package + expression. + + Raise ValidationError exceptions on validation errors. These + will contain the list of known licenses in the related component/package. + """ + if not expression: + # Using the license_expression from the related component/package as default value + return related_object.license_expression + + licenses = related_object.licenses.for_expression() + + if related_object.has_license_choices: + all_licenses = ( + License.objects.scope(related_object.dataspace) + .exclude(id__in=licenses) + .for_expression() + ) + related_object_licenses = get_license_objects( + related_object.license_choices_expression, all_licenses + ) + licenses = list(licenses) + list(related_object_licenses) + + return normalize_and_validate_expression( + expression, licenses, validate_known=True, include_available=True + ) + + +def available_licenses_message(licenses): + """ + Return an HTML formatted message string representing a list of + available licenses given a `licenses` License QuerySet or Licensing + object. + + Return an empty strings if there are no available known symbols + (such as when the license expression was not parsed against a list + of known licenses) + """ + licensing = build_licensing(licenses) + + if licensing.known_symbols: + sorted_keys = ", ".join(sorted(licensing.known_symbols.keys())) + return f"
Available licenses: {sorted_keys}" + return "" + + +class LicenseExpressionWidget( + AwesompleteInputWidgetMixin, + widgets.Textarea, +): + template_name = "django/forms/widgets/license_expression.html" + + +class LicenseExpressionFormMixin: + """ + Form mixin to validate and clean license_expression fields. + Can be mixed with ImportModelForm and DataspacedAdminForm classes. + + Support Product, Component, Package models, as well as related + type ProductComponent, Subcomponent, ProductPackage models. + + Support for multiple license_expression fields though `expression_field_names`. + + The [Product|Component|Package].license_expression is validated + against all the licenses from the current dataspace. + + The [ProductComponent|Subcomponent|ProductPackage].license_expression is validated + against the licenses assigned to the related Component/Package. + + If the related Component/Package is not set yet, or has no value for `license_expression`, + the expression is validated against all the licenses of the current dataspace. + """ + + relation_fk_field = None + expression_field_names = ["license_expression"] + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + for expression_field_name in self.expression_field_names: + expression_field = self.fields.get(expression_field_name) + if expression_field: + widget = expression_field.widget + final_attrs = widget.build_attrs( + base_attrs=widget.attrs, + extra_attrs=self.get_expression_widget_attrs(), + ) + expression_field.widget = LicenseExpressionWidget(final_attrs) + + def get_expression_widget_attrs(self): + if not self.relation_fk_field: + return {} + + related_model = self._meta.model._meta.get_field(self.relation_fk_field).related_model + related_model_name = related_model._meta.model_name + # Those 2 attrs are required to properly setup the license_expression_builder + attrs = { + "related_model_name": related_model_name.title(), + "related_api_url": reverse(f"api_v2:{related_model_name}-list"), + } + + instance = getattr(self, "instance", None) + related_object = getattr(instance, self.relation_fk_field, None) + + if instance and related_object: + attrs["related_object_license_expression"] = related_object.license_expression + if related_object.has_license_choices: + attrs["license_choices_expression"] = related_object.license_choices_expression + + return attrs + + def clean_license_expression(self): + """ + Return a normalized license expression string validated against + a list of known licenses in the dataspace or a related component/package. + + If the expression is empty, return an empty string. + + Raise ValidationError exceptions on validation errors. These + will not contain the list of known licenses (which is too big). + """ + expression = self.cleaned_data.get("license_expression") + + related_object = None + if self.relation_fk_field: + related_object = self.cleaned_data.get(self.relation_fk_field) + + if related_object and related_object.license_expression: + # for ProductComponent, Subcomponent, or ProductPackage + return clean_related_expression(expression, related_object) + # for Product, Component, ProductPackage, or ProductComponent without Component/Package FK, + # or without `license_expression` value. + return self.clean_expression_base(expression) + + def clean_expression_base(self, expression): + """ + Return a normalized license expression string validated against + the list of known licenses in the dataspace. + + If the expression is empty, return an empty string. + + Raise ValidationError exceptions on validation errors. These + will not contain the list of known licenses (which is too big). + """ + if not expression: + return "" + + # ImportModelForm, DejacodeMassUpdateForm + dataspace = getattr(self, "dataspace", None) + + if not dataspace: # DataspacedAdminForm + _object = getattr(self.request, "_object", None) + + # Instance Dataspace during edition, User Dataspace during addition + if self.instance.pk: + dataspace = self.instance.dataspace + elif _object: + # Editing in an alternate dataspace + dataspace = _object.dataspace + else: + dataspace = self.request.user.dataspace + + licenses = License.objects.scope(dataspace).for_expression() + normalized = normalize_and_validate_expression( + expression, licenses, validate_known=True, include_available=False + ) + return normalized or expression + + def extra_init(self, request, modeladmin): + """ + Add the client_data required for the license expression builder + in MassUpdateForm. + """ + modeladmin.setup_license_builder(request) + + +def validate_expression_on_relations(component): + """ + Return a mapping of errors as: + {relation model class: [id of objects with errors,...]]} + The errors are collected from the validation of the + Component.license_expression against + - the ProductComponent.license_expression + - the Subcomponent.license_expression. + """ + # NOTE: we do not need to resolve/validate the + # Component.license_expression here: it is fetched from the DB and + # has been stored as a valid expression made only of license keys. + + licensing = build_licensing(licenses=component.licenses.for_expression()) + relations = chain(component.related_parents.all(), component.productcomponents.all()) + + errors = defaultdict(list) + for relation in relations: + if not relation.license_expression: + continue + + try: + # Ensure that the relation uses license keys defined on the related component. + normalize_and_validate_expression( + relation.license_expression, licensing, validate_known=True, include_available=False + ) + except (ValidationError, ExpressionError, ParseError, TypeError): + errors[relation.__class__].append(relation.id) + + return errors + + +def combine_license_expressions(expressions, simplify=False): + """Return a license expression string combining multiple `expressions` with an AND.""" + expressions = [e for e in expressions if e and e.strip()] + + if len(expressions) == 1: + return expressions[0] + + licensing = Licensing() + # join the possible multiple detected license expression with an AND + expression_objects = [licensing.parse(e, simple=True) for e in expressions] + combined_expression_object = licensing.AND(*expression_objects) + if simplify: + combined_expression_object = combined_expression_object.simplify() + return str(combined_expression_object) + + +def get_unique_license_keys(license_expression): + licensing = build_licensing() + parsed = licensing.parse(license_expression, validate=False, strict=False, simple=True) + symbols = licensing.license_symbols(parsed, unique=True, decompose=True) + return {symbol.key for symbol in symbols} + + +def get_licensing_for_formatted_render(dataspace, show_policy=False, license_keys=None): + license_qs = License.objects.scope(dataspace).for_expression(show_policy, license_keys) + licensing = build_licensing(license_qs) + return licensing + + +def get_formatted_expression(licensing, license_expression, show_policy, show_category=False): + normalized = parse_expression( + license_expression, licenses=licensing, validate_known=False, validate_strict=False + ) + return normalized.render_as_readable( + as_link=True, show_policy=show_policy, show_category=show_category + ) diff --git a/component_catalog/management/commands/collectcpes.py b/component_catalog/management/commands/collectcpes.py new file mode 100644 index 00000000..cfd2b412 --- /dev/null +++ b/component_catalog/management/commands/collectcpes.py @@ -0,0 +1,162 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# DejaCode is a trademark of nexB Inc. +# SPDX-License-Identifier: AGPL-3.0-only +# See https://github.com/nexB/dejacode for support or download. +# See https://aboutcode.org for more information about AboutCode FOSS projects. +# + +import io +import zipfile +from collections import defaultdict +from pathlib import Path + +from django.core.management.base import CommandError + +import requests +from defusedxml import ElementTree + +from component_catalog.models import Component +from dje.management.commands import DataspacedCommand + + +class Command(DataspacedCommand): + help = ( + "Collects CPEs from the official CPE dictionary and set the cpe field value " + "when matches with DejaCode Components are found." + ) + + def add_arguments(self, parser): + super().add_arguments(parser) + parser.add_argument( + "cpe_dictionary_location", + help=( + "Local path on the disk of an official CPE dictionary as XML.\n" + "An URL pointing to a zipped XML is also supported, for example: " + "https://nvd.nist.gov/feeds/xml/cpe/dictionary/official-cpe-dictionary_v2.3.xml.zip" + ), + ) + + def handle(self, *args, **options): + super().handle(*args, **options) + + cpe_dictionary_location = options["cpe_dictionary_location"] + + if cpe_dictionary_location.startswith("http"): + self.stdout.write("Downloading the CPE dictionary...") + xml_content = self.load_cpe_from_url(url=cpe_dictionary_location) + else: + self.stdout.write("Loading the CPE dictionary from disk...") + xml_content = self.load_cpe_from_disk(cpe_dictionary_location) + + self.stdout.write("Extracting the CPE names from XML content...") + root = ElementTree.fromstring(xml_content) + cpe_element_schema = "{http://scap.nist.gov/schema/cpe-extension/2.3}cpe23-item" + + cpe_names = [] + for child in root: + for cpe23_item in child.findall(cpe_element_schema): + cpe_name = cpe23_item.attrib.get("name") + cpe_names.append(cpe_name) + + cpes_count = len(cpe_names) + self.stdout.write(f"Building an index of {cpes_count} CPEs for matching...") + cpe_index = self.build_cpe_index(cpe_names) + + component_updated_count = 0 + component_qs = Component.objects.scope(self.dataspace).filter(cpe="") + + self.stdout.write( + f"Matching {component_qs.count()} Components from the DejaCode catalog " + f"against the index of {cpes_count} CPEs..." + ) + for component in component_qs: + component_name = self.normalize_name(component.name) + component_version = self.normalize_version(component.version) + + name_match = cpe_index.get(component_name) + if name_match: + version_match = name_match.get(component_version) + + if version_match: + component_updated_count += 1 + Component.objects.filter(id=component.id).update(cpe=version_match) + + msg = f"{component_updated_count} Component(s) updated." + self.stdout.write(self.style.SUCCESS(msg)) + + @staticmethod + def normalize_name(name): + """ + Return a normalized name replacing empty spaces, the "-" char, + and lowering the whole string. + """ + return name.replace(" ", "_").replace("-", "_").lower() + + @staticmethod + def normalize_version(version): + """ + Return a normalized version replacing empty spaces, the "-" char, + and lowering the whole string. + Also, null version in the cpe system are declared with the '-' char, + this is replaced by empty string representing null version on the DejaCode side. + If the version ends with ".0", this bit is removed so we can match on version where + the ".0" is not present on the cpe side or DejaCode side. + For example: "3.2" on the cpe side will match "3.2.0" on the DejaCode side once the + normalization is applied. + """ + if version == "-": + return "" + + return version.replace(" ", "_").replace("-", "_").lower().removesuffix(".0") + + @staticmethod + def load_cpe_from_disk(cpe_dictionary_location): + """Return the XML content of the file at `cpe_dictionary_location`.""" + cpe_dictionary_path = Path(cpe_dictionary_location) + if not cpe_dictionary_path.exists(): + raise CommandError(f"{cpe_dictionary_location} not found.") + + xml_content = cpe_dictionary_path.read_text() + return xml_content + + @staticmethod + def load_cpe_from_url(url): + """Return the XML content of the zipfile downloaded from `url`.""" + response = requests.get(url, timeout=10) + + try: + response.raise_for_status() + except requests.RequestException as e: + raise CommandError(e) + + zip_document = zipfile.ZipFile(io.BytesIO(response.content)) + namelist = zip_document.namelist() + + if len(namelist) != 1 and not namelist[0].endswith("xml"): + raise CommandError("CPE dictionary .xml file not found in .zip") + + xml_content = zip_document.read(name=namelist[0]) + return xml_content + + def build_cpe_index(self, cpe_names): + """ + Build an index of CPEs as a dictionary keyed by `name`. + Format: + { + 'component_name': { + 'version_1': 'cpe', + 'version_2': 'cpe', + } + } + """ + cpe_index = defaultdict(dict) + + for cpe in cpe_names: + cpe_parts = cpe.split(":") + _, name, version = cpe_parts[3], cpe_parts[4], cpe_parts[5] + name = self.normalize_name(name) + version = self.normalize_version(version) + cpe_index[name][version] = cpe + + return cpe_index diff --git a/component_catalog/management/commands/collectpackagesdata.py b/component_catalog/management/commands/collectpackagesdata.py new file mode 100644 index 00000000..b4631391 --- /dev/null +++ b/component_catalog/management/commands/collectpackagesdata.py @@ -0,0 +1,60 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# DejaCode is a trademark of nexB Inc. +# SPDX-License-Identifier: AGPL-3.0-only +# See https://github.com/nexB/dejacode for support or download. +# See https://aboutcode.org for more information about AboutCode FOSS projects. +# + +from django.db.models import Q + +from component_catalog.models import Package +from dje.management.commands import DataspacedCommand + + +class Command(DataspacedCommand): + help = ('Collects and saves md5, sha1, and size values where one of those ' + 'are missing in the given Dataspace on Package instances.') + + def add_arguments(self, parser): + super().add_arguments(parser) + parser.add_argument( + '--save', + action='store_true', + dest='save', + default=False, + help='Use save() in place of update() (default) to trigger all ' + 'associated logic and signals. Fields such as last_modified_date ' + 'will be updated.', + ) + + def handle(self, *args, **options): + super().handle(*args, **options) + + packages = ( + Package.objects.scope(self.dataspace) + .exclude(download_url='') + .filter(Q(md5='') | Q(sha1='') | Q(size__isnull=True)) + ) + + self.stdout.write(f'{packages.count()} Packages in the queue.') + + update_count = 0 + for package in packages: + self.stdout.write(f'Collecting: {package.download_url}') + update_fields = package.collect_data(save=False) + if not update_fields: + continue + + if options['save']: + package.save() + else: + Package.objects.filter(pk=package.pk).update( + **{field: getattr(package, field) for field in update_fields} + ) + + self.stdout.write(f"{', '.join(update_fields)} updated") + update_count += 1 + + msg = f'{update_count} Package(s) updated.' + self.stdout.write(self.style.SUCCESS(msg)) diff --git a/component_catalog/management/commands/componentfrompackage.py b/component_catalog/management/commands/componentfrompackage.py new file mode 100644 index 00000000..5d2ab02d --- /dev/null +++ b/component_catalog/management/commands/componentfrompackage.py @@ -0,0 +1,166 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# DejaCode is a trademark of nexB Inc. +# SPDX-License-Identifier: AGPL-3.0-only +# See https://github.com/nexB/dejacode for support or download. +# See https://aboutcode.org for more information about AboutCode FOSS projects. +# + +from django.contrib.auth import get_user_model +from django.core.exceptions import ObjectDoesNotExist +from django.core.exceptions import ValidationError +from django.core.management.base import CommandError +from django.forms.models import model_to_dict + +from component_catalog.models import Component +from component_catalog.models import ComponentAssignedPackage +from component_catalog.models import ComponentStatus +from component_catalog.models import ComponentType +from component_catalog.models import Package +from dje.management.commands import DataspacedCommand +from organization.models import Owner + + +class Command(DataspacedCommand): + help = "Create Components from Packages" + + def add_arguments(self, parser): + super().add_arguments(parser) + parser.add_argument("username", help="Your username, for History entries.") + parser.add_argument( + "--last_modified_date", + help=( + "Limit the packages batch to objects created/modified after that date. " + 'Format: "YYYY-MM-DD"' + ), + ) + + def handle(self, *args, **options): + super().handle(*args, **options) + + try: + self.user = get_user_model().objects.get( + username=options["username"], + dataspace=self.dataspace, + ) + except ObjectDoesNotExist: + raise CommandError("The given username does not exist.") + + # Packages without any assigned components + package_qs = Package.objects.scope(self.dataspace).filter(component__isnull=True) + + if last_modified_date := options["last_modified_date"]: + try: + package_qs = package_qs.filter(last_modified_date__gt=last_modified_date) + except ValidationError as e: + raise CommandError(e) + + self.component_qs = Component.objects.scope(self.dataspace) + self.owner_qs = Owner.objects.scope(self.dataspace) + self.approved_status = ComponentStatus.objects.scope(self.dataspace).get(label="Approved") + self.file_type = ComponentType.objects.scope(self.dataspace).get(label="File") + + created = [] + errors = [] + + for package in package_qs: + component = None + try: + component = self.create_component_from_package(package) + except Exception as e: + errors.append(e) + + if component: + created.append(component) + _ = ComponentAssignedPackage.objects.create( + component=component, + package=package, + dataspace=self.dataspace, + ) + + self.stdout.write(self.style.SUCCESS(f"{len(created)} Component(s) created.")) + if errors: + self.stdout.write(self.style.ERROR(f"{len(errors)} errors:")) + for error in errors: + self.stdout.write(self.style.ERROR(f"- {error}")) + + def create_component_from_package(self, package): + # When a component with this name/version already exist, skip + if self.component_qs.filter(name__iexact=package.name, version=package.version): + return + + component_data = model_to_dict(package) + component_data.pop("id", None) + # ``licenses`` are assigned from the ``license_expression`` field + component_data.pop("licenses", None) + component_data["curation_level"] = 45 + # ForeignKeys + component_data["configuration_status"] = self.approved_status + component_data["type"] = self.file_type + # Package usage policies are not shared with components + # The proper policy will be set from the ``license_expression`` value + component_data.pop("usage_policy", None) + + if inferred_url := package.inferred_url: + component_data["code_view_url"] = inferred_url + component_data["homepage_url"] = inferred_url + + if package.notice_text: + component_data["is_license_notice"] = True + component_data["is_notice_in_codebase"] = True + + # Check for components that already exists in a different version, + # we can use the owner the keywords from that component + if existing := self.component_qs.filter(name__iexact=package.name, owner__isnull=False): + component_data["owner"] = existing[0].owner + if keywords := existing[0].keywords: + component_data["keywords"] = keywords + + # Set owner + if not component_data.get("owner"): + component_data["owner"] = self.get_owner(component_data) + + component = Component.create_from_data(self.user, component_data, validate=False) + component.update_completion_level() + return component + + def get_owner(self, component_data): + owner_name = "" + contact_info = "" + owner_type = "" + + # Start with the ``parties`` field + parties = component_data.get("parties") + if parties and parties[0].get("name"): + owner_name = parties[0].get("name") + owner_type = "Person" + contact_info = parties[0].get("email") or "" + # If there is a ``holder`` value on the package, search reference data Owners + # for that and use it if found, or create a new owner otherwise. + elif holder := component_data.get("holder"): + owner_name = holder + # If the component name, derived from the package name, is already in reference + # data components in a different version, use the owner of that component. + else: + owner_name = component_data.get("name") + if related_components := self.component_qs.filter(name__icontains=owner_name): + return related_components[0].owner + + if match := self.owner_qs.filter(name__icontains=owner_name): + return match[0] + elif match := self.owner_qs.filter(name__icontains=f"{owner_name} Project"): + return match[0] + + if owner_name: + owner, _ = self.owner_qs.get_or_create( + name=f"{owner_name}", + dataspace=self.dataspace, + defaults={ + "contact_info": contact_info, + "type": owner_type, + "created_by": self.user, + }, + ) + return owner + + self.stdout.write(f"Cannot found owner for {component_data.get('name')}") diff --git a/component_catalog/management/commands/setpurls.py b/component_catalog/management/commands/setpurls.py new file mode 100644 index 00000000..e5d5b357 --- /dev/null +++ b/component_catalog/management/commands/setpurls.py @@ -0,0 +1,111 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# DejaCode is a trademark of nexB Inc. +# SPDX-License-Identifier: AGPL-3.0-only +# See https://github.com/nexB/dejacode for support or download. +# See https://aboutcode.org for more information about AboutCode FOSS projects. +# + +import traceback + +from django.contrib.auth import get_user_model +from django.core.exceptions import ObjectDoesNotExist +from django.core.management.base import CommandError + +from component_catalog.models import Package +from dje.management.commands import DataspacedCommand + + +class Command(DataspacedCommand): + help = ( + 'Set the Package URL "purl" generated from the Download URL field ' + "on Package instances in the given Dataspace." + ) + + def add_arguments(self, parser): + super().add_arguments(parser) + parser.add_argument("username", help="Your username, for History entries.") + parser.add_argument( + "--overwrite", + action="store_true", + dest="overwrite", + default=False, + help="Overwrite the existing Package URL values.", + ) + parser.add_argument( + "--save", + action="store_true", + dest="save", + default=False, + help="Use save() in place of update() (default) to trigger all " + "associated logic and signals. Fields such as last_modified_date " + "will be updated.", + ) + parser.add_argument( + "--history", + action="store_true", + dest="history", + default=False, + help="Create CHANGE History entries on Package when Package URL is set.", + ) + + @staticmethod + def get_purl_summary(packages): + package_count = packages.count() + purl_count = packages.exclude(type="").count() + no_purl_count = package_count - purl_count + purl_percent = int(purl_count / package_count * 100) + msg = ( + f"{package_count:,d} Packages, " + f"{purl_count:,d} ({purl_percent}%) with a Package URL, " + f"{no_purl_count:,d} without." + ) + return msg + + def handle(self, *args, **options): + super().handle(*args, **options) + + try: + user = get_user_model().objects.get( + username=options["username"], + dataspace=self.dataspace, + ) + except ObjectDoesNotExist: + raise CommandError("The given username does not exist.") + + packages = Package.objects.scope(self.dataspace) + pre_update_summary = self.get_purl_summary(packages) + update_count = 0 + error_count = 0 + + for package in packages.iterator(chunk_size=2000): + try: + package_url = package.update_package_url( + user=user, + save=options["save"], + overwrite=options["overwrite"], + history=options["history"], + ) + except Exception: + self.stderr.write( + f"Error encountered when processing Package:" + f" {str(package)} ({package.uuid})" + ) + self.stderr.write(traceback.format_exc()) + error_count += 1 + continue + + if package_url: + update_count += 1 + + if options["verbosity"] > 1: + self.stdout.write(f"Set {package_url} from {package.download_url}") + + msg = ( + f"{update_count:,d} Package(s) updated with a Package URL in " + f"the {self.dataspace} Dataspace." + ) + self.stdout.write(self.style.SUCCESS(msg)) + self.stdout.write("Pre-update: " + pre_update_summary) + self.stdout.write("Post-update: " + self.get_purl_summary(packages)) + self.stdout.write(f"Number of errors encountered when updating Packages: " f"{error_count}") diff --git a/component_catalog/management/commands/updatecompletionlevels.py b/component_catalog/management/commands/updatecompletionlevels.py new file mode 100644 index 00000000..10a1a7ef --- /dev/null +++ b/component_catalog/management/commands/updatecompletionlevels.py @@ -0,0 +1,27 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# DejaCode is a trademark of nexB Inc. +# SPDX-License-Identifier: AGPL-3.0-only +# See https://github.com/nexB/dejacode for support or download. +# See https://aboutcode.org for more information about AboutCode FOSS projects. +# + +from component_catalog.models import Component +from dje.management.commands import DataspacedCommand + + +class Command(DataspacedCommand): + help = ( + "Updates the completion level value of all Component objects. " + "This will not trigger a save if the computed value is identical " + "to the stored value." + ) + + def handle(self, *args, **options): + super().handle(*args, **options) + + components = Component.objects.scope(self.dataspace) + updated_count = sum(1 for component in components if component.update_completion_level()) + + msg = f"{updated_count} Component(s) updated." + self.stdout.write(self.style.SUCCESS(msg)) diff --git a/component_catalog/migrations/0001_initial.py b/component_catalog/migrations/0001_initial.py new file mode 100644 index 00000000..e01bde12 --- /dev/null +++ b/component_catalog/migrations/0001_initial.py @@ -0,0 +1,1198 @@ +# Generated by Django 4.2.7 on 2023-11-27 21:12 + +import component_catalog.models +import django.contrib.postgres.fields +import django.core.validators +from django.db import migrations, models +import dje.fields +import dje.models +import policy.models +import re +import uuid + + +class Migration(migrations.Migration): + initial = True + + dependencies = [] + + operations = [ + migrations.CreateModel( + name="AcceptableLinkage", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "uuid", + models.UUIDField( + default=uuid.uuid4, editable=False, verbose_name="UUID" + ), + ), + ("label", models.CharField(max_length=40)), + ("description", models.TextField(blank=True)), + ], + options={ + "ordering": ["label"], + }, + ), + migrations.CreateModel( + name="Component", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "uuid", + models.UUIDField( + default=uuid.uuid4, editable=False, verbose_name="UUID" + ), + ), + ( + "created_date", + models.DateTimeField( + auto_now_add=True, + db_index=True, + help_text="The date and time the object was created.", + ), + ), + ( + "last_modified_date", + models.DateTimeField( + auto_now=True, + db_index=True, + help_text="The date and time the object was last modified.", + ), + ), + ( + "reference_notes", + models.TextField( + blank=True, + help_text="Reference Notes provide background details about the sofware and licenses in DejaCode, alerting you to pertinent ownership history or licensing complexities", + ), + ), + ( + "request_count", + models.PositiveSmallIntegerField(blank=True, null=True), + ), + ( + "holder", + models.TextField( + blank=True, + help_text="The name(s) of the copyright holder(s) of a software package as documented in the code. This field is intended to record the copyright holder independently of copyright statement dates and formats, and generally corresponds to the owner of the associated software project.", + ), + ), + ( + "keywords", + dje.fields.JSONListField( + blank=True, + default=list, + help_text="A keyword is a category or label that helps you to find items for particular requirements.", + ), + ), + ( + "cpe", + models.CharField( + blank=True, + help_text="Common Platform Enumeration (CPE) is a standardized method of describing and identifying a computing asset. CPE does not necessarily identify a unique instance or version of a computing asset. For example, a CPE could identify a component name with a version range.", + max_length=1024, + verbose_name="CPE", + ), + ), + ( + "vcs_url", + models.CharField( + blank=True, + help_text="URL to the Version Control System (VCS).", + max_length=1024, + validators=[ + django.core.validators.RegexValidator( + re.compile("^[\\w+-_]+://[\\S]+$"), + code="invalid", + message="Enter a valid URI.", + ) + ], + verbose_name="VCS URL", + ), + ), + ( + "code_view_url", + models.URLField( + blank=True, + help_text="A URL that allows you to browse and view the source code online.", + max_length=1024, + verbose_name="Code view URL", + ), + ), + ( + "bug_tracking_url", + models.URLField( + blank=True, + help_text="A URL to the bug reporting system.", + max_length=1024, + verbose_name="Bug tracking URL", + ), + ), + ( + "name", + models.CharField( + db_index=True, + help_text="Name by which the component is commonly referenced.", + max_length=100, + validators=[ + django.core.validators.RegexValidator( + re.compile("^[ .a-zA-Z0-9!#\"\\':,&()+_-]+$"), + "Enter a valid value consisting of spaces, periods, letters, numbers, or !#\"':,&()+_-.", + "invalid", + ) + ], + ), + ), + ( + "version", + models.CharField( + blank=True, + db_index=True, + help_text="Identifies a specific version of a component. The combination of name + version uniquely identifies a component. If the version is (nv) or blank, it signifies an unstated/unknown version (or it indicates that version does not apply to the component), and it does not imply that the information in this component definition applies to any or all possible versions of the component.", + max_length=100, + validators=[ + django.core.validators.RegexValidator( + re.compile("^[ .a-zA-Z0-9!#\"\\',&()+_-]+$"), + "Enter a valid value consisting of spaces, periods, letters, numbers, or !#\"',&()+_-.", + "invalid", + ) + ], + ), + ), + ( + "release_date", + models.DateField( + blank=True, + help_text="The date that the component was released by its owner.", + null=True, + ), + ), + ( + "description", + models.TextField( + blank=True, + help_text="Free form description, preferably as provided by the author(s).", + ), + ), + ( + "copyright", + models.TextField( + blank=True, + help_text="The copyright statement(s) that pertain to this component, as contained in the source or as specified in an associated file.", + ), + ), + ( + "homepage_url", + models.URLField( + blank=True, + help_text="Homepage URL for the component.", + max_length=1024, + verbose_name="Homepage URL", + ), + ), + ( + "primary_language", + models.CharField( + blank=True, + db_index=True, + help_text="The primary programming language associated with the component.", + max_length=50, + ), + ), + ( + "admin_notes", + models.TextField( + blank=True, + help_text="Comments about the component, provided by administrators, intended for viewing and maintenance by administrators only.", + ), + ), + ( + "notice_text", + dje.fields.NoStripTextField( + blank=True, + help_text="The notice text provided by the authors of a component to identify the copyright statement(s), contributors, and/or license obligations that apply to a component.", + ), + ), + ( + "license_expression", + models.CharField( + blank=True, + db_index=True, + help_text='On a component or a product in DejaCode, a license expression defines the relationship of one or more licenses to that software as declared by its licensor. More than one applicable license can be expressed as "license-key-a AND license-key-b". A choice of applicable licenses can be expressed as "license-key-a OR license-key-b", and you can indicate the primary (preferred) license by placing it first, on the left-hand side of the OR relationship. The relationship words (OR, AND) can be combined as needed, and the use of parentheses can be applied to clarify the meaning; for example "((license-key-a AND license-key-b) OR (license-key-c))". An exception to a license can be expressed as “license-key WITH license-exception-key".', + max_length=1024, + verbose_name="License expression", + ), + ), + ( + "approval_reference", + models.CharField( + blank=True, + help_text="The name or number of a document (e.g. approval document, contract, etc.) that indicates that the component is approved for use in your organization.", + max_length=200, + ), + ), + ( + "guidance", + models.TextField( + blank=True, + help_text="Component usage guidance is provided by your organization to specify recommendations, requirements and restrictions regarding your usage of this component.", + verbose_name="Component usage guidance", + ), + ), + ( + "is_active", + models.BooleanField( + db_index=True, + default=True, + help_text="When set to True (Yes), this field indicates that a component definition in the catalog is currently in use (active). When set to False (No), this field indicates that a component is deprecated (inactive) and should not be used, and the component will not appear in the user views. When the field value is Unknown, the component will not appear in the user views, usually suggesting that the component has not yet been evaluated.", + null=True, + verbose_name="Is active", + ), + ), + ( + "curation_level", + models.PositiveSmallIntegerField( + db_index=True, + default=0, + help_text='A numeric value, from 0 to 100, that indicates the level of completeness of all the pertinent component data, as well as the state of that data being reviewed by a senior administrator. General guidelines: "10" indicates basic data present. "20" indicates copyright and notice data are provided. assigned. "30" indicates all license data are provided. "40" indicates all available technical details (including URLs and primary language) are provided. "50" indicates that relevant parent and child are provided. Any other values are at the discretion of a senior administrative reviewer.', + validators=[django.core.validators.MaxValueValidator(100)], + ), + ), + ( + "completion_level", + models.PositiveSmallIntegerField( + db_index=True, + default=0, + help_text="Completion level is a number automatically calculated by the application to indicate the completeness of the data for a specific component. Fields that influence the calculation include: Notice Text, Copyright, Description, Package, Homepage URL, Keyword, License, Notice Filename, Notice URL, Bug Tracking URL, Code View URL, Primary Language, Release Date, VCS URL, Owner, Type, and Version.", + ), + ), + ( + "is_license_notice", + models.BooleanField( + help_text="Indicator (Yes, No, Unknown) regarding whether the notice text contains a statement about the licenses that apply to the component as a whole.", + null=True, + ), + ), + ( + "is_copyright_notice", + models.BooleanField( + help_text="Indicator (Yes, No, Unknown) regarding whether the notice text contains one or more copyright statements that apply to the component as a whole.", + null=True, + ), + ), + ( + "is_notice_in_codebase", + models.BooleanField( + help_text="Indicator (Yes, No, Unknown) regarding whether a notice is internal to a component (for example, if the notice text is in the source file header of the component).", + null=True, + ), + ), + ( + "notice_filename", + models.CharField( + blank=True, + help_text="Optional filename to identify a notice file associated with a component. If a filename is not provided, the application will assume that this notice is internal to this component (for example, in a source file header).", + max_length=255, + ), + ), + ( + "notice_url", + models.URLField( + blank=True, + help_text="A URL that contains notice text for a component.", + max_length=1024, + verbose_name="Notice URL", + ), + ), + ( + "dependencies", + models.JSONField( + blank=True, + default=list, + help_text="Identifies one or more potential dependencies required to deploy a component in a particular context, with an emphasis on dependencies that may have an impact on licensing and/or attribution obligations.", + ), + ), + ( + "project", + models.CharField( + blank=True, + db_index=True, + help_text="Project is a free-form label that you can use to group and find packages and components that interest you; for example, you may be starting a new development project, evaluating them for use in a product or you may want to get approval to use them.", + max_length=50, + ), + ), + ( + "codescan_identifier", + models.URLField( + blank=True, + help_text="A component identifier from a code scanning application.", + max_length=1024, + ), + ), + ( + "website_terms_of_use", + models.TextField( + blank=True, + help_text="The Terms of Use or Terms of Service specified on a software project website. These are terms that apply in addition to or in absence of an asserted license for a component package.", + ), + ), + ( + "ip_sensitivity_approved", + models.BooleanField( + default=False, + help_text="The component software can be combined with sensitive or critical IP, as determined by legal review. This information will be used for product architecture review.", + verbose_name="IP sensitivity approved", + ), + ), + ( + "affiliate_obligations", + models.BooleanField( + default=False, + help_text="The component license contains terms that would impose obligations on a legal entity affiliate.", + ), + ), + ( + "affiliate_obligation_triggers", + models.TextField( + blank=True, + help_text="Explanation of how affiliate obligations are triggered, and what is the mitigation strategy.", + ), + ), + ( + "concluded_license", + models.CharField( + blank=True, + db_index=True, + help_text="This is a memo field to record the conclusions of the legal team after full review and scanning of the component package, and is only intended to document that decision. The main value of the field is to clarify the company interpretation of the license that should apply to a component when there is a choice, or when there is ambiguity in the original component documentation.", + max_length=1024, + ), + ), + ( + "legal_comments", + models.TextField( + blank=True, + help_text="Notes to be entered and shared among legal team members during the legal review process.", + ), + ), + ( + "sublicense_allowed", + models.BooleanField( + help_text="The component license grants some or all of the rights acquired under the original license, and allows usage of the licensed code to be licensed under an overriding license, although obligations such as attribution generally still apply. This allowance is typical of permissive licenses, but is often not allowed in copyleft and proprietary licenses. The right to sublicense is explicit in some license texts (such as the MIT License) but is not always stated; implicit permission to sublicense is a legal interpretation.", + null=True, + ), + ), + ( + "express_patent_grant", + models.BooleanField( + help_text="The license that applies to this component expressly grants a patent license.", + null=True, + ), + ), + ( + "covenant_not_to_assert", + models.BooleanField( + help_text="The license that applies to this component has language that we agree not to assert our patents against users of a project under this license.", + null=True, + ), + ), + ( + "indemnification", + models.BooleanField( + help_text="The license that applies to this component has one or more scenarios that require indemnification.", + null=True, + ), + ), + ( + "legal_reviewed", + models.BooleanField( + default=False, + help_text="This component definition has been reviewed by the organization legal team.", + ), + ), + ( + "distribution_formats_allowed", + models.CharField( + blank=True, + choices=[ + ("Binary", "Binary"), + ("Source", "Source"), + ("All - Binary or Source", "All - Binary or Source"), + ], + default="", + help_text="The software distribution formats allowed by the component license.", + max_length=30, + ), + ), + ( + "acceptable_linkages", + django.contrib.postgres.fields.ArrayField( + base_field=models.CharField(max_length=40), + blank=True, + help_text="Your organization's legal review team can identify one or more specific linkages (software interactions) that are acceptable between this component and your organization's products in order to comply with your organization's license compliance standards.", + null=True, + size=None, + ), + ), + ( + "export_restrictions", + models.TextField( + blank=True, + help_text="The export restrictions and/or requirements associated with a component as determined by legal review.", + ), + ), + ( + "approved_download_location", + models.URLField( + blank=True, + help_text="The link to a pristine (unmodified) component package download as specified by legal review.", + max_length=1024, + ), + ), + ( + "approved_community_interaction", + models.TextField( + blank=True, + help_text="The community interaction allowed with this software project.", + ), + ), + ], + options={ + "ordering": ("name", "version"), + "permissions": ( + ( + "change_usage_policy_on_component", + "Can change the usage_policy of component", + ), + ), + "abstract": False, + }, + bases=( + policy.models.SetPolicyFromLicenseMixin, + dje.models.ParentChildModelMixin, + component_catalog.models.DefaultOnAdditionMixin, + component_catalog.models.LicenseExpressionMixin, + models.Model, + ), + ), + migrations.CreateModel( + name="ComponentAssignedLicense", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "uuid", + models.UUIDField( + default=uuid.uuid4, editable=False, verbose_name="UUID" + ), + ), + ], + options={ + "verbose_name": "Assigned license", + "ordering": ("component__name", "license__name"), + }, + ), + migrations.CreateModel( + name="ComponentAssignedPackage", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "uuid", + models.UUIDField( + default=uuid.uuid4, editable=False, verbose_name="UUID" + ), + ), + ], + options={ + "verbose_name": "component assigned package", + "ordering": ("component", "package"), + }, + ), + migrations.CreateModel( + name="ComponentKeyword", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "uuid", + models.UUIDField( + default=uuid.uuid4, editable=False, verbose_name="UUID" + ), + ), + ( + "label", + models.CharField( + blank=True, + help_text="A short, descriptive label to categorize components and support searches, reports, filters, and facets.", + max_length=50, + ), + ), + ( + "description", + models.TextField( + blank=True, + help_text="Additional remarks about the intention and purpose of a Keyword value.", + ), + ), + ], + options={ + "ordering": ["label"], + }, + ), + migrations.CreateModel( + name="ComponentStatus", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "uuid", + models.UUIDField( + default=uuid.uuid4, editable=False, verbose_name="UUID" + ), + ), + ( + "default_on_addition", + models.BooleanField( + default=False, + help_text="Indicates this instance is automatically assigned by the application to an object when it is initially created.", + verbose_name="Default on addition", + ), + ), + ( + "label", + models.CharField( + help_text="Concise name to identify the status.", max_length=50 + ), + ), + ( + "text", + models.TextField( + help_text="Descriptive text to define the status purpose precisely." + ), + ), + ], + options={ + "verbose_name_plural": "component status", + "ordering": ["label"], + "abstract": False, + }, + ), + migrations.CreateModel( + name="ComponentType", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "uuid", + models.UUIDField( + default=uuid.uuid4, editable=False, verbose_name="UUID" + ), + ), + ( + "label", + models.CharField( + help_text="Label that indicates the scope, function, and complexity of a component. Every dataspace has its own list of component types. Examples include: product, package, project, assembly, module, platform, directory, file, snippet.", + max_length=50, + ), + ), + ( + "notes", + models.TextField( + blank=True, help_text="Optional descriptive text." + ), + ), + ], + options={ + "ordering": ["label"], + }, + ), + migrations.CreateModel( + name="Package", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "uuid", + models.UUIDField( + default=uuid.uuid4, editable=False, verbose_name="UUID" + ), + ), + ( + "created_date", + models.DateTimeField( + auto_now_add=True, + db_index=True, + help_text="The date and time the object was created.", + ), + ), + ( + "last_modified_date", + models.DateTimeField( + auto_now=True, + db_index=True, + help_text="The date and time the object was last modified.", + ), + ), + ( + "reference_notes", + models.TextField( + blank=True, + help_text="Reference Notes provide background details about the sofware and licenses in DejaCode, alerting you to pertinent ownership history or licensing complexities", + ), + ), + ( + "request_count", + models.PositiveSmallIntegerField(blank=True, null=True), + ), + ( + "type", + models.CharField( + blank=True, + help_text="A short code to identify the type of this package. For example: gem for a Rubygem, docker for a container, pypi for a Python Wheel or Egg, maven for a Maven Jar, deb for a Debian package, etc.", + max_length=16, + ), + ), + ( + "namespace", + models.CharField( + blank=True, + help_text="Package name prefix, such as Maven groupid, Docker image owner, GitHub user or organization, etc.", + max_length=255, + ), + ), + ( + "name", + models.CharField( + blank=True, help_text="Name of the package.", max_length=100 + ), + ), + ( + "version", + models.CharField( + blank=True, help_text="Version of the package.", max_length=100 + ), + ), + ( + "qualifiers", + models.CharField( + blank=True, + help_text="Extra qualifying data for a package such as the name of an OS, architecture, distro, etc.", + max_length=1024, + ), + ), + ( + "subpath", + models.CharField( + blank=True, + help_text="Extra subpath within a package, relative to the package root.", + max_length=200, + ), + ), + ( + "holder", + models.TextField( + blank=True, + help_text="The name(s) of the copyright holder(s) of a software package as documented in the code. This field is intended to record the copyright holder independently of copyright statement dates and formats, and generally corresponds to the owner of the associated software project.", + ), + ), + ( + "keywords", + dje.fields.JSONListField( + blank=True, + default=list, + help_text="A keyword is a category or label that helps you to find items for particular requirements.", + ), + ), + ( + "cpe", + models.CharField( + blank=True, + help_text="Common Platform Enumeration (CPE) is a standardized method of describing and identifying a computing asset. CPE does not necessarily identify a unique instance or version of a computing asset. For example, a CPE could identify a component name with a version range.", + max_length=1024, + verbose_name="CPE", + ), + ), + ( + "homepage_url", + models.URLField( + blank=True, + help_text="Homepage URL.", + max_length=1024, + verbose_name="Homepage URL", + ), + ), + ( + "vcs_url", + models.CharField( + blank=True, + help_text="URL to the Version Control System (VCS).", + max_length=1024, + validators=[ + django.core.validators.RegexValidator( + re.compile("^[\\w+-_]+://[\\S]+$"), + code="invalid", + message="Enter a valid URI.", + ) + ], + verbose_name="VCS URL", + ), + ), + ( + "code_view_url", + models.URLField( + blank=True, + help_text="A URL that allows you to browse and view the source code online.", + max_length=1024, + verbose_name="Code view URL", + ), + ), + ( + "bug_tracking_url", + models.URLField( + blank=True, + help_text="A URL to the bug reporting system.", + max_length=1024, + verbose_name="Bug tracking URL", + ), + ), + ( + "sha256", + models.CharField( + blank=True, + help_text="SHA256 checksum hex-encoded, as in sha256sum.", + max_length=64, + verbose_name="SHA256", + ), + ), + ( + "sha512", + models.CharField( + blank=True, + help_text="SHA512 checksum hex-encoded, as in sha512sum.", + max_length=128, + verbose_name="SHA512", + ), + ), + ( + "filename", + models.CharField( + blank=True, + db_index=True, + help_text="The exact file name (typically an archive of some type) of the package. This is usually the name of the file as downloaded from a website.", + max_length=255, + validators=[component_catalog.models.validate_filename], + verbose_name="Filename", + ), + ), + ( + "download_url", + models.CharField( + blank=True, + help_text="The download URL for obtaining the package.", + max_length=1024, + validators=[ + django.core.validators.RegexValidator( + re.compile("^[\\w+-_]+://[\\S]+$"), + code="invalid", + message="Enter a valid URI.", + ) + ], + verbose_name="Download URL", + ), + ), + ( + "sha1", + models.CharField( + blank=True, + db_index=True, + help_text="The SHA1 signature of the package file.", + max_length=40, + verbose_name="SHA1", + ), + ), + ( + "md5", + models.CharField( + blank=True, + db_index=True, + help_text="The MD5 signature of the package file.", + max_length=32, + verbose_name="MD5", + ), + ), + ( + "size", + models.BigIntegerField( + blank=True, + db_index=True, + help_text="The size of the package file in bytes.", + null=True, + ), + ), + ( + "release_date", + models.DateField( + blank=True, + help_text="The date that the package file was created, or when it was posted to its original download source.", + null=True, + ), + ), + ( + "primary_language", + models.CharField( + blank=True, + db_index=True, + help_text="The primary programming language associated with the package.", + max_length=50, + ), + ), + ( + "description", + models.TextField( + blank=True, + help_text="Free form description, preferably as provided by the author(s).", + ), + ), + ( + "project", + models.CharField( + blank=True, + db_index=True, + help_text="Project is a free-form label that you can use to group and find packages and components that interest you; for example, you may be starting a new development project, evaluating them for use in a product or you may want to get approval to use them.", + max_length=50, + ), + ), + ( + "notes", + models.TextField( + blank=True, + help_text="Descriptive information about the package.", + ), + ), + ( + "license_expression", + models.CharField( + blank=True, + db_index=True, + help_text='On a package in DejaCode, a license expression defines the relationship of one or more licenses to that software as declared by its licensor. More than one applicable license can be expressed as "license-key-a AND license-key-b". A choice of applicable licenses can be expressed as "license-key-a OR license-key-b", and you can indicate the primary (preferred) license by placing it first, on the left-hand side of the OR relationship. The relationship words (OR, AND) can be combined as needed, and the use of parentheses can be applied to clarify the meaning; for example "((license-key-a AND license-key-b) OR (license-key-c))". An exception to a license can be expressed as “license-key WITH license-exception-key".', + max_length=1024, + verbose_name="License expression", + ), + ), + ( + "copyright", + models.TextField( + blank=True, + help_text="The copyright statement(s) that pertain to this package, as contained in the source or as specified in an associated file.", + ), + ), + ( + "notice_text", + dje.fields.NoStripTextField( + blank=True, + help_text="The notice text provided by the authors of a package to identify the copyright statement(s), contributors, and/or license obligations that apply to a package.", + ), + ), + ( + "author", + models.TextField( + blank=True, + help_text="The name(s) of the author(s) of a software package as documented in the code.", + ), + ), + ( + "dependencies", + models.JSONField( + blank=True, + default=list, + help_text="Identifies one or more potential dependencies required to deploy a package in a particular context, with an emphasis on dependencies that may have an impact on licensing and/or attribution obligations.", + ), + ), + ( + "repository_homepage_url", + models.URLField( + blank=True, + help_text="URL to the page for this package in its package repository. This is typically different from the package homepage URL proper.", + max_length=1024, + verbose_name="Repository homepage URL", + ), + ), + ( + "repository_download_url", + models.URLField( + blank=True, + help_text="Download URL to download the actual archive of code of this package in its package repository. This may be different from the actual download URL.", + max_length=1024, + verbose_name="Repository download URL", + ), + ), + ( + "api_data_url", + models.URLField( + blank=True, + help_text="API URL to obtain structured data for this package such as the URL to a JSON or XML api its package repository.", + max_length=1024, + verbose_name="API data URL", + ), + ), + ( + "declared_license", + models.TextField( + blank=True, + help_text="The declared license mention, tag or text as found in a package manifest. This can be a string, a list or dict of strings possibly nested, as found originally in the manifest.", + ), + ), + ( + "datasource_id", + models.CharField( + blank=True, + help_text="The identifier for the datafile handler used to obtain this package.", + max_length=64, + ), + ), + ( + "file_references", + models.JSONField( + blank=True, + default=list, + help_text="List of file paths and details for files referenced in a package manifest. These may not actually exist on the filesystem. The exact semantics and base of these paths is specific to a package type or datafile format.", + ), + ), + ( + "parties", + models.JSONField( + blank=True, + default=list, + help_text="A list of parties such as a person, project or organization.", + ), + ), + ], + options={ + "ordering": ["filename"], + "permissions": ( + ( + "change_usage_policy_on_package", + "Can change the usage_policy of package", + ), + ), + }, + bases=( + policy.models.SetPolicyFromLicenseMixin, + component_catalog.models.LicenseExpressionMixin, + models.Model, + ), + ), + migrations.CreateModel( + name="PackageAssignedLicense", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "uuid", + models.UUIDField( + default=uuid.uuid4, editable=False, verbose_name="UUID" + ), + ), + ], + options={ + "verbose_name": "package assigned license", + }, + ), + migrations.CreateModel( + name="Subcomponent", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "uuid", + models.UUIDField( + default=uuid.uuid4, editable=False, verbose_name="UUID" + ), + ), + ( + "created_date", + models.DateTimeField( + auto_now_add=True, + db_index=True, + help_text="The date and time the object was created.", + ), + ), + ( + "last_modified_date", + models.DateTimeField( + auto_now=True, + db_index=True, + help_text="The date and time the object was last modified.", + ), + ), + ( + "reference_notes", + models.TextField( + blank=True, + help_text="Reference Notes provide background details about the sofware and licenses in DejaCode, alerting you to pertinent ownership history or licensing complexities", + ), + ), + ( + "notes", + models.TextField( + blank=True, + help_text="Free form text about how the component is being used in this context, especially useful if the information is needed for the review process.", + ), + ), + ( + "is_deployed", + models.BooleanField( + default=True, + help_text="Indicates if the component is deployed in this context. Default = True.", + ), + ), + ( + "is_modified", + models.BooleanField( + default=False, + help_text="Indicates if the original third-party component has been modified. Default = False.", + ), + ), + ( + "extra_attribution_text", + models.TextField( + blank=True, + help_text="Additional text to be supplied with the component when attribution is generated. For example, you may want to explain a license choice when the component is available under a choice of licenses.", + ), + ), + ( + "package_paths", + models.TextField( + blank=True, + help_text="This field is deprecated in DejaCode. To define one or more specific location(s) of a Component in a Product, create a Product Codebase Resource for each location, specifying the codebase path and referencing the Product Component.", + ), + ), + ( + "license_expression", + models.CharField( + blank=True, + db_index=True, + help_text='On a subcomponent relationship (which defines a child component of another component), a license expression is limited by the license(s) assigned to the child component, and expresses the license(s) that apply to the context of the child component as it is used by the parent component. More than one applicable license can be expressed as "license-key-a AND license-key-b". A choice of licenses can be expressed as "license-key-a OR license-key-b", and you can indicate the primary license as defined by your business by placing it first, on the left-hand side of the OR relationship. The relationship words (OR, AND) can be combined as needed, and the use of parentheses can be applied to clarify the meaning; for example "((license-key-a AND license-key-b) OR (license-key-c))". An exception to a license can be expressed as “license-key WITH license-exception-key".', + max_length=1024, + verbose_name="License expression", + ), + ), + ( + "purpose", + models.CharField( + blank=True, + db_index=True, + help_text="Indicates how this component/package is used in this context. Suggested values are: Core, Test, Tool, Build, Reference, Requirement.", + max_length=50, + ), + ), + ], + options={ + "verbose_name": "subcomponent relationship", + "ordering": ["parent", "child"], + "permissions": ( + ( + "change_usage_policy_on_subcomponent", + "Can change the usage_policy of subcomponent relationship", + ), + ), + }, + bases=(component_catalog.models.LicenseExpressionMixin, models.Model), + ), + migrations.CreateModel( + name="SubcomponentAssignedLicense", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "uuid", + models.UUIDField( + default=uuid.uuid4, editable=False, verbose_name="UUID" + ), + ), + ], + options={ + "verbose_name": "subcomponent assigned license", + }, + ), + ] diff --git a/component_catalog/migrations/0002_initial.py b/component_catalog/migrations/0002_initial.py new file mode 100644 index 00000000..01777994 --- /dev/null +++ b/component_catalog/migrations/0002_initial.py @@ -0,0 +1,26 @@ +# Generated by Django 4.2.7 on 2023-11-27 21:12 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + initial = True + + dependencies = [ + ("component_catalog", "0001_initial"), + ("dje", "0001_initial"), + ] + + operations = [ + migrations.AddField( + model_name="subcomponentassignedlicense", + name="dataspace", + field=models.ForeignKey( + editable=False, + help_text="A Dataspace is an independent, exclusive set of DejaCode data, which can be either nexB master reference data or installation-specific data.", + on_delete=django.db.models.deletion.PROTECT, + to="dje.dataspace", + ), + ), + ] diff --git a/component_catalog/migrations/0003_initial.py b/component_catalog/migrations/0003_initial.py new file mode 100644 index 00000000..3268b00b --- /dev/null +++ b/component_catalog/migrations/0003_initial.py @@ -0,0 +1,98 @@ +# Generated by Django 4.2.7 on 2023-11-27 21:12 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import dje.fields + + +class Migration(migrations.Migration): + initial = True + + dependencies = [ + ("component_catalog", "0002_initial"), + ("dje", "0001_initial"), + ("license_library", "0001_initial"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AddField( + model_name="subcomponentassignedlicense", + name="license", + field=models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + to="license_library.license", + ), + ), + migrations.AddField( + model_name="subcomponentassignedlicense", + name="subcomponent", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="component_catalog.subcomponent", + ), + ), + migrations.AddField( + model_name="subcomponent", + name="child", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="related_parents", + to="component_catalog.component", + ), + ), + migrations.AddField( + model_name="subcomponent", + name="created_by", + field=models.ForeignKey( + editable=False, + help_text="The application user who created the object.", + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="created_%(class)ss", + serialize=False, + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AddField( + model_name="subcomponent", + name="dataspace", + field=models.ForeignKey( + editable=False, + help_text="A Dataspace is an independent, exclusive set of DejaCode data, which can be either nexB master reference data or installation-specific data.", + on_delete=django.db.models.deletion.PROTECT, + to="dje.dataspace", + ), + ), + migrations.AddField( + model_name="subcomponent", + name="last_modified_by", + field=dje.fields.LastModifiedByField( + editable=False, + help_text="The application user who last modified the object.", + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="modified_%(class)ss", + serialize=False, + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AddField( + model_name="subcomponent", + name="licenses", + field=models.ManyToManyField( + through="component_catalog.SubcomponentAssignedLicense", + to="license_library.license", + ), + ), + migrations.AddField( + model_name="subcomponent", + name="parent", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="related_children", + to="component_catalog.component", + ), + ), + ] diff --git a/component_catalog/migrations/0004_initial.py b/component_catalog/migrations/0004_initial.py new file mode 100644 index 00000000..235830de --- /dev/null +++ b/component_catalog/migrations/0004_initial.py @@ -0,0 +1,385 @@ +# Generated by Django 4.2.7 on 2023-11-27 21:12 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import dje.fields + + +class Migration(migrations.Migration): + initial = True + + dependencies = [ + ("policy", "0001_initial"), + ("license_library", "0001_initial"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ("component_catalog", "0003_initial"), + ("dje", "0001_initial"), + ("organization", "0001_initial"), + ] + + operations = [ + migrations.AddField( + model_name="subcomponent", + name="usage_policy", + field=models.ForeignKey( + blank=True, + help_text="An administrator can communicate company policy for an entry by setting the Usage Policy indicator.", + null=True, + on_delete=django.db.models.deletion.PROTECT, + to="policy.usagepolicy", + ), + ), + migrations.AddField( + model_name="packageassignedlicense", + name="dataspace", + field=models.ForeignKey( + editable=False, + help_text="A Dataspace is an independent, exclusive set of DejaCode data, which can be either nexB master reference data or installation-specific data.", + on_delete=django.db.models.deletion.PROTECT, + to="dje.dataspace", + ), + ), + migrations.AddField( + model_name="packageassignedlicense", + name="license", + field=models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + to="license_library.license", + ), + ), + migrations.AddField( + model_name="packageassignedlicense", + name="package", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="component_catalog.package", + ), + ), + migrations.AddField( + model_name="package", + name="created_by", + field=models.ForeignKey( + editable=False, + help_text="The application user who created the object.", + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="created_%(class)ss", + serialize=False, + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AddField( + model_name="package", + name="dataspace", + field=models.ForeignKey( + editable=False, + help_text="A Dataspace is an independent, exclusive set of DejaCode data, which can be either nexB master reference data or installation-specific data.", + on_delete=django.db.models.deletion.PROTECT, + to="dje.dataspace", + ), + ), + migrations.AddField( + model_name="package", + name="last_modified_by", + field=dje.fields.LastModifiedByField( + editable=False, + help_text="The application user who last modified the object.", + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="modified_%(class)ss", + serialize=False, + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AddField( + model_name="package", + name="licenses", + field=models.ManyToManyField( + through="component_catalog.PackageAssignedLicense", + to="license_library.license", + ), + ), + migrations.AddField( + model_name="package", + name="usage_policy", + field=models.ForeignKey( + blank=True, + help_text="An administrator can communicate company policy for an entry by setting the Usage Policy indicator.", + null=True, + on_delete=django.db.models.deletion.PROTECT, + to="policy.usagepolicy", + ), + ), + migrations.AddField( + model_name="componenttype", + name="dataspace", + field=models.ForeignKey( + editable=False, + help_text="A Dataspace is an independent, exclusive set of DejaCode data, which can be either nexB master reference data or installation-specific data.", + on_delete=django.db.models.deletion.PROTECT, + to="dje.dataspace", + ), + ), + migrations.AddField( + model_name="componentstatus", + name="dataspace", + field=models.ForeignKey( + editable=False, + help_text="A Dataspace is an independent, exclusive set of DejaCode data, which can be either nexB master reference data or installation-specific data.", + on_delete=django.db.models.deletion.PROTECT, + to="dje.dataspace", + ), + ), + migrations.AddField( + model_name="componentkeyword", + name="dataspace", + field=models.ForeignKey( + editable=False, + help_text="A Dataspace is an independent, exclusive set of DejaCode data, which can be either nexB master reference data or installation-specific data.", + on_delete=django.db.models.deletion.PROTECT, + to="dje.dataspace", + ), + ), + migrations.AddField( + model_name="componentassignedpackage", + name="component", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="component_catalog.component", + ), + ), + migrations.AddField( + model_name="componentassignedpackage", + name="dataspace", + field=models.ForeignKey( + editable=False, + help_text="A Dataspace is an independent, exclusive set of DejaCode data, which can be either nexB master reference data or installation-specific data.", + on_delete=django.db.models.deletion.PROTECT, + to="dje.dataspace", + ), + ), + migrations.AddField( + model_name="componentassignedpackage", + name="package", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="component_catalog.package", + ), + ), + migrations.AddField( + model_name="componentassignedlicense", + name="component", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="component_catalog.component", + ), + ), + migrations.AddField( + model_name="componentassignedlicense", + name="dataspace", + field=models.ForeignKey( + editable=False, + help_text="A Dataspace is an independent, exclusive set of DejaCode data, which can be either nexB master reference data or installation-specific data.", + on_delete=django.db.models.deletion.PROTECT, + to="dje.dataspace", + ), + ), + migrations.AddField( + model_name="componentassignedlicense", + name="license", + field=models.ForeignKey( + help_text="Select from list of licenses.", + on_delete=django.db.models.deletion.PROTECT, + to="license_library.license", + ), + ), + migrations.AddField( + model_name="component", + name="children", + field=models.ManyToManyField( + through="component_catalog.Subcomponent", + to="component_catalog.component", + ), + ), + migrations.AddField( + model_name="component", + name="configuration_status", + field=models.ForeignKey( + blank=True, + help_text="The configuration status can be used to communicate the current stage of the review process and whether additional review is required.", + null=True, + on_delete=django.db.models.deletion.PROTECT, + to="component_catalog.componentstatus", + ), + ), + migrations.AddField( + model_name="component", + name="created_by", + field=models.ForeignKey( + editable=False, + help_text="The application user who created the object.", + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="created_%(class)ss", + serialize=False, + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AddField( + model_name="component", + name="dataspace", + field=models.ForeignKey( + editable=False, + help_text="A Dataspace is an independent, exclusive set of DejaCode data, which can be either nexB master reference data or installation-specific data.", + on_delete=django.db.models.deletion.PROTECT, + to="dje.dataspace", + ), + ), + migrations.AddField( + model_name="component", + name="last_modified_by", + field=dje.fields.LastModifiedByField( + editable=False, + help_text="The application user who last modified the object.", + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="modified_%(class)ss", + serialize=False, + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AddField( + model_name="component", + name="licenses", + field=models.ManyToManyField( + help_text='The license that applies to a component. There could be more than one license, in which case a choice is usually required. The expression "(default)" next to the license name indicates that this license applies to the component even if you do not assert a particular license.', + through="component_catalog.ComponentAssignedLicense", + to="license_library.license", + ), + ), + migrations.AddField( + model_name="component", + name="owner", + field=models.ForeignKey( + blank=True, + help_text="Owner is an optional field selected by the user to identify the original creator (copyright holder) of the component. If this component is in its original, unmodified state, the component owner is associated with the original author/publisher. If this component has been copied and modified, the component owner should be the owner that has copied and modified it.", + null=True, + on_delete=django.db.models.deletion.PROTECT, + to="organization.owner", + ), + ), + migrations.AddField( + model_name="component", + name="packages", + field=models.ManyToManyField( + through="component_catalog.ComponentAssignedPackage", + to="component_catalog.package", + ), + ), + migrations.AddField( + model_name="component", + name="type", + field=models.ForeignKey( + blank=True, + help_text="A component type provides a label to filter and sort components.", + null=True, + on_delete=django.db.models.deletion.PROTECT, + to="component_catalog.componenttype", + ), + ), + migrations.AddField( + model_name="component", + name="usage_policy", + field=models.ForeignKey( + blank=True, + help_text="An administrator can communicate company policy for an entry by setting the Usage Policy indicator.", + null=True, + on_delete=django.db.models.deletion.PROTECT, + to="policy.usagepolicy", + ), + ), + migrations.AddField( + model_name="acceptablelinkage", + name="dataspace", + field=models.ForeignKey( + editable=False, + help_text="A Dataspace is an independent, exclusive set of DejaCode data, which can be either nexB master reference data or installation-specific data.", + on_delete=django.db.models.deletion.PROTECT, + to="dje.dataspace", + ), + ), + migrations.AlterUniqueTogether( + name="subcomponentassignedlicense", + unique_together={("subcomponent", "license"), ("dataspace", "uuid")}, + ), + migrations.AlterUniqueTogether( + name="subcomponent", + unique_together={("parent", "child"), ("dataspace", "uuid")}, + ), + migrations.AlterUniqueTogether( + name="packageassignedlicense", + unique_together={("package", "license"), ("dataspace", "uuid")}, + ), + migrations.AddIndex( + model_name="package", + index=models.Index(fields=["md5"], name="component_c_md5_33e2c1_idx"), + ), + migrations.AddIndex( + model_name="package", + index=models.Index(fields=["sha1"], name="component_c_sha1_5b9041_idx"), + ), + migrations.AddIndex( + model_name="package", + index=models.Index(fields=["sha256"], name="component_c_sha256_dba399_idx"), + ), + migrations.AddIndex( + model_name="package", + index=models.Index(fields=["sha512"], name="component_c_sha512_a0eb10_idx"), + ), + migrations.AlterUniqueTogether( + name="package", + unique_together={ + ("dataspace", "uuid"), + ( + "dataspace", + "type", + "namespace", + "name", + "version", + "qualifiers", + "subpath", + "download_url", + "filename", + ), + }, + ), + migrations.AlterUniqueTogether( + name="componenttype", + unique_together={("dataspace", "label"), ("dataspace", "uuid")}, + ), + migrations.AlterUniqueTogether( + name="componentstatus", + unique_together={("dataspace", "label"), ("dataspace", "uuid")}, + ), + migrations.AlterUniqueTogether( + name="componentkeyword", + unique_together={("dataspace", "label"), ("dataspace", "uuid")}, + ), + migrations.AlterUniqueTogether( + name="componentassignedpackage", + unique_together={("component", "package"), ("dataspace", "uuid")}, + ), + migrations.AlterUniqueTogether( + name="componentassignedlicense", + unique_together={("component", "license"), ("dataspace", "uuid")}, + ), + migrations.AlterUniqueTogether( + name="component", + unique_together={("dataspace", "name", "version"), ("dataspace", "uuid")}, + ), + migrations.AlterUniqueTogether( + name="acceptablelinkage", + unique_together={("dataspace", "label"), ("dataspace", "uuid")}, + ), + ] diff --git a/component_catalog/migrations/__init__.py b/component_catalog/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/component_catalog/models.py b/component_catalog/models.py new file mode 100644 index 00000000..9ae9e405 --- /dev/null +++ b/component_catalog/models.py @@ -0,0 +1,2352 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# DejaCode is a trademark of nexB Inc. +# SPDX-License-Identifier: AGPL-3.0-only +# See https://github.com/nexB/dejacode for support or download. +# See https://aboutcode.org for more information about AboutCode FOSS projects. +# + +import logging +import re +from contextlib import suppress +from urllib.parse import quote_plus + +from django.contrib.postgres.fields import ArrayField +from django.core import validators +from django.core.exceptions import MultipleObjectsReturned +from django.core.exceptions import ObjectDoesNotExist +from django.core.exceptions import ValidationError +from django.core.validators import EMPTY_VALUES +from django.db import models +from django.db.models import CharField +from django.db.models import Exists +from django.db.models import OuterRef +from django.db.models.functions import Concat +from django.dispatch import receiver +from django.template.defaultfilters import filesizeformat +from django.utils.functional import cached_property +from django.utils.html import format_html +from django.utils.text import format_lazy +from django.utils.text import get_valid_filename +from django.utils.text import normalize_newlines +from django.utils.translation import gettext_lazy as _ + +from attributecode.model import About +from cyclonedx import model as cyclonedx_model +from cyclonedx.model import component as cyclonedx_component +from packageurl import PackageURL +from packageurl.contrib import purl2url +from packageurl.contrib import url2purl +from packageurl.contrib.django.models import PackageURLMixin +from packageurl.contrib.django.models import PackageURLQuerySetMixin +from packageurl.contrib.django.utils import without_empty_values + +from component_catalog.license_expression_dje import build_licensing +from component_catalog.license_expression_dje import get_license_objects +from component_catalog.license_expression_dje import parse_expression +from dejacode_toolkit import spdx +from dejacode_toolkit.download import DataCollectionException +from dejacode_toolkit.download import collect_package_data +from dejacode_toolkit.purldb import PurlDB +from dje import urn +from dje.copier import post_copy +from dje.copier import post_update +from dje.fields import JSONListField +from dje.fields import NoStripTextField +from dje.models import DataspacedManager +from dje.models import DataspacedModel +from dje.models import DataspacedQuerySet +from dje.models import ExternalReferenceMixin +from dje.models import History +from dje.models import HistoryFieldsMixin +from dje.models import ParentChildModelMixin +from dje.models import ParentChildRelationshipModel +from dje.models import ReferenceNotesMixin +from dje.tasks import tasks_logger +from dje.utils import set_fields_from_object +from dje.validators import generic_uri_validator +from dje.validators import validate_url_segment +from dje.validators import validate_version +from license_library.models import License +from license_library.models import LicenseChoice +from policy.models import SetPolicyFromLicenseMixin +from policy.models import UsagePolicyMixin +from workflow.models import RequestMixin + +logger = logging.getLogger("dje") + + +COMPONENT_PACKAGE_COMMON_FIELDS = [ + "copyright", + "dependencies", + "description", + "holder", + "homepage_url", + "license_expression", + "name", + "notice_text", + "primary_language", + "release_date", + "version", +] + + +def validate_filename(value): + invalid_chars = ["/", "\\", ":"] + if any(char in value for char in invalid_chars): + raise ValidationError( + _("Enter a valid filename: slash, backslash, or colon are not allowed.") + ) + + +class LicenseExpressionMixin: + """Model mixin for models that store license expressions.""" + + def _get_licensing(self): + """Return a Licensing object built from the assigned licenses.""" + # WARNING: Do not apply select/prefect_related here but on the main QuerySet instead + # For example: prefetch_related('component_set__licenses__dataspace') + return build_licensing(self.licenses.all()) + + licensing = cached_property(_get_licensing) + + def _get_normalized_expression(self): + """ + Return this object ``license_expression`` field value as a normalized parsed + expression object. + """ + if self.license_expression: + return parse_expression( + self.license_expression, + licenses=self.licensing, + validate_known=False, + validate_strict=False, + ) + + normalized_expression = cached_property(_get_normalized_expression) + + def get_license_expression(self, template="{symbol.key}", as_link=False, show_policy=False): + """ + Validate and Return the license_expression value set on this instance. + The license expression is NOT validated for known symbols. + Use the `template` format string to render each license in the expression. + if `as_link` is True, render the expression as a link. + """ + if self.license_expression: + rendered = self.normalized_expression.render_as_readable( + template, + as_link=as_link, + show_policy=show_policy, + ) + return format_html(rendered) + + def get_license_expression_attribution(self): + # note: the fields use in the template must be available as attributes or + # properties on a License. + template = '{symbol.short_name}' + return self.get_license_expression(template) + + license_expression_attribution = cached_property(get_license_expression_attribution) + + def get_license_expression_linked(self): + return self.get_license_expression(as_link=True) + + license_expression_linked = cached_property(get_license_expression_linked) + + def get_license_expression_linked_with_policy(self): + license_expression = self.get_license_expression(as_link=True, show_policy=True) + if license_expression: + return format_html('{}', license_expression) + + def get_license_expression_spdx_id(self): + """ + Return the license_expression formatted for SPDX compatibility. + + This includes a workaround for a SPDX spec limitation, where license exceptions + that do not exist in the SPDX list cannot be provided as "LicenseRef-" in the + "hasExtractedLicensingInfos". + The current fix is to use AND rather than WITH for any exception that is a + "LicenseRef-". + + See discussion at https://github.com/spdx/tools-java/issues/73 + """ + expression = self.get_license_expression("{symbol.spdx_id}") + if expression: + return expression.replace("WITH LicenseRef-", "AND LicenseRef-") + + def _get_primary_license(self): + """ + Return the primary license key of this instance or None. The primary license is + the left most license of the expression. It can be the combination of a + license WITH an exception and therefore may contain more than one key. + + WARNING: This does not support exception as primary_license. + """ + if self.license_expression: + licensing = build_licensing() + return licensing.primary_license_key(self.license_expression) + + primary_license = cached_property(_get_primary_license) + + def save(self, *args, **kwargs): + """ + Call the handle_assigned_licenses method on save, except during copy. + During copy, as some Licenses referenced by the license_expression may not exists in the + target Dataspace yet, the handle_assigned_licenses() would not be able to create the + proper assignments and the UUID of those assignments would not be shared with + reference Dataspace. + Thus, the handle_assigned_licenses() is skipped during the copy process and the + License assignments are handled by the m2m copy. + """ + super().save(*args, **kwargs) + self.handle_assigned_licenses(copy=kwargs.get("copy")) + + def handle_assigned_licenses(self, copy=False): + """ + Create missing AssignedLicense instances and deletes the ones non-referenced + in the license_expression. + + In `copy` mode, all the license assignments are deleted to avoid any conflicts + during the copy/update process where all the assignments are properly created. + """ + licenses_field = self._meta.get_field("licenses") + AssignedLicense = licenses_field.remote_field.through + + # Looking for the FK field name, on the AssignedLicense, that points to this Model + fk_field_name = [ + field + for field in AssignedLicense._meta.get_fields() + if field.many_to_one and field.concrete and field.related_model == self.__class__ + ] + + if len(fk_field_name) != 1: + return + fk_field_name = fk_field_name[0].name + + assigned_license_qs = AssignedLicense.objects.filter( + **{"dataspace": self.dataspace, fk_field_name: self} + ) + + if copy: + # Deletes all existing license assignments to ensure UUID integrity + # as the licenses will be properly assigned during the copy/update process + assigned_license_qs.delete() + return + + # Get the full list of licenses is required here for proper + # validation. We cannot rely on the assigned licenses since we + # are modifying those assignments. + all_licenses = License.objects.scope(self.dataspace).for_expression() + licenses = get_license_objects(self.license_expression, all_licenses) + + for license_instance in licenses: + AssignedLicense.objects.get_or_create( + **{ + "dataspace": self.dataspace, + fk_field_name: self, + "license": license_instance, + } + ) + + assigned_license_qs.exclude(license__in=licenses).delete() + + @cached_property + def license_choices_expression(self): + """Return the license choices as an expression.""" + return LicenseChoice.objects.get_choices_expression(self.license_expression, self.dataspace) + + @cached_property + def has_license_choices(self): + """Return `True` if applying the LicenseChoice results in a new expression.""" + return self.license_expression != self.license_choices_expression + + @property + def attribution_required(self): + return any(license.attribution_required for license in self.licenses.all()) + + @property + def redistribution_required(self): + return any(license.redistribution_required for license in self.licenses.all()) + + @property + def change_tracking_required(self): + return any(license.change_tracking_required for license in self.licenses.all()) + + @cached_property + def compliance_alerts(self): + """ + Return the list of all existing `compliance_alert` through this license + `usage_policy`. + """ + return [ + license.usage_policy.compliance_alert + for license in self.licenses.all() + if license.usage_policy_id and license.usage_policy.compliance_alert + ] + + def compliance_table_class(self): + """Return a CSS class for a table row based on the licenses `compliance_alerts`.""" + if "error" in self.compliance_alerts: + return "table-danger" + elif "warning" in self.compliance_alerts: + return "table-warning" + + +def get_cyclonedx_properties(instance): + """ + Return fields not supported natively by CycloneDX as properties. + Those fields are required to load the BOM without major data loss. + See https://github.com/nexB/aboutcode-cyclonedx-taxonomy + """ + property_prefix = "aboutcode" + property_fields = [ + "filename", # package-only + "download_url", # package-only + "primary_language", + "homepage_url", + "notice_text", + ] + properties = [ + cyclonedx_model.Property(name=f"{property_prefix}:{field_name}", value=value) + for field_name in property_fields + if (value := getattr(instance, field_name, None)) not in EMPTY_VALUES + ] + return properties + + +class HolderMixin(models.Model): + """Add the `holder` field.""" + + holder = models.TextField( + blank=True, + help_text=_( + "The name(s) of the copyright holder(s) of a software package as documented in the " + "code. This field is intended to record the copyright holder independently of " + "copyright statement dates and formats, and generally corresponds to the owner of " + "the associated software project." + ), + ) + + class Meta: + abstract = True + + +class KeywordsMixin(models.Model): + """Add the `keywords` field.""" + + keywords = JSONListField( + blank=True, + help_text=_( + "A keyword is a category or label that helps you to find items " + "for particular requirements." + ), + ) + + class Meta: + abstract = True + + +class CPEMixin(models.Model): + """Add the `cpe` field.""" + + cpe = models.CharField( + _("CPE"), + blank=True, + max_length=1024, + help_text=_( + "Common Platform Enumeration (CPE) is a standardized method of describing and " + "identifying a computing asset. CPE does not necessarily identify a unique instance " + "or version of a computing asset. For example, a CPE could identify a component name " + "with a version range." + ), + ) + + class Meta: + abstract = True + + def get_spdx_cpe_external_ref(self): + if self.cpe: + return spdx.ExternalRef( + category="SECURITY", + type="cpe23Type", + locator=self.cpe, + ) + + +class URLFieldsMixin(models.Model): + homepage_url = models.URLField( + _("Homepage URL"), + max_length=1024, + blank=True, + help_text=_("Homepage URL."), + ) + + # The URLField validation is too strict to support values like git:// + vcs_url = models.CharField( + _("VCS URL"), + max_length=1024, + validators=[generic_uri_validator], + blank=True, + help_text=_("URL to the Version Control System (VCS)."), + ) + + code_view_url = models.URLField( + _("Code view URL"), + max_length=1024, + blank=True, + help_text=_("A URL that allows you to browse and view the source code online."), + ) + + bug_tracking_url = models.URLField( + _("Bug tracking URL"), + max_length=1024, + blank=True, + help_text=_("A URL to the bug reporting system."), + ) + + class Meta: + abstract = True + + +class HashFieldsMixin(models.Model): + """ + The hash fields are not indexed by default, use the `indexes` in Meta as needed: + + class Meta: + indexes = [ + models.Index(fields=['md5']), + models.Index(fields=['sha1']), + models.Index(fields=['sha256']), + models.Index(fields=['sha512']), + ] + """ + + md5 = models.CharField( + _("MD5"), + max_length=32, + blank=True, + help_text=_("MD5 checksum hex-encoded, as in md5sum."), + ) + sha1 = models.CharField( + _("SHA1"), + max_length=40, + blank=True, + help_text=_("SHA1 checksum hex-encoded, as in sha1sum."), + ) + sha256 = models.CharField( + _("SHA256"), + max_length=64, + blank=True, + help_text=_("SHA256 checksum hex-encoded, as in sha256sum."), + ) + sha512 = models.CharField( + _("SHA512"), + max_length=128, + blank=True, + help_text=_("SHA512 checksum hex-encoded, as in sha512sum."), + ) + + class Meta: + abstract = True + + +class ComponentType(DataspacedModel): + label = models.CharField( + max_length=50, + help_text=_( + "Label that indicates the scope, function, and complexity of a component. " + "Every dataspace has its own list of component types. Examples include: " + "product, package, project, assembly, module, platform, directory, file, snippet." + ), + ) + + notes = models.TextField( + blank=True, + help_text=_("Optional descriptive text."), + ) + + class Meta: + unique_together = (("dataspace", "label"), ("dataspace", "uuid")) + ordering = ["label"] + + def __str__(self): + return self.label + + +CONFIGURATION_STATUS_HELP = _( + "The configuration status can be used to communicate the current stage of the review process " + "and whether additional review is required." +) + + +class DefaultOnAdditionManager(DataspacedManager): + def get_default_on_addition_qs(self, dataspace): + """ + Return the QuerySet with default_on_addition=True scoped to the given `dataspace`. + The QS count should be 0 or 1 max. + """ + return self.scope(dataspace).filter(default_on_addition=True) + + +class DefaultOnAdditionFieldMixin(models.Model): + default_on_addition = models.BooleanField( + _("Default on addition"), + default=False, + help_text=_( + "Indicates this instance is automatically assigned by the " + "application to an object when it is initially created." + ), + ) + + objects = DefaultOnAdditionManager() + + class Meta: + abstract = True + + def save(self, *args, **kwargs): + """ + Make sure only one default_on_addition is set to True per Dataspace by forcing False + for default_on_addition on any other instance. + Note that this cannot be done in clean() as the dataspace will not have + been set on the instance yet. + """ + if self.default_on_addition: + qs = self.__class__.objects.get_default_on_addition_qs(self.dataspace) + qs.update(default_on_addition=False) + super().save(*args, **kwargs) + + +class BaseStatusMixin(DefaultOnAdditionFieldMixin, models.Model): + label = models.CharField( + max_length=50, + help_text=_("Concise name to identify the status."), + ) + + text = models.TextField( + help_text=_("Descriptive text to define the status purpose precisely."), + ) + + class Meta: + abstract = True + unique_together = (("dataspace", "label"), ("dataspace", "uuid")) + ordering = ["label"] + + def __str__(self): + return self.label + + +class DefaultOnAdditionMixin: + def save(self, *args, **kwargs): + """ + Set the default Status on ADDITION, if a Status instance was set as default. + Note that this cannot be done in clean() as the dataspace will not be + set on the instance yet. + """ + is_addition = not self.pk + + if is_addition: + default_on_addition_fields = [ + field + for field in self._meta.get_fields() + if field.is_relation + and issubclass(field.related_model, DefaultOnAdditionFieldMixin) + ] + + for on_addition_field in default_on_addition_fields: + if not getattr(self, on_addition_field.name): + related_model = on_addition_field.related_model + default = related_model.objects.get_default_on_addition_qs( + self.dataspace + ).first() + setattr(self, on_addition_field.name, default) + + super().save(*args, **kwargs) + + +class ComponentStatus(BaseStatusMixin, DataspacedModel): + class Meta(BaseStatusMixin.Meta): + verbose_name_plural = _("component status") + + +def component_mixin_factory(verbose_name): + """ + Return a BaseComponentMixin class suitable for Component and Product. + This factory logic is required to inject a variable verbose name in the help_text. + """ + + class BaseComponentMixin( + DefaultOnAdditionMixin, + LicenseExpressionMixin, + URLFieldsMixin, + RequestMixin, + HistoryFieldsMixin, + models.Model, + ): + """Component and Product common Model fields.""" + + name = models.CharField( + db_index=True, + max_length=100, + help_text=format_lazy( + "Name by which the {verbose_name} is commonly referenced.", + verbose_name=_(verbose_name), + ), + validators=[validate_url_segment], + ) + + version = models.CharField( + db_index=True, + max_length=100, + blank=True, + help_text=format_lazy( + "Identifies a specific version of a {verbose_name}. The combination of " + "name + version uniquely identifies a {verbose_name}. If the version is " + "(nv) or blank, it signifies an unstated/unknown version (or it indicates " + "that version does not apply to the {verbose_name}), and it does not imply " + "that the information in this {verbose_name} definition applies to any " + "or all possible versions of the {verbose_name}.", + verbose_name=_(verbose_name), + ), + validators=[validate_version], + ) + + owner = models.ForeignKey( + to="organization.Owner", + null=True, + blank=True, + on_delete=models.PROTECT, + help_text=format_lazy( + "Owner is an optional field selected by the user to identify the original " + "creator (copyright holder) of the {verbose_name}. " + "If this {verbose_name} is in its original, unmodified state, the {verbose_name}" + " owner is associated with the original author/publisher. " + "If this {verbose_name} has been copied and modified, " + "the {verbose_name} owner should be the owner that has copied and " + "modified it.", + verbose_name=_(verbose_name), + ), + ) + + release_date = models.DateField( + null=True, + blank=True, + help_text=format_lazy( + "The date that the {verbose_name} was released by its owner.", + verbose_name=_(verbose_name), + ), + ) + + description = models.TextField( + blank=True, + help_text=_("Free form description, preferably as provided by the author(s)."), + ) + + copyright = models.TextField( + blank=True, + help_text=format_lazy( + "The copyright statement(s) that pertain to this {verbose_name}, as " + "contained in the source or as specified in an associated file.", + verbose_name=_(verbose_name), + ), + ) + + homepage_url = models.URLField( + _("Homepage URL"), + max_length=1024, + blank=True, + help_text=format_lazy( + "Homepage URL for the {verbose_name}.", + verbose_name=_(verbose_name), + ), + ) + + primary_language = models.CharField( + db_index=True, + max_length=50, + blank=True, + help_text=format_lazy( + "The primary programming language associated with the {verbose_name}.", + verbose_name=_(verbose_name), + ), + ) + + admin_notes = models.TextField( + blank=True, + help_text=format_lazy( + "Comments about the {verbose_name}, provided by administrators, " + "intended for viewing and maintenance by administrators only.", + verbose_name=_(verbose_name), + ), + ) + + notice_text = NoStripTextField( + blank=True, + help_text=format_lazy( + "The notice text provided by the authors of a {verbose_name} to identify " + "the copyright statement(s), contributors, and/or license obligations that apply" + " to a {verbose_name}.", + verbose_name=_(verbose_name), + ), + ) + + license_expression = models.CharField( + _("License expression"), + max_length=1024, + blank=True, + db_index=True, + help_text=_( + "On a component or a product in DejaCode, a license expression defines the " + "relationship of one or more licenses to that software as declared by its " + 'licensor. More than one applicable license can be expressed as "license-key-a ' + 'AND license-key-b". A choice of applicable licenses can be expressed as ' + '"license-key-a OR license-key-b", and you can indicate the primary (preferred) ' + "license by placing it first, on the left-hand side of the OR relationship. " + "The relationship words (OR, AND) can be combined as needed, and the use of " + 'parentheses can be applied to clarify the meaning; for example "((license-key-a ' + 'AND license-key-b) OR (license-key-c))". An exception to a license can be ' + 'expressed as “license-key WITH license-exception-key".' + ), + ) + + class Meta: + abstract = True + unique_together = (("dataspace", "name", "version"), ("dataspace", "uuid")) + ordering = ("name", "version") + + def __str__(self): + if self.version: + return f"{self.name} {self.version}" + return self.name + + def get_url(self, name, params=None): + if not params: + params = [self.dataspace.name, quote_plus(self.name)] + if self.version: + params.append(quote_plus(self.version)) + return super().get_url(name, params) + + def get_absolute_url(self): + return self.get_url("details") + + def get_change_url(self): + return self.get_url("change") + + def get_delete_url(self): + return self.get_url("delete") + + def get_about_files_url(self): + return self.get_url("about_files") + + def get_export_spdx_url(self): + return self.get_url("export_spdx") + + def get_export_cyclonedx_url(self): + return self.get_url("export_cyclonedx") + + def get_about_files(self): + """ + Return the list of all AboutCode files from all the Packages + related to this instance. + """ + return [ + about_file + for package in self.all_packages + for about_file in package.get_about_files() + ] + + def as_cyclonedx(self, license_expression_spdx=None): + """Return this Component/Product as an CycloneDX Component entry.""" + supplier = None + if self.owner: + supplier = cyclonedx_model.OrganizationalEntity( + name=self.owner.name, + urls=[self.owner.homepage_url], + ) + + expression_spdx = license_expression_spdx or self.get_license_expression_spdx_id() + licenses = [] + if expression_spdx: + licenses = [ + cyclonedx_model.LicenseChoice(license_expression=expression_spdx), + ] + + if self.__class__.__name__ == "Product": + component_type = cyclonedx_component.ComponentType.APPLICATION + else: + component_type = cyclonedx_component.ComponentType.LIBRARY + + return cyclonedx_component.Component( + name=self.name, + component_type=component_type, + version=self.version, + bom_ref=str(self.uuid), + supplier=supplier, + licenses=licenses, + copyright_=self.copyright, + description=self.description, + cpe=getattr(self, "cpe", None), + properties=get_cyclonedx_properties(self), + ) + + return BaseComponentMixin + + +BaseComponentMixin = component_mixin_factory("component") + + +class ComponentQuerySet(DataspacedQuerySet): + def with_has_hierarchy(self): + subcomponents = Subcomponent.objects.filter( + models.Q(child_id=OuterRef("pk")) | models.Q(parent_id=OuterRef("pk")) + ) + return self.annotate(has_hierarchy=Exists(subcomponents)) + + +PROJECT_FIELD_HELP = _( + "Project is a free-form label that you can use to group and find packages and components " + "that interest you; for example, you may be starting a new development project, " + "evaluating them for use in a product or you may want to get approval to use them." +) + + +class Component( + ReferenceNotesMixin, + UsagePolicyMixin, + SetPolicyFromLicenseMixin, + ExternalReferenceMixin, + HolderMixin, + KeywordsMixin, + CPEMixin, + ParentChildModelMixin, + BaseComponentMixin, + DataspacedModel, +): + configuration_status = models.ForeignKey( + to="component_catalog.ComponentStatus", + on_delete=models.PROTECT, + null=True, + blank=True, + help_text=CONFIGURATION_STATUS_HELP, + ) + + type = models.ForeignKey( + to="component_catalog.ComponentType", + on_delete=models.PROTECT, + null=True, + blank=True, + help_text=_("A component type provides a label to filter and sort components."), + ) + + approval_reference = models.CharField( + max_length=200, + blank=True, + help_text=_( + "The name or number of a document (e.g. approval document, contract, etc.) " + "that indicates that the component is approved for use in your organization." + ), + ) + + guidance = models.TextField( + verbose_name=_("Component usage guidance"), + blank=True, + help_text=_( + "Component usage guidance is provided by your organization to specify " + "recommendations, requirements and restrictions regarding your usage of this " + "component.", + ), + ) + + is_active = models.BooleanField( + verbose_name=_("Is active"), + null=True, + db_index=True, + default=True, + help_text=_( + "When set to True (Yes), this field indicates that a component definition in the " + "catalog is currently in use (active). When set to False (No), this field indicates " + "that a component is deprecated (inactive) and should not be used, and the component " + "will not appear in the user views. When the field value is Unknown, the component " + "will not appear in the user views, usually suggesting that the component has not " + "yet been evaluated." + ), + ) + + curation_level = models.PositiveSmallIntegerField( + db_index=True, + default=0, + validators=[validators.MaxValueValidator(100)], + help_text=_( + "A numeric value, from 0 to 100, that indicates the level of completeness of " + "all the pertinent component data, as well as the state of that data being " + 'reviewed by a senior administrator. General guidelines: "10" indicates basic ' + 'data present. "20" indicates copyright and notice data are provided. ' + 'assigned. "30" indicates all license data are provided. "40" indicates all ' + "available technical details (including URLs and primary language) are provided. " + '"50" indicates that relevant parent and child are provided. ' + "Any other values are at the discretion of a senior administrative reviewer." + ), + ) + + COMPONENT_FIELDS_WEIGHT = ( + ("notice_text", 10), + ("copyright", 5), + ("description", 5), + ("packages", 5), + ("homepage_url", 5), + ("keywords", 5), + ("licenses", 5), + ("notice_filename", 5), + ("notice_url", 5), + ("bug_tracking_url", 3), + ("code_view_url", 3), + ("primary_language", 3), + ("release_date", 3), + ("vcs_url", 3), + ("owner", 2), + ("type", 2), + ("version", 2), + ) + + completion_level = models.PositiveSmallIntegerField( + default=0, + db_index=True, + help_text=_( + "Completion level is a number automatically calculated by the application to " + "indicate the completeness of the data for a specific component. Fields that " + "influence the calculation include: Notice Text, Copyright, Description, Package, " + "Homepage URL, Keyword, License, Notice Filename, Notice URL, Bug Tracking URL, " + "Code View URL, Primary Language, Release Date, VCS URL, Owner, Type, and Version." + ), + ) + + is_license_notice = models.BooleanField( + null=True, + help_text=_( + "Indicator (Yes, No, Unknown) regarding whether the notice text contains a " + "statement about the licenses that apply to the component as a whole." + ), + ) + + is_copyright_notice = models.BooleanField( + null=True, + help_text=_( + "Indicator (Yes, No, Unknown) regarding whether the notice text contains one or " + "more copyright statements that apply to the component as a whole." + ), + ) + + is_notice_in_codebase = models.BooleanField( + null=True, + help_text=_( + "Indicator (Yes, No, Unknown) regarding whether a notice is internal to a " + "component (for example, if the notice text is in the source file header of " + "the component)." + ), + ) + + notice_filename = models.CharField( + max_length=255, + blank=True, + help_text=_( + "Optional filename to identify a notice file associated with a component. If a " + "filename is not provided, the application will assume that this notice is " + "internal to this component (for example, in a source file header)." + ), + ) + + notice_url = models.URLField( + _("Notice URL"), + max_length=1024, + blank=True, + help_text=_("A URL that contains notice text for a component."), + ) + + dependencies = models.JSONField( + blank=True, + default=list, + help_text=_( + "Identifies one or more potential dependencies required to deploy a component in " + "a particular context, with an emphasis on dependencies that may have an impact " + "on licensing and/or attribution obligations." + ), + ) + + project = models.CharField( + max_length=50, + db_index=True, + blank=True, + help_text=PROJECT_FIELD_HELP, + ) + + codescan_identifier = models.URLField( + max_length=1024, + blank=True, + help_text=_("A component identifier from a code scanning application."), + ) + + website_terms_of_use = models.TextField( + blank=True, + help_text=_( + "The Terms of Use or Terms of Service specified on a software project website. " + "These are terms that apply in addition to or in absence of an asserted license " + "for a component package." + ), + ) + + ip_sensitivity_approved = models.BooleanField( + verbose_name=_("IP sensitivity approved"), + default=False, + help_text=_( + "The component software can be combined with sensitive or critical IP, as determined " + "by legal review. This information will be used for product architecture review." + ), + ) + + affiliate_obligations = models.BooleanField( + default=False, + help_text=_( + "The component license contains terms that would impose obligations on a legal entity " + "affiliate." + ), + ) + + affiliate_obligation_triggers = models.TextField( + blank=True, + help_text=_( + "Explanation of how affiliate obligations are triggered, and what is the mitigation " + "strategy." + ), + ) + + concluded_license = models.CharField( + max_length=1024, + blank=True, + db_index=True, + help_text=_( + "This is a memo field to record the conclusions of the legal team after full review " + "and scanning of the component package, and is only intended to document that " + "decision. The main value of the field is to clarify the company interpretation of " + "the license that should apply to a component when there is a choice, or when there " + "is ambiguity in the original component documentation." + ), + ) + + legal_comments = models.TextField( + blank=True, + help_text=_( + "Notes to be entered and shared among legal team members during the legal review " + "process." + ), + ) + + sublicense_allowed = models.BooleanField( + null=True, + help_text=_( + "The component license grants some or all of the rights acquired under the original " + "license, and allows usage of the licensed code to be licensed under an overriding " + "license, although obligations such as attribution generally still apply. " + "This allowance is typical of permissive licenses, but is often not allowed in " + "copyleft and proprietary licenses. The right to sublicense is explicit in some " + "license texts (such as the MIT License) but is not always stated; implicit " + "permission to sublicense is a legal interpretation." + ), + ) + + express_patent_grant = models.BooleanField( + null=True, + help_text=_( + "The license that applies to this component expressly grants a patent license." + ), + ) + + covenant_not_to_assert = models.BooleanField( + null=True, + help_text=_( + "The license that applies to this component has language that we agree not to assert " + "our patents against users of a project under this license." + ), + ) + + indemnification = models.BooleanField( + null=True, + help_text=_( + "The license that applies to this component has one or more scenarios that " + "require indemnification." + ), + ) + + legal_reviewed = models.BooleanField( + default=False, + help_text=_("This component definition has been reviewed by the organization legal team."), + ) + + DISTRIBUTION_FORMATS_CHOICES = ( + ("Binary", "Binary"), + ("Source", "Source"), + ("All - Binary or Source", "All - Binary or Source"), + ) + + distribution_formats_allowed = models.CharField( + blank=True, + max_length=30, + default="", + choices=DISTRIBUTION_FORMATS_CHOICES, + help_text=_("The software distribution formats allowed by the component license."), + ) + + acceptable_linkages = ArrayField( + models.CharField( + max_length=40, + ), + blank=True, + null=True, + help_text=_( + "Your organization's legal review team can identify one or more " + "specific linkages (software interactions) that are acceptable " + "between this component and your organization's products in order " + "to comply with your organization's license compliance standards." + ), + ) + + export_restrictions = models.TextField( + blank=True, + help_text=_( + "The export restrictions and/or requirements associated with a component as " + "determined by legal review." + ), + ) + + approved_download_location = models.URLField( + max_length=1024, + blank=True, + help_text=_( + "The link to a pristine (unmodified) component package download as specified by " + "legal review." + ), + ) + + approved_community_interaction = models.TextField( + blank=True, + help_text=_("The community interaction allowed with this software project."), + ) + + licenses = models.ManyToManyField( + to="license_library.License", + through="ComponentAssignedLicense", + help_text=_( + "The license that applies to a component. There could be more than one license, " + 'in which case a choice is usually required. The expression "(default)" next to ' + "the license name indicates that this license applies to the component even if " + "you do not assert a particular license." + ), + ) + + # This reference all the Components associated with self through a + # Subcomponent relation where self is the parents. + # Only the children are copied on ParentChild relation type. + children = models.ManyToManyField( + to="self", + through="Subcomponent", + through_fields=("parent", "child"), + symmetrical=False, + ) + + # Explicitly declared to be handled by the m2m copy + packages = models.ManyToManyField( + to="component_catalog.Package", + through="ComponentAssignedPackage", + ) + + objects = DataspacedManager.from_queryset(ComponentQuerySet)() + + class Meta(BaseComponentMixin.Meta): + permissions = ( + ("change_usage_policy_on_component", "Can change the usage_policy of component"), + ) + + @property + def urn(self): + return urn.build("component", name=self.name, version=self.version) + + @property + def details_url(self): + return self.get_absolute_url() + + @staticmethod + def get_extra_relational_fields(): + return ["external_references"] + + @property + def permission_protected_fields(self): + return {"usage_policy": "change_usage_policy_on_component"} + + @property + def case_insensitive_unique_on(self): + return ["name"] + + @property + def css_icon(self): + return "fa-puzzle-piece" + + @cached_property + def package(self): + """Return the Package instance if 1 and only 1 Package is assigned to this Component.""" + with suppress(ObjectDoesNotExist, MultipleObjectsReturned): + return self.packages.get() + + def compute_completion_level(self): + """ + Return the computed value for the completion_level. + Convert the result based on the fields weight into a percentage. + For ManyToMany fields, we add the weight if there's at least 1 relation. + """ + max_weight = 0 + current_weight = 0 + + for field_name, weight in self.COMPONENT_FIELDS_WEIGHT: + max_weight += weight + field = self._meta.get_field(field_name) + is_m2m = isinstance(field, models.ManyToManyField) + field_value = getattr(self, field_name) + + if (is_m2m and field_value.count()) or (not is_m2m and field_value): + current_weight += weight + + return int(current_weight * 100 / max_weight) + + def update_completion_level(self): + """ + Update the completion_level of the current instance. + Using update() rather than save() to avoid noise in the history. + Hits the DB only if the recomputed value is different from the current + one. + Return True if the update was done. + """ + computed_level = self.compute_completion_level() + + if self.completion_level != computed_level: + Component.objects.filter(pk=self.pk).update(completion_level=computed_level) + msg = f"Updated completion_level for Component {self.pk}, new value: {computed_level}" + logger.debug(msg) + return True + + @cached_property + def all_packages(self): + return self.packages.all() + + def where_used(self, user): + """Callable made available in the reporting system.""" + return f"Product {self.product_set.get_related_secured_queryset(user).count()}\n" + + @property + def aboutcode_data(self): + """ + Return a dict of AboutCode supported fields. + Fields without a value are not included. + """ + component_data = { + "name": self.name, + "version": self.version, + "description": self.description, + "homepage_url": self.homepage_url, + "license_expression": self.license_expression, + "copyright": self.copyright, + "notice_url": self.notice_url, + "vcs_repository": self.vcs_url, + } + + owner = getattr(self, "owner", None) + if owner: + component_data.update( + { + "owner": owner.name, + "owner_url": owner.homepage_url, + "contact": owner.contact_info, + } + ) + + if self.license_expression: + component_data.update( + { + "attribute": self.attribution_required, + "redistribute": self.redistribution_required, + "track_changes": self.change_tracking_required, + } + ) + + return without_empty_values(component_data) + + def as_spdx(self, license_concluded=None): + """Return this Component as an SPDX Package entry.""" + external_refs = [] + + if cpe_external_ref := self.get_spdx_cpe_external_ref(): + external_refs.append(cpe_external_ref) + + attribution_texts = [] + if self.notice_text: + attribution_texts.append(self.notice_text) + + license_expression_spdx = self.get_license_expression_spdx_id() + + return spdx.Package( + name=self.name, + spdx_id=f"dejacode-{self._meta.model_name}-{self.uuid}", + supplier=self.owner.as_spdx() if self.owner else "", + license_concluded=license_concluded or license_expression_spdx, + license_declared=license_expression_spdx, + copyright_text=self.copyright, + version=self.version, + homepage=self.homepage_url, + description=self.description, + release_date=str(self.release_date) if self.release_date else "", + attribution_texts=attribution_texts, + external_refs=external_refs, + ) + + def get_spdx_packages(self): + return [self] + + +@receiver([post_copy, post_update], sender=Component) +def update_completion_level(sender, **kwargs): + """Update the Component.completion_level after copy or update.""" + kwargs["target"].update_completion_level() + + +class ComponentRelationshipMixin(models.Model): + notes = models.TextField( + blank=True, + help_text=_( + "Free form text about how the component is being used in this context, " + "especially useful if the information is needed for the review process." + ), + ) + + is_deployed = models.BooleanField( + default=True, + help_text=_("Indicates if the component is deployed in this context. Default = True."), + ) + + is_modified = models.BooleanField( + default=False, + help_text=_( + "Indicates if the original third-party component has been modified. Default = False." + ), + ) + + extra_attribution_text = models.TextField( + blank=True, + help_text=_( + "Additional text to be supplied with the component when attribution is generated. " + "For example, you may want to explain a license choice when the component is " + "available under a choice of licenses." + ), + ) + + package_paths = models.TextField( + blank=True, + help_text=_( + "This field is deprecated in DejaCode. To define one or more specific location(s) " + "of a Component in a Product, create a Product Codebase Resource for each location, " + "specifying the codebase path and referencing the Product Component." + ), + ) + + class Meta: + abstract = True + + @cached_property + def related_component_or_package(self): + """ + Return the related object instance: + - ProductComponent.component + - Subcomponent.child + - ProductPackage.package + """ + return ( + getattr(self, "component", None) + or getattr(self, "child", None) + or getattr(self, "package", None) + ) + + @cached_property + def standard_notice(self): + """ + Return a line separated combination of all License.standard_notice associated to + this relationship. + """ + return "\n\n".join( + [license.standard_notice for license in self.licenses.all() if license.standard_notice] + ) + + +class Subcomponent( + ReferenceNotesMixin, + UsagePolicyMixin, + LicenseExpressionMixin, + HistoryFieldsMixin, + ComponentRelationshipMixin, + ParentChildRelationshipModel, +): + parent = models.ForeignKey( + to="component_catalog.Component", + on_delete=models.CASCADE, + related_name="related_children", + ) + + child = models.ForeignKey( + to="component_catalog.Component", + on_delete=models.CASCADE, + related_name="related_parents", + ) + + # This license_expression is never generated but always stored. + license_expression = models.CharField( + _("License expression"), + max_length=1024, + blank=True, + db_index=True, + help_text=_( + "On a subcomponent relationship (which defines a child component of another " + "component), a license expression is limited by the license(s) assigned to the " + "child component, and expresses the license(s) that apply to the context of the " + "child component as it is used by the parent component. More than one applicable " + 'license can be expressed as "license-key-a AND license-key-b". A choice of licenses ' + 'can be expressed as "license-key-a OR license-key-b", and you can indicate the ' + "primary license as defined by your business by placing it first, on the left-hand " + "side of the OR relationship. The relationship words (OR, AND) can be combined as " + "needed, and the use of parentheses can be applied to clarify the meaning; for " + 'example "((license-key-a AND license-key-b) OR (license-key-c))". An exception ' + 'to a license can be expressed as “license-key WITH license-exception-key".' + ), + ) + + licenses = models.ManyToManyField( + to="license_library.License", + through="SubcomponentAssignedLicense", + ) + + purpose = models.CharField( + max_length=50, + db_index=True, + blank=True, + help_text=_( + "Indicates how this component/package is used in this context. " + "Suggested values are: Core, Test, Tool, Build, Reference, Requirement." + ), + ) + + class Meta: + verbose_name = _("subcomponent relationship") + unique_together = (("parent", "child"), ("dataspace", "uuid")) + ordering = ["parent", "child"] + permissions = ( + ( + "change_usage_policy_on_subcomponent", + "Can change the usage_policy of subcomponent relationship", + ), + ) + + def __str__(self): + if self.purpose: + return f"{self.purpose}: {self.child}" + return f"{self.child}" + + def clean(self, from_api=False): + # If one of the main Object (child or parent) is not saved in the DB + # yet then no further validation possible. + if not self.child_id or not self.parent_id: + return + super().clean(from_api) + + @property + def permission_protected_fields(self): + return {"usage_policy": "change_usage_policy_on_subcomponent"} + + def get_policy_from_child_component(self): + """Return the UsagePolicy associated to this Subcomponent child Component.""" + child_policy = self.child.usage_policy + if child_policy: + return child_policy.get_associated_policy_to_model(self) + + policy_from_child_component = cached_property(get_policy_from_child_component) + + +class ComponentAssignedLicense(DataspacedModel): + component = models.ForeignKey( + to="component_catalog.Component", + on_delete=models.CASCADE, + ) + + license = models.ForeignKey( + to="license_library.License", + on_delete=models.PROTECT, + help_text=_("Select from list of licenses."), + ) + + class Meta: + # 'dataspace' should never be part of the unique_together on + # Models that are used as a "through" relation. See #7404. + unique_together = (("component", "license"), ("dataspace", "uuid")) + ordering = ("component__name", "license__name") + verbose_name = _("Assigned license") + + def __str__(self): + return f"{self.component} is under {self.license}." + + +class SubcomponentAssignedLicense(DataspacedModel): + subcomponent = models.ForeignKey( + to="component_catalog.Subcomponent", + on_delete=models.CASCADE, + ) + + license = models.ForeignKey( + to="license_library.License", + on_delete=models.PROTECT, + ) + + class Meta: + unique_together = (("subcomponent", "license"), ("dataspace", "uuid")) + verbose_name = _("subcomponent assigned license") + + def __str__(self): + return f"{self.subcomponent} is under {self.license}." + + +class AcceptableLinkage(DataspacedModel): + label = models.CharField( + max_length=40, + ) + + description = models.TextField( + blank=True, + ) + + class Meta: + ordering = ["label"] + unique_together = (("dataspace", "label"), ("dataspace", "uuid")) + + def __str__(self): + return self.label + + def has_references(self): + return ( + Component.objects.scope(self.dataspace) + .filter(acceptable_linkages__contains=[self.label]) + .exists() + ) + + +class ComponentKeyword(DataspacedModel): + label = models.CharField( + max_length=50, + blank=True, + help_text=_( + "A short, descriptive label to categorize components and support searches, " + "reports, filters, and facets." + ), + ) + + description = models.TextField( + blank=True, + help_text=_("Additional remarks about the intention and purpose of a Keyword value."), + ) + + class Meta: + ordering = ["label"] + unique_together = (("dataspace", "label"), ("dataspace", "uuid")) + + def __str__(self): + return self.label + + +PACKAGE_URL_FIELDS = ["type", "namespace", "name", "version", "qualifiers", "subpath"] + + +class PackageQuerySet(PackageURLQuerySetMixin, DataspacedQuerySet): + def annotate_sortable_identifier(self): + """ + Annotate the QuerySet with a `sortable_identifier` value that combines + all the Package URL fields and the filename. + This value in used in the Package list for sorting by Identifier. + """ + return self.annotate( + sortable_identifier=Concat(*PACKAGE_URL_FIELDS, "filename", output_field=CharField()) + ) + + +class Package( + ExternalReferenceMixin, + UsagePolicyMixin, + SetPolicyFromLicenseMixin, + LicenseExpressionMixin, + RequestMixin, + HistoryFieldsMixin, + ReferenceNotesMixin, + HolderMixin, + KeywordsMixin, + CPEMixin, + URLFieldsMixin, + HashFieldsMixin, + PackageURLMixin, + DataspacedModel, +): + filename = models.CharField( + _("Filename"), + blank=True, + db_index=True, + max_length=255, # 255 is the maximum on most filesystems + validators=[validate_filename], + help_text=_( + "The exact file name (typically an archive of some type) of the package. " + "This is usually the name of the file as downloaded from a website." + ), + ) + + download_url = models.CharField( + _("Download URL"), + max_length=1024, + validators=[generic_uri_validator], + blank=True, + help_text=_("The download URL for obtaining the package."), + ) + + sha1 = models.CharField( + _("SHA1"), + max_length=40, + blank=True, + db_index=True, + help_text=_("The SHA1 signature of the package file."), + ) + + md5 = models.CharField( + _("MD5"), + max_length=32, + blank=True, + db_index=True, + help_text=_("The MD5 signature of the package file."), + ) + + size = models.BigIntegerField( + blank=True, + null=True, + db_index=True, + help_text=_("The size of the package file in bytes."), + ) + + release_date = models.DateField( + blank=True, + null=True, + help_text=_( + "The date that the package file was created, or when it was posted to its " + "original download source." + ), + ) + + primary_language = models.CharField( + db_index=True, + max_length=50, + blank=True, + help_text=_("The primary programming language associated with the package."), + ) + + description = models.TextField( + blank=True, + help_text=_("Free form description, preferably as provided by the author(s)."), + ) + + project = models.CharField( + max_length=50, + db_index=True, + blank=True, + help_text=PROJECT_FIELD_HELP, + ) + + notes = models.TextField( + blank=True, + help_text=_("Descriptive information about the package."), + ) + + license_expression = models.CharField( + _("License expression"), + max_length=1024, + blank=True, + db_index=True, + help_text=_( + "On a package in DejaCode, a license expression defines the relationship of one or " + "more licenses to that software as declared by its licensor. More than one " + 'applicable license can be expressed as "license-key-a AND license-key-b". A choice ' + 'of applicable licenses can be expressed as "license-key-a OR license-key-b", and you ' + "can indicate the primary (preferred) license by placing it first, on the left-hand " + "side of the OR relationship. The relationship words (OR, AND) can be combined as " + "needed, and the use of parentheses can be applied to clarify the meaning; for " + 'example "((license-key-a AND license-key-b) OR (license-key-c))". An exception to ' + 'a license can be expressed as “license-key WITH license-exception-key".' + ), + ) + + copyright = models.TextField( + blank=True, + help_text=_( + "The copyright statement(s) that pertain to this package, as contained in the " + "source or as specified in an associated file." + ), + ) + + notice_text = NoStripTextField( + blank=True, + help_text=_( + "The notice text provided by the authors of a package to identify the copyright " + "statement(s), contributors, and/or license obligations that apply to a package." + ), + ) + + author = models.TextField( + blank=True, + help_text=_( + "The name(s) of the author(s) of a software package as documented in the code." + ), + ) + + dependencies = models.JSONField( + blank=True, + default=list, + help_text=_( + "Identifies one or more potential dependencies required to deploy a package in " + "a particular context, with an emphasis on dependencies that may have an impact " + "on licensing and/or attribution obligations." + ), + ) + + repository_homepage_url = models.URLField( + _("Repository homepage URL"), + max_length=1024, + blank=True, + help_text=_( + "URL to the page for this package in its package repository. " + "This is typically different from the package homepage URL proper." + ), + ) + + repository_download_url = models.URLField( + _("Repository download URL"), + max_length=1024, + blank=True, + help_text=_( + "Download URL to download the actual archive of code of this " + "package in its package repository. " + "This may be different from the actual download URL." + ), + ) + + api_data_url = models.URLField( + _("API data URL"), + max_length=1024, + blank=True, + help_text=_( + "API URL to obtain structured data for this package such as the " + "URL to a JSON or XML api its package repository." + ), + ) + + declared_license = models.TextField( + blank=True, + help_text=_( + "The declared license mention, tag or text as found in a package manifest. " + "This can be a string, a list or dict of strings possibly nested, " + "as found originally in the manifest." + ), + ) + + datasource_id = models.CharField( + max_length=64, + blank=True, + help_text=_("The identifier for the datafile handler used to obtain this package."), + ) + + file_references = models.JSONField( + default=list, + blank=True, + help_text=_( + "List of file paths and details for files referenced in a package " + "manifest. These may not actually exist on the filesystem. " + "The exact semantics and base of these paths is specific to a " + "package type or datafile format." + ), + ) + + parties = models.JSONField( + default=list, + blank=True, + help_text=_("A list of parties such as a person, project or organization."), + ) + + licenses = models.ManyToManyField( + to="license_library.License", + through="PackageAssignedLicense", + ) + + objects = DataspacedManager.from_queryset(PackageQuerySet)() + + class Meta: + ordering = ["filename"] + unique_together = ( + ("dataspace", "uuid"), + # This constraint prevent to insert twice the exact same Package data. + # If one value of the filename, download_url, or any purl fields, changed, + # the Package is not a duplicate and can be created. + # Note that an empty string '' counts as a unique value. + # + # A `package_url` can be identical for multiple files. + # For example, ".zip" and ".whl" release of a Package may share the same `package_url`. + # Therefore, we only apply this unique constraint on `package_url` in the context of a + # `download_url` and `filename`. + # Also, a duplicated `download_url`+`filename` combination is allowed if any of the + # `package_url` fields is different. + ( + "dataspace", + *PACKAGE_URL_FIELDS, + "download_url", + "filename", + ), + ) + indexes = [ + models.Index(fields=["md5"]), + models.Index(fields=["sha1"]), + models.Index(fields=["sha256"]), + models.Index(fields=["sha512"]), + ] + permissions = ( + ("change_usage_policy_on_package", "Can change the usage_policy of package"), + ) + + def __str__(self): + return self.identifier + + def save(self, *args, **kwargs): + """Do not allow to save without an identifier.""" + self.enforce_identifier() + super().save(*args, **kwargs) + + def enforce_identifier(self): + """Raise a error if an identifier, package_url or filename, is not set.""" + if not self.identifier: + raise ValidationError("package_url or filename required") + + @property + def identifier(self): + """ + Provide a unique value to identify each Package. + It is the Package URL (minus the 'pkg:' prefix) if one exists; + otherwise it is the Package Filename. + """ + return self.short_package_url or self.filename + + @classmethod + def identifier_help(cls): + return _( + "Package Identifier is a system-derived field that provides a " + "unique value to identify each Package. " + "It is the Package URL if one exists; " + "otherwise it is the Package Filename." + ) + + @property + def plain_package_url(self): + """Package URL without the qualifiers and subpath fields.""" + try: + package_url = PackageURL(self.type, self.namespace, self.name, self.version) + except ValueError: + return "" + return str(package_url) + + @property + def short_package_url(self): + """Plain Package URL (no qualifiers no subpath) without the 'pkg:' prefix.""" + return self.plain_package_url.replace("pkg:", "", 1) + + @property + def package_url_filename(self): + """ + Return the Package URL string as a valid filename. + Useful when `Package.filename` is not available. + """ + cleaned_package_url = self.short_package_url + for char in "/@?=#": + cleaned_package_url = cleaned_package_url.replace(char, "_") + return get_valid_filename(cleaned_package_url) + + @property + def inferred_url(self): + """Return the URL deduced from the information available in a Package URL (purl).""" + return purl2url.get_repo_url(self.package_url) + + def get_url(self, name, params=None, include_identifier=False): + if not params: + params = [self.dataspace.name, quote_plus(str(self.uuid))] + if include_identifier: + params.insert(1, self.identifier) + return super().get_url(name, params) + + def get_absolute_url(self): + return self.get_url("details", include_identifier=True) + + @property + def details_url(self): + return self.get_absolute_url() + + def get_change_url(self): + return self.get_url("change", include_identifier=True) + + def get_delete_url(self): + return self.get_url("delete") + + def get_about_files_url(self): + return self.get_url("about_files") + + def get_export_spdx_url(self): + return self.get_url("export_spdx") + + def get_export_cyclonedx_url(self): + return self.get_url("export_cyclonedx") + + @classmethod + def get_identifier_fields(cls): + """ + Explicit list of identifier fields as we do not enforce a unique together + on this model. + This is used in the Importer, to catch duplicate entries. + """ + return ["filename", "download_url", *PACKAGE_URL_FIELDS] + + @property + def permission_protected_fields(self): + return {"usage_policy": "change_usage_policy_on_package"} + + @staticmethod + def autocomplete_term_adjust(term): + """Cleanup the `term` string replacing some special chars into spaces.""" + chars_to_replace_ = "-_@/" + for char in chars_to_replace_: + term = term.replace(char, " ") + return term + + def collect_data(self, force_update=False, save=True): + """ + Download the Package content using the `download_url` to collect the + md5, sha1, sha256, sha512, size, and filename. + If all values for those fields are set, the process + will be skipped unless the `force_update` option is given. + Return `True` if the package instance was updated. + """ + if not self.download_url: + tasks_logger.info("No Download URL available.") + return + + collect_fields = [ + "size", + "md5", + "sha1", + "sha256", + "sha512", + "filename", + ] + + all_has_values = all(getattr(self, field_name, None) for field_name in collect_fields) + if all_has_values and not force_update: + tasks_logger.info("Size, MD5, SHA1, SH256, SHA512, and filename values already set.") + return + + try: + package_data = collect_package_data(self.download_url) + except DataCollectionException as e: + tasks_logger.info(e) + return + tasks_logger.info("Package data collected.") + + update_fields = [] + for field_name in collect_fields: + if not getattr(self, field_name, None): + setattr(self, field_name, package_data.get(field_name)) + update_fields.append(field_name) + + if save: + self.save(update_fields=update_fields) + tasks_logger.info(f'Package field(s) updated: {", ".join(update_fields)}') + + return update_fields + + def update_package_url(self, user, save=False, overwrite=False, history=False): + """ + Generate and set a Package URL from the Download URL on this Package instance. + By default, update() is used on the Package to avoid updating field such as + `last_modified_date`. save() can be used instead providing the `save` argument. + Existing Package URL value can be overwritten using the `overwrite` argument. + When `history` is True, History entry will be created along updating the + Package URL. + """ + skip_conditions = [self.package_url and not overwrite, not self.download_url] + if any(skip_conditions): + return + + package_url = url2purl.get_purl(self.download_url) + if not package_url or str(package_url) == str(self.package_url): + return + + if save: + self.set_package_url(package_url) + self.last_modified_by = user + self.save() + else: + package_url_dict = package_url.to_dict(encode=True, empty="") + Package.objects.filter(pk=self.pk).update(**package_url_dict) + + if history: + History.log_change(user, self, message="Set Package URL from Download URL") + + return package_url + + @property + def size_formatted(self): + if self.size: + return f"{self.size} ({filesizeformat(self.size)})" + + @cached_property + def component(self): + """Return the Component instance if 1 and only 1 Component is assigned to this Package.""" + with suppress(ObjectDoesNotExist, MultipleObjectsReturned): + return self.component_set.get() + + def set_values_from_component(self, component, user): + changed_fields = set_fields_from_object( + source=component, + target=self, + fields=COMPONENT_PACKAGE_COMMON_FIELDS, + ) + if changed_fields: + self.last_modified_by = user + self.save() + change_message = [{"changed": {"fields": changed_fields}}] + History.log_change(user, self, message=change_message) + return changed_fields + + @property + def css_icon(self): + return "fa-archive" + + @classmethod + def package_url_help(cls): + return _( + 'A Package URL "purl" is a URL string used to identify and locate ' + "a software package in a mostly universal and uniform way across " + "programing languages, package managers, packaging conventions, " + "tools, APIs and databases." + ) + + @property + def aboutcode_data(self): + """ + Return a dict of AboutCode supported fields. + Fields without a value are not included. + """ + package_data = { + "about_resource": self.filename or ".", + "package_url": self.package_url, + "name": self.name, + "version": self.version, + "description": self.description, + "checksum_sha1": self.sha1, + "checksum_md5": self.md5, + "copyright": self.copyright, + "download_url": self.download_url, + "homepage_url": self.homepage_url, + "license_expression": self.license_expression, + "notes": self.notes, + } + + if self.license_expression: + package_data.update( + { + "attribute": self.attribution_required, + "redistribute": self.redistribution_required, + "track_changes": self.change_tracking_required, + } + ) + + return without_empty_values(package_data) + + def as_about(self, extra=None): + """ + Return ABOUT file data as a dict. + https://github.com/nexB/aboutcode-toolkit/blob/develop/SPECIFICATION.rst + + Optionally provided `extra` dict will be added to the returned data. + + If one and only one Component is assigned to the Package, the Component values + will be included if the corresponding Package field is empty. + """ + package_data = self.aboutcode_data + + component = self.component + if component: + # Overrides Component data with available Package data + aboutcode_data = {**component.aboutcode_data, **package_data} + else: + aboutcode_data = package_data + + if extra: + aboutcode_data.update(extra) + + return aboutcode_data + + def as_about_yaml(self, extra=None): + """Return ABOUT file data YAML formatted.""" + about_data = self.as_about(extra) + if about_data: + about = About() + about.load_dict(about_data, base_dir="") + return about.dumps() + + @property + def about_file_name(self): + """ + Return the ABOUT file filename. + Using the `Package.filename` when available, or the Package URL slug. + """ + return f"{self.filename or self.package_url_filename}.ABOUT" + + @property + def notice_file_name(self): + """ + Return the NOTICE file filename. + Using the `Package.filename` when available, or the Package URL slug. + """ + return f"{self.filename or self.package_url_filename}.NOTICE" + + def get_about_files(self): + """ + Return a list of all AboutCode files related to that Package: + - .ABOUT + - .NOTICE + - .LICENSE + """ + about_files = [] + extra = {} + + notice_text = self.notice_text + component = self.component + if component and component.notice_text: + notice_text = component.notice_text + + if notice_text: + about_files.append((self.notice_file_name, normalize_newlines(notice_text))) + extra["notice_file"] = self.notice_file_name + + licenses = [] + # Using `license_expression` fields to avoid extra DB queries. + if component and component.license_expression: + licenses = component.licenses.all() + elif self.license_expression: + licenses = self.licenses.all() + + for license_instance in licenses: + license_file_name = f"{license_instance.key}.LICENSE" + normalized_text = normalize_newlines(license_instance.full_text) + about_files.append((license_file_name, normalized_text)) + extra.setdefault("licenses", []).append( + { + "key": license_instance.key, + "name": license_instance.name, + "file": license_file_name, + } + ) + + about_files.append((self.about_file_name, self.as_about_yaml(extra))) + + return about_files + + def as_spdx(self, license_concluded=None): + """Return this Package as an SPDX Package entry.""" + checksums = [ + spdx.Checksum(algorithm=algorithm, value=checksum_value) + for algorithm in ["sha1", "md5"] + if (checksum_value := getattr(self, algorithm)) + ] + + attribution_texts = [] + if self.notice_text: + attribution_texts.append(self.notice_text) + + external_refs = [] + + if package_url := self.package_url: + external_refs.append( + spdx.ExternalRef( + category="PACKAGE-MANAGER", + type="purl", + locator=package_url, + ) + ) + + if cpe_external_ref := self.get_spdx_cpe_external_ref(): + external_refs.append(cpe_external_ref) + + license_expression_spdx = self.get_license_expression_spdx_id() + + return spdx.Package( + name=self.name or self.filename, + spdx_id=f"dejacode-{self._meta.model_name}-{self.uuid}", + download_location=self.download_url, + license_declared=license_expression_spdx, + license_concluded=license_concluded or license_expression_spdx, + copyright_text=self.copyright, + version=self.version, + homepage=self.homepage_url, + filename=self.filename, + description=self.description, + release_date=str(self.release_date) if self.release_date else "", + comment=self.notes, + attribution_texts=attribution_texts, + checksums=checksums, + external_refs=external_refs, + ) + + def get_spdx_packages(self): + return [self] + + def as_cyclonedx(self, license_expression_spdx=None): + """Return this Package as an CycloneDX Component entry.""" + expression_spdx = license_expression_spdx or self.get_license_expression_spdx_id() + + licenses = [] + if expression_spdx: + licenses = [ + cyclonedx_model.LicenseChoice(license_expression=expression_spdx), + ] + + hash_fields = { + "md5": cyclonedx_model.HashAlgorithm.MD5, + "sha1": cyclonedx_model.HashAlgorithm.SHA_1, + "sha256": cyclonedx_model.HashAlgorithm.SHA_256, + "sha512": cyclonedx_model.HashAlgorithm.SHA_512, + } + hashes = [ + cyclonedx_model.HashType(algorithm=algorithm, hash_value=hash_value) + for field_name, algorithm in hash_fields.items() + if (hash_value := getattr(self, field_name)) + ] + + purl = self.package_url + return cyclonedx_component.Component( + name=self.name, + version=self.version, + bom_ref=purl or str(self.uuid), + purl=purl, + licenses=licenses, + copyright_=self.copyright, + description=self.description, + cpe=self.cpe, + author=self.author, + hashes=hashes, + properties=get_cyclonedx_properties(self), + ) + + @cached_property + def github_repo_url(self): + """Generate a GitHub code view URL from a Download URL if the format is known.""" + if not self.download_url: + return + + patterns = [ + r"^https?://github.com/(?P.+)/(?P.+)/archive/(?P.+)" + r"(.zip|.tar.gz)$", + r"^https?://github.com/(?P.+)/(?P.+)/releases/download/(?P.+)/.*$", + ] + + for pattern in patterns: + compiled_pattern = re.compile(pattern, re.VERBOSE) + match = compiled_pattern.match(self.download_url) + if match: + url_template = "https://github.com/{owner}/{repo}/tree/{branch}" + return url_template.format(**match.groupdict()) + + def where_used(self, user): + """Callable for the reporting system.""" + return ( + f"Product {self.product_set.get_related_secured_queryset(user).count()}\n" + f"Component {self.component_set.count()}\n" + ) + + def get_purldb_entries(self, user, max_request_call=0, timeout=None): + """ + Return the PurlDB entries that correspond to this Package instance. + + Matching on the following fields order: + - Package URL + - SHA1 + - Download URL + + A `max_request_call` integer can be provided to limit the number of + HTTP requests made to the PackageURL server. + By default, one request will be made per field until a match is found. + Providing max_request_call=1 will stop after the first request, even + is nothing was found. + """ + payloads = [] + + package_url = self.package_url + if package_url: + payloads.append({"purl": package_url}) + if self.sha1: + payloads.append({"sha1": self.sha1}) + if self.download_url: + payloads.append({"download_url": self.download_url}) + + for index, payload in enumerate(payloads): + if max_request_call and index >= max_request_call: + return + + packages_data = PurlDB(user).find_packages(payload, timeout) + if packages_data: + return packages_data + + +class PackageAssignedLicense(DataspacedModel): + package = models.ForeignKey( + to="component_catalog.Package", + on_delete=models.CASCADE, + ) + + license = models.ForeignKey( + to="license_library.License", + on_delete=models.PROTECT, + ) + + class Meta: + unique_together = (("package", "license"), ("dataspace", "uuid")) + verbose_name = _("package assigned license") + + def __str__(self): + return f"{self.package} is under {self.license}." + + +class ComponentAssignedPackage(DataspacedModel): + component = models.ForeignKey( + to="component_catalog.Component", + on_delete=models.CASCADE, + ) + + package = models.ForeignKey( + to="component_catalog.Package", + on_delete=models.CASCADE, + ) + + class Meta: + unique_together = (("component", "package"), ("dataspace", "uuid")) + ordering = ("component", "package") + verbose_name = _("component assigned package") + + def __str__(self): + return f"<{self.component}>: {self.package}" diff --git a/component_catalog/programming_languages.py b/component_catalog/programming_languages.py new file mode 100644 index 00000000..3f600089 --- /dev/null +++ b/component_catalog/programming_languages.py @@ -0,0 +1,309 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# DejaCode is a trademark of nexB Inc. +# SPDX-License-Identifier: AGPL-3.0-only +# See https://github.com/nexB/dejacode for support or download. +# See https://aboutcode.org for more information about AboutCode FOSS projects. +# + + +PROGRAMMING_LANGUAGES = ( + "4th Dimension/4D", + "ABAP", + "ABC", + "ActionScript", + "Ada", + "Agilent VEE", + "Algol", + "Alice", + "Angelscript", + "ANTLR", + "Apex", + "APL", + "AppleScript", + "Arc", + "Arduino", + "ASP", + "AspectJ", + "Assembly", + "ATLAS", + "Augeas", + "AutoHotkey", + "AutoIt", + "AutoLISP", + "Automator", + "Avenue", + "Awk", + "Bash", + "Batchfile", + "Visual Basic", + "bc", + "BCPL", + "BETA", + "BlitzMax", + "Boo", + "Bourne Shell", + "Bro", + "C", + "C Shell", + "C#", + "C++", + "C++/CLI", + "C-Omega", + "Caml", + "Ceylon", + "CFML", + "cg", + "Ch", + "Chapel", + "CHILL", + "CIL", + "CL (OS/400)", + "Clarion", + "Clean", + "Clipper", + "Clojure", + "CLU", + "COBOL", + "Cobra", + "CoffeeScript", + "ColdFusion", + "COMAL", + "Common Lisp", + "Coq", + "Crystal", + "CSS", + "cT", + "Curl", + "D", + "Dart", + "DCL", + "DCPU-16 ASM", + "Delphi/Object Pascal", + "DiBOL", + "Dylan", + "E", + "eC", + "Ecl", + "ECMAScript", + "EGL", + "Eiffel", + "Elixir", + "Elm", + "Emacs Lisp", + "Erlang", + "Etoys", + "Euler", + "Euphoria", + "EXEC", + "Expect", + "F#", + "Factor", + "Falcon", + "Fancy", + "Fantom", + "Felix", + "Fish", + "Flex", + "Forth", + "Fortran", + "Fortran 90", + "Fortress", + "FreePascal", + "Frege", + "GAS", + "Gambas", + "GDScript", + "GLSL (OpenGL Shading Language)", + "GNU Octave", + "Go", + "Google AppsScript", + "Gosu", + "Groovy", + "Haskell", + "Haxe", + "Heron", + "HTML", + "HPL", + "Hy", + "HyperTalk", + "Icon", + "IDL", + "Inform", + "Informix-4GL", + "INTERCAL", + "Io", + "Ioke", + "J", + "J#", + "JADE", + "Java", + "Java FX Script", + "JavaScript", + "JScript", + "JScript.NET", + "Julia", + "Kaya", + "Korn Shell", + "Kotlin", + "LabVIEW", + "Ladder Logic", + "Lasso", + "Limbo", + "Lingo", + "Lisp", + "LiveScript", + "Logo", + "Logtalk", + "LotusScript", + "LPC", + "Lua", + "Lustre", + "M4", + "MAD", + "Mako", + "Magic", + "Magik", + "Malbolge", + "MANTIS", + "Maple", + "Mathematica", + "MATLAB", + "Max/MSP", + "MAXScript", + "MEL", + "Mercury", + "Mirah", + "Miva", + "ML", + "Monkey", + "Modula", + "Modula-2", + "Modula-3", + "MOO", + "Moto", + "MS-DOS Batch", + "MUMPS", + "MXML", + "N/A", + "NASM", + "NATURAL", + "Nemerle", + "Nimrod", + "NQC", + "NSIS", + "Nu", + "NXT-G", + "Oberon", + "Object Rexx", + "Objective-C", + "Objective-C++", + "Objective-J", + "OCaml", + "Occam", + "Octave", + "ooc", + "Opa", + "OpenCL", + "OpenEdge ABL", + "OPL", + "Oz", + "Paradox", + "Parrot", + "Pascal", + "Perl", + "PHP", + "Pike", + "PILOT", + "PL/I", + "PL/SQL", + "Pliant", + "PostScript", + "POV-Ray", + "PowerBasic", + "PowerScript", + "PowerShell", + "Processing", + "Progress 4GL", + "Prolog", + "Puppet", + "Pure Data", + "Python", + "Q", + "QBasic", + "QML", + "R", + "Racket", + "Ragel", + "REALBasic", + "Red", + "REBOL", + "Revolution", + "REXX", + "RPG (OS/400)", + "Ruby", + "Rust", + "S", + "SR", + "S-PLUS", + "SAS", + "Sather", + "Scala", + "Scheme", + "Scilab", + "Scratch", + "Scriptol", + "sed", + "Seed7", + "Self", + "Shell", + "SIGNAL", + "Simula", + "Simulink", + "Slate", + "Smalltalk", + "Smarty", + "Snobol", + "SPARK", + "SPSS", + "SQL", + "SQR", + "Squeak", + "Squirrel", + "Standard ML", + "Suneido", + "SuperCollider", + "Swift", + "SWIG", + "TACL", + "Tcl", + "Tcsh", + "Tex", + "thinBasic", + "TOM", + "Transact-SQL", + "Turing", + "TypeScript", + "Vala/Genie", + "VBScript", + "Velocity", + "Verilog", + "VHDL", + "VimL", + "Visual Basic .NET", + "Visual FoxPro", + "WebDNA", + "Whitespace", + "X10", + "xBase", + "XBase++", + "XBasic", + "Xen", + "XML", + "XPL", + "XSLT", + "XQuery", + "yacc", + "Yorick", + "Z shell", +) diff --git a/component_catalog/templates/admin/component_catalog/add_to_product.html b/component_catalog/templates/admin/component_catalog/add_to_product.html new file mode 100644 index 00000000..854add47 --- /dev/null +++ b/component_catalog/templates/admin/component_catalog/add_to_product.html @@ -0,0 +1,82 @@ +{% extends "admin/base_site.html" %} + +{% load i18n static admin_urls %} + +{% block breadcrumbs %} + {% include "admin/includes/breadcrumbs.html" with title='Add To Product' %} +{% endblock %} + +{% block content_title %} +

Add {% trans opts.verbose_name %} to a product

+{% endblock %} + +{% block content %} +
+ {% csrf_token %} +

+ You can use this page to assign {% trans opts.verbose_name_plural|title %} to a Product, either an existing one or a + new Product that you specify. + The Product provides you with a group of {% trans opts.verbose_name_plural|title %} that are used together, so that + you can generate Attribution documentation for all of the {% trans opts.verbose_name_plural|title %} in that Product. +

+

+ {{ form.non_field_errors }} + + {{ form.product.errors }} + {{ form.product }} + {% if perms.product_portfolio.add_product %} + + {% endif %} + {{ form.ct }} + {{ form.ids }} +

+

+
+
+

The following {% trans opts.verbose_name_plural %} will be added to the product:

+
+
    + {% for obj in form.get_selected_objects %} +
  • {{ obj }}
  • + {% endfor %} +
+
+
+
+ + +
+{% endblock %} + +{% block javascripts %} + {{ block.super }} + +{% endblock %} \ No newline at end of file diff --git a/component_catalog/templates/admin/component_catalog/component/change_form.html b/component_catalog/templates/admin/component_catalog/component/change_form.html new file mode 100644 index 00000000..8bbae6ad --- /dev/null +++ b/component_catalog/templates/admin/component_catalog/component/change_form.html @@ -0,0 +1,12 @@ +{% extends "admin/change_form_extended.html" %} + +{% block javascripts %} + {{ block.super }} + +{% endblock %} \ No newline at end of file diff --git a/component_catalog/templates/admin/component_catalog/component/delete_confirmation.html b/component_catalog/templates/admin/component_catalog/component/delete_confirmation.html new file mode 100644 index 00000000..591a3b0e --- /dev/null +++ b/component_catalog/templates/admin/component_catalog/component/delete_confirmation.html @@ -0,0 +1,8 @@ +{% extends 'admin/delete_confirmation.html' %} + +{% block javascripts %} + {{ block.super }} + {% if not perms_lacking and not protected and object.packages.exists %} + {% include 'admin/component_catalog/component/delete_packages_js.html' with packages=object.packages.all %} + {% endif %} +{% endblock %} \ No newline at end of file diff --git a/component_catalog/templates/admin/component_catalog/component/delete_packages_js.html b/component_catalog/templates/admin/component_catalog/component/delete_packages_js.html new file mode 100644 index 00000000..53f98e8d --- /dev/null +++ b/component_catalog/templates/admin/component_catalog/component/delete_packages_js.html @@ -0,0 +1,25 @@ + \ No newline at end of file diff --git a/component_catalog/templates/admin/component_catalog/component/delete_selected_confirmation.html b/component_catalog/templates/admin/component_catalog/component/delete_selected_confirmation.html new file mode 100644 index 00000000..76e0eb62 --- /dev/null +++ b/component_catalog/templates/admin/component_catalog/component/delete_selected_confirmation.html @@ -0,0 +1,8 @@ +{% extends 'admin/delete_selected_confirmation.html' %} + +{% block javascripts %} + {{ block.super }} + {% if not perms_lacking and not protected and queryset.exists %} + {% include 'admin/component_catalog/component/delete_packages_js.html' with plural=True %} + {% endif %} +{% endblock %} \ No newline at end of file diff --git a/component_catalog/templates/admin/component_catalog/component/set_policy.html b/component_catalog/templates/admin/component_catalog/component/set_policy.html new file mode 100644 index 00000000..e109d457 --- /dev/null +++ b/component_catalog/templates/admin/component_catalog/component/set_policy.html @@ -0,0 +1,32 @@ +{% extends "admin/set_policy_base.html" %} + +{% block content_title %} +

Set usage policy from licenses

+{% endblock %} + +{% block table %} + + + + + + + + + + {% for component in form.get_objects %} + + + + + + + + + {% endfor %} +
ComponentUsage policyPolicy from licensePrimary licenseLicense expression
{{ component }}{{ component.usage_policy|default:'' }}{{ component.policy_from_primary_license|default:'' }}{{ component.primary_license|default:'' }}{{ component.license_expression }} + {% if component.policy_from_primary_license and component.usage_policy != component.policy_from_primary_license %} + + {% endif %} +
+{% endblock %} \ No newline at end of file diff --git a/component_catalog/templates/admin/component_catalog/package/change_form.html b/component_catalog/templates/admin/component_catalog/package/change_form.html new file mode 100644 index 00000000..ac68cd98 --- /dev/null +++ b/component_catalog/templates/admin/component_catalog/package/change_form.html @@ -0,0 +1,27 @@ +{% extends "admin/change_form_extended.html" %} + +{% load i18n admin_urls %} + +{% block object-tools-items %} + {{ block.super }} + {% if change %}{# Do not display the following on ADDITION #} + {# The `original` value may not be available, during a "Save as" with errors for example #} + {% url 'component_catalog:package_about_files' user.dataspace original.uuid as package_about_files %} + {% if package_about_files %} +
  • + {% trans 'Download AboutCode files' %} +
  • + {% endif %} + {% endif %} +{% endblock %} + +{% block javascripts %} + {{ block.super }} + +{% endblock %} \ No newline at end of file diff --git a/component_catalog/templates/admin/component_catalog/package/change_list.html b/component_catalog/templates/admin/component_catalog/package/change_list.html new file mode 100644 index 00000000..06af0c7b --- /dev/null +++ b/component_catalog/templates/admin/component_catalog/package/change_list.html @@ -0,0 +1,20 @@ +{% extends "admin/change_list_extended.html" %} + +{% load static %} + +{% block javascripts %} + {{ block.super }} + +{% endblock %} \ No newline at end of file diff --git a/component_catalog/templates/admin/component_catalog/package/set_policy.html b/component_catalog/templates/admin/component_catalog/package/set_policy.html new file mode 100644 index 00000000..c37069f8 --- /dev/null +++ b/component_catalog/templates/admin/component_catalog/package/set_policy.html @@ -0,0 +1,32 @@ +{% extends "admin/set_policy_base.html" %} + +{% block content_title %} +

    Set usage policy from licenses

    +{% endblock %} + +{% block table %} + + + + + + + + + + {% for package in form.get_objects %} + + + + + + + + + {% endfor %} +
    PackageUsage policyPolicy from licensePrimary licenseLicense expression
    {{ package }}{{ package.usage_policy|default:'' }}{{ package.policy_from_primary_license|default:'' }}{{ package.primary_license|default:'' }}{{ package.license_expression }} + {% if package.policy_from_primary_license and package.usage_policy != package.policy_from_primary_license %} + + {% endif %} +
    +{% endblock %} \ No newline at end of file diff --git a/component_catalog/templates/admin/component_catalog/subcomponent/set_policy.html b/component_catalog/templates/admin/component_catalog/subcomponent/set_policy.html new file mode 100644 index 00000000..e57e4d21 --- /dev/null +++ b/component_catalog/templates/admin/component_catalog/subcomponent/set_policy.html @@ -0,0 +1,34 @@ +{% extends "admin/set_policy_base.html" %} + +{% block content_title %} +

    Set usage policy from components

    +{% endblock %} + +{% block table %} + + + + + + + + + + + {% for subcomponent in form.get_objects %} + + + + + + + + + + {% endfor %} +
    Subcomponent relationshipParentChildUsage policyPolicy from child componentLicense expression
    {{ subcomponent }}{{ subcomponent.parent }}{{ subcomponent.child }}{{ subcomponent.usage_policy|default:'' }}{{ subcomponent.policy_from_child_component|default:'' }}{{ subcomponent.license_expression }} + {% if subcomponent.policy_from_child_component and subcomponent.usage_policy != subcomponent.policy_from_child_component %} + + {% endif %} +
    +{% endblock %} \ No newline at end of file diff --git a/component_catalog/templates/admin/set_policy_base.html b/component_catalog/templates/admin/set_policy_base.html new file mode 100644 index 00000000..69193e81 --- /dev/null +++ b/component_catalog/templates/admin/set_policy_base.html @@ -0,0 +1,65 @@ +{% extends "admin/base_site.html" %} + +{% load i18n static admin_urls %} + +{% block breadcrumbs %} + {% include "admin/includes/breadcrumbs.html" with title='Set Policy' %} +{% endblock %} + +{% block content %} +
    {% csrf_token %} + {% if adminform.form.errors %} +

    {% if adminform.form.errors|length == 1 %}{% trans "Please correct the error below." %}{% else %}{% trans "Please correct the errors below." %}{% endif %}

    +
      {% for error in adminform.form.non_field_errors %}
    • {{ error }}
    • {% endfor %}
    + {% endif %} + +

    + You can set the usage policy on the application objects below where the object has + no policy set, or where the policy is different from the associated component or + license, by selecting the object(s) that you want to update and clicking the + "Set policies" button below. +

    + + {% block table %} + {% endblock %} + + +
    +{% endblock %} + +{% block javascripts %} + {{ block.super }} + +{% endblock %} \ No newline at end of file diff --git a/component_catalog/templates/component_catalog/base_component_package_details.html b/component_catalog/templates/component_catalog/base_component_package_details.html new file mode 100644 index 00000000..1cbc1e88 --- /dev/null +++ b/component_catalog/templates/component_catalog/base_component_package_details.html @@ -0,0 +1,33 @@ +{% extends 'object_details_base.html' %} +{% load i18n static %} + +{% block content %} + {{ block.super }} + {% if form %} + {% include 'component_catalog/includes/add_to_modal.html' with modal_dialog_class='modal-lg' %} + {% endif %} + {% if add_to_component_form %} + {% include 'component_catalog/includes/add_to_modal.html' with form=add_to_component_form %} + {% endif %} +{% endblock %} + +{% block extrastyle %} + {{ block.super }} + + +{% endblock %} + +{% block javascripts %} + {{ block.super }} + + + + {% include 'includes/dependencies-json-viewer.js.html' %} + {% if open_add_to_package_modal %} + + {% endif %} +{% endblock %} \ No newline at end of file diff --git a/component_catalog/templates/component_catalog/base_component_package_list.html b/component_catalog/templates/component_catalog/base_component_package_list.html new file mode 100644 index 00000000..d90a75cd --- /dev/null +++ b/component_catalog/templates/component_catalog/base_component_package_list.html @@ -0,0 +1,87 @@ +{% extends 'object_list_base.html' %} +{% load i18n %} + +{% block top-right-buttons %} + + {% if form or add_to_component_form %} +
    + +
    + {% endif %} + {{ block.super }} +
    + {{ filter.form.sort }} +
    +{% endblock %} + +{% block content %} + {{ block.super }} + {% if form %} + {% include 'component_catalog/includes/add_to_modal.html' %} + {% endif %} + {% if add_to_component_form %} + {% include 'component_catalog/includes/add_to_modal.html' with form=add_to_component_form %} + {% endif %} +{% endblock %} + +{% block javascripts %} + {{ block.super }} + {% if form or add_to_component_form %} + {% include 'component_catalog/includes/add_to.js.html' %} + {% endif %} + + +{% endblock %} \ No newline at end of file diff --git a/component_catalog/templates/component_catalog/component_details.html b/component_catalog/templates/component_catalog/component_details.html new file mode 100644 index 00000000..173980dd --- /dev/null +++ b/component_catalog/templates/component_catalog/component_details.html @@ -0,0 +1,63 @@ +{% extends 'component_catalog/base_component_package_details.html' %} +{% load i18n static %} + +{% block pager-toolbar %} + {% if form %} + + + + {% endif %} + {% if user.is_authenticated %} +
    + +
    + {% endif %} + {% if user.is_staff and is_user_dataspace %} + {% if has_change_permission or has_change_subcomponent_permission or has_change_package_permission %} + + {% endif %} + {% endif %} + {{ block.super }} +{% endblock %} + +{% block javascripts %} + {{ block.super }} + + {% include 'component_catalog/includes/component_hierarchy.js.html' with related_parents=tabsets.Hierarchy.fields.0.1.related_parents related_children=tabsets.Hierarchy.fields.0.1.related_children productcomponents=tabsets.Hierarchy.fields.0.1.productcomponents %} + {% if tabsets.Owner.extra %} + {% include 'organization/includes/owner_hierarchy.js.html' with current_owner=object.owner parents=tabsets.Owner.extra.context.owner_parents children=tabsets.Owner.extra.context.owner_children tab_name="tab_owner" %} + {% endif %} +{% endblock %} \ No newline at end of file diff --git a/component_catalog/templates/component_catalog/includes/add_package_modal.html b/component_catalog/templates/component_catalog/includes/add_package_modal.html new file mode 100644 index 00000000..75ac5595 --- /dev/null +++ b/component_catalog/templates/component_catalog/includes/add_package_modal.html @@ -0,0 +1,76 @@ +{% load static %} + + + + + \ No newline at end of file diff --git a/component_catalog/templates/component_catalog/includes/add_to.js.html b/component_catalog/templates/component_catalog/includes/add_to.js.html new file mode 100644 index 00000000..08b5d021 --- /dev/null +++ b/component_catalog/templates/component_catalog/includes/add_to.js.html @@ -0,0 +1,55 @@ + \ No newline at end of file diff --git a/component_catalog/templates/component_catalog/includes/add_to_modal.html b/component_catalog/templates/component_catalog/includes/add_to_modal.html new file mode 100644 index 00000000..7b17e89c --- /dev/null +++ b/component_catalog/templates/component_catalog/includes/add_to_modal.html @@ -0,0 +1,26 @@ +{% load crispy_forms_tags %} + \ No newline at end of file diff --git a/component_catalog/templates/component_catalog/includes/component_hierarchy.js.html b/component_catalog/templates/component_catalog/includes/component_hierarchy.js.html new file mode 100644 index 00000000..49478251 --- /dev/null +++ b/component_catalog/templates/component_catalog/includes/component_hierarchy.js.html @@ -0,0 +1,84 @@ + \ No newline at end of file diff --git a/component_catalog/templates/component_catalog/includes/component_list_table.html b/component_catalog/templates/component_catalog/includes/component_list_table.html new file mode 100644 index 00000000..77291107 --- /dev/null +++ b/component_catalog/templates/component_catalog/includes/component_list_table.html @@ -0,0 +1,100 @@ +{% load i18n %} +{% load inject_preserved_filters from dje_tags %} +{% load naturaltime_short from dje_tags %} + + {% if form %} + {% include 'includes/object_list_table_header.html' with include_checkbox=True checkbox_select_all=True %} + {% else %} + {% include 'includes/object_list_table_header.html' %} + {% endif %} + + {% for name_version_group in name_version_groups %} + {% for object in name_version_group %} + {# Display the first name version and hide the others #} + + {% if is_grouping_active %} + + {% endif %} + {% if form %} + + {% endif %} + + + {% if dataspace.show_usage_policy_in_user_views %} + + {% endif %} + + + + + {% if dataspace.show_type_in_component_list_view %} + + {% endif %} + + {% endfor %} + {% empty %} + + {% endfor %} + +
    + {% if forloop.first and name_version_group|length > 1 %} + + {% endif %} + + + + {% if object.has_hierarchy or object.request_count or object.cpe %} +
      + {% if object.has_hierarchy %} +
    • + +
    • + {% endif %} + {% if request.user.is_authenticated and is_user_dataspace and object.request_count %} +
    • + R +
    • + {% endif %} + {% if object.cpe in vulnerable_cpes %} +
    • + {% include 'component_catalog/includes/vulnerability_icon_link.html' %} +
    • + {% endif %} +
    + {% endif %} + + {{ object.name }} + + {% if filter.show_created_date %} +
    Created {{ object.created_date|naturaltime_short }}
    + {% endif %} + {% if filter.show_last_modified_date %} +
    Modified {{ object.last_modified_date|naturaltime_short }}
    + {% endif %} +
    + {{ object.version }} + + {% if object.usage_policy %} + {{ object.get_usage_policy_icon_tooltip }} + {% endif %} + + {% if dataspace.show_usage_policy_in_user_views %} + {{ object.get_license_expression_linked_with_policy|default_if_none:"" }} + {% else %} + {{ object.license_expression_linked|default_if_none:"" }} + {% endif %} + + {{ object.primary_language }} + + {% if object.owner %} + {{ object.owner }} + {% endif %} + +
      + {% for keyword in object.keywords %} +
    • {{ keyword }}
    • + {% endfor %} +
    +
    + {{ object.type.label }} +
    No results.
    \ No newline at end of file diff --git a/component_catalog/templates/component_catalog/includes/hierarchy_instance_box.html b/component_catalog/templates/component_catalog/includes/hierarchy_instance_box.html new file mode 100644 index 00000000..0c08374b --- /dev/null +++ b/component_catalog/templates/component_catalog/includes/hierarchy_instance_box.html @@ -0,0 +1,67 @@ +{% load i18n %} +
    +
    +
      + {% if request.user.is_authenticated and is_user_dataspace and instance.request_count %} +
    • + {# {% if not is_current %} is required to properly trigger the tab switch #} + + R + +
    • + {% endif %} + {% if instance.dataspace.show_usage_policy_in_user_views and instance.usage_policy %} +
    • + {{ instance.get_usage_policy_as_icon }} +
    • + {% endif %} +
    + + + {% include 'includes/object_icon.html' with object=instance only %} + {% if is_current %} + {{ instance }} + {% elif relation.package_id %} + {{ instance }} + {% if has_edit_productpackage %} + + + + {% endif %} + {% elif instance.is_active or is_product %} + {{ instance }} + {% if relation.component_id and has_edit_productcomponent %} + + + + {% endif %} + {% elif not instance %} + + + + {% if relation.name %} + {{ relation.name }} {{ relation.version }} + {% else %} + (Component data missing) + {% endif %} + {% if has_edit_productcomponent %} + + + + {% endif %} + {% else %} + {{ instance }} + {% endif %} + + +
    + {% if subcomponent %} + {{ subcomponent.child.license_expression_linked|default_if_none:"" }} + {% elif relation %} + {{ relation.related_component_or_package.license_expression_linked|default_if_none:"" }} + {% else %} + {{ instance.license_expression_linked|default_if_none:"" }} + {% endif %} +
    +
    +
    \ No newline at end of file diff --git a/component_catalog/templates/component_catalog/includes/license_tags.html b/component_catalog/templates/component_catalog/includes/license_tags.html new file mode 100644 index 00000000..fd8a86a0 --- /dev/null +++ b/component_catalog/templates/component_catalog/includes/license_tags.html @@ -0,0 +1,20 @@ +{# 'License' tab in the Product/Component/Package details views #} +{% load as_icon from dje_tags %} +{% load i18n %} +{% if license %} +
      + {% for label, value, text in license.get_license_tab_displayed_tags %} + {% if value == True or value == False or include_unknown_value %} +
    • + {{ value|as_icon }} + {{ label }} +
    • + {% endif %} + {% endfor %} +
    + {% if license.is_active or license.is_component_license %} + Detailed license conditions + {% endif %} +{% else %} + No tag for this license. +{% endif %} \ No newline at end of file diff --git a/component_catalog/templates/component_catalog/includes/package_list_table.html b/component_catalog/templates/component_catalog/includes/package_list_table.html new file mode 100644 index 00000000..07888a20 --- /dev/null +++ b/component_catalog/templates/component_catalog/includes/package_list_table.html @@ -0,0 +1,91 @@ +{% load i18n %} +{% load inject_preserved_filters from dje_tags %} +{% load urlize_target_blank from dje_tags %} +{% load naturaltime_short from dje_tags %} + + {% if form or add_to_component_form %} + {% include 'includes/object_list_table_header.html' with include_checkbox=True checkbox_select_all=True %} + {% else %} + {% include 'includes/object_list_table_header.html' %} + {% endif %} + + {% for object in object_list %} + + {% if form or add_to_component_form %} + + {% endif %} + + {% if dataspace.show_usage_policy_in_user_views %} + + {% endif %} + + + + + + {% empty %} + + {% endfor %} + +
    + + +
      + {% if object.request_count and request.user.is_authenticated and is_user_dataspace %} +
    • + R +
    • + {% endif %} + {% if object.plain_package_url in vulnerable_purls %} +
    • + {% include 'component_catalog/includes/vulnerability_icon_link.html' %} +
    • + {% endif %} +
    + + {{ object }} + + {% if filter.show_created_date %} +
    Created {{ object.created_date|naturaltime_short }}
    + {% endif %} + {% if filter.show_last_modified_date %} +
    Modified {{ object.last_modified_date|naturaltime_short }}
    + {% endif %} +
    + {% if object.usage_policy %} + {{ object.get_usage_policy_icon_tooltip }} + {% endif %} + + {% if dataspace.show_usage_policy_in_user_views %} + {{ object.get_license_expression_linked_with_policy|default_if_none:"" }} + {% else %} + {{ object.license_expression_linked|default_if_none:"" }} + {% endif %} + + {{ object.primary_language }} + + {% if object.download_url %} + + {% if object.filename %}{{ object.filename }}{% else %}{{ object.download_url }}{% endif %} + + {% endif %} + + {% with components=object.component_set.all %} +
      + {% for component in components|slice:":8" %} +
    • + + {% if component.is_active %} + {{ component.get_absolute_link }} + {% else %} + {{ component }} + {% endif %} +
    • + {% endfor %} + {% if components|length > 8 %} +
    • ...
    • +
    • See all {{ components|length }} Components
    • + {% endif %} +
    + {% endwith %} +
    No results.
    \ No newline at end of file diff --git a/component_catalog/templates/component_catalog/includes/scan_delete_modal.html b/component_catalog/templates/component_catalog/includes/scan_delete_modal.html new file mode 100644 index 00000000..30f7d731 --- /dev/null +++ b/component_catalog/templates/component_catalog/includes/scan_delete_modal.html @@ -0,0 +1,22 @@ +{% load i18n %} + \ No newline at end of file diff --git a/component_catalog/templates/component_catalog/includes/scan_list_table.html b/component_catalog/templates/component_catalog/includes/scan_list_table.html new file mode 100644 index 00000000..86b579f3 --- /dev/null +++ b/component_catalog/templates/component_catalog/includes/scan_list_table.html @@ -0,0 +1,52 @@ +{% load i18n %} +{% load humanize %} + + + + + + + + + + {% for scan in scans %} + + + + {% empty %} + + {% endfor %} + +
    +
    + {% trans "Scans" %}: {{ paginator.count|intcomma }} results +
    +
    +
    +
    + {% if scan.package %} + + + {{ scan.package }} + + {% else %} + Package not found in DejaCode + {% endif %} +
    + + {% if scan.package %} + {{ scan.package.download_url }} + {% else %} + {{ scan.input_sources.0.source }} + {% endif %} + +
    +
    + Created {{ scan.created_date|naturaltime }} +
    +
    +
    + {% include 'component_catalog/includes/scan_progress.html' with scan=scan only %} +
    +
    +
    No results.
    \ No newline at end of file diff --git a/component_catalog/templates/component_catalog/includes/scan_matches_modal.html b/component_catalog/templates/component_catalog/includes/scan_matches_modal.html new file mode 100644 index 00000000..d2867083 --- /dev/null +++ b/component_catalog/templates/component_catalog/includes/scan_matches_modal.html @@ -0,0 +1,30 @@ + + + \ No newline at end of file diff --git a/component_catalog/templates/component_catalog/includes/scan_package_modal.html b/component_catalog/templates/component_catalog/includes/scan_package_modal.html new file mode 100644 index 00000000..c26e1113 --- /dev/null +++ b/component_catalog/templates/component_catalog/includes/scan_package_modal.html @@ -0,0 +1,23 @@ + \ No newline at end of file diff --git a/component_catalog/templates/component_catalog/includes/scan_progress.html b/component_catalog/templates/component_catalog/includes/scan_progress.html new file mode 100644 index 00000000..d609fc66 --- /dev/null +++ b/component_catalog/templates/component_catalog/includes/scan_progress.html @@ -0,0 +1,22 @@ +{{ scan.runs.0.status|title }} +{% include 'component_catalog/includes/scan_status.html' with status=scan.runs.0.status only %} + +{% if compact_mode %} +
    + {% if view_url %} +   + {% endif %} + {% if scan.download_result_url %} + + {% endif %} +
    +{% else %} +
    + {% if scan.download_result_url %} + Results + {% endif %} + {% if scan.delete_url %} + + {% endif %} +
    +{% endif %} \ No newline at end of file diff --git a/component_catalog/templates/component_catalog/includes/scan_status.html b/component_catalog/templates/component_catalog/includes/scan_status.html new file mode 100644 index 00000000..7b534220 --- /dev/null +++ b/component_catalog/templates/component_catalog/includes/scan_status.html @@ -0,0 +1,11 @@ +
    + {% if status == 'success' %} +
    + {% elif status == 'failure' or status == "stopped" or status == "stale" %} +
    + {% elif status == 'running' %} +
    + {% elif status == 'not_started' or status == 'queued' %} +
    + {% endif %} +
    \ No newline at end of file diff --git a/component_catalog/templates/component_catalog/includes/scan_summary_to_package_modal.html b/component_catalog/templates/component_catalog/includes/scan_summary_to_package_modal.html new file mode 100644 index 00000000..5c8436ae --- /dev/null +++ b/component_catalog/templates/component_catalog/includes/scan_summary_to_package_modal.html @@ -0,0 +1,55 @@ +{% load crispy_forms_tags %} + + + \ No newline at end of file diff --git a/component_catalog/templates/component_catalog/includes/scan_to_package_modal.html b/component_catalog/templates/component_catalog/includes/scan_to_package_modal.html new file mode 100644 index 00000000..fa7db3b8 --- /dev/null +++ b/component_catalog/templates/component_catalog/includes/scan_to_package_modal.html @@ -0,0 +1,26 @@ +{% load crispy_forms_tags %} + + + \ No newline at end of file diff --git a/component_catalog/templates/component_catalog/includes/vulnerability_icon_link.html b/component_catalog/templates/component_catalog/includes/vulnerability_icon_link.html new file mode 100644 index 00000000..e360ab29 --- /dev/null +++ b/component_catalog/templates/component_catalog/includes/vulnerability_icon_link.html @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/component_catalog/templates/component_catalog/package_details.html b/component_catalog/templates/component_catalog/package_details.html new file mode 100644 index 00000000..3b2234c7 --- /dev/null +++ b/component_catalog/templates/component_catalog/package_details.html @@ -0,0 +1,63 @@ +{% extends 'component_catalog/base_component_package_details.html' %} +{% load i18n static %} + +{% block pager-toolbar %} + {% if form or add_to_component_form %} +
    + +
    + {% endif %} + {% if user.is_authenticated %} + + {% endif %} + {% if has_add_package_permission %} +
    + + + +
    + {% endif %} + {{ block.super }} +{% endblock %} + +{% block content %} + {{ block.super }} + {% if has_add_package_permission %} + {% include 'component_catalog/includes/add_package_modal.html' %} + {% endif %} +{% endblock %} \ No newline at end of file diff --git a/component_catalog/templates/component_catalog/package_form.html b/component_catalog/templates/component_catalog/package_form.html new file mode 100644 index 00000000..f163cab3 --- /dev/null +++ b/component_catalog/templates/component_catalog/package_form.html @@ -0,0 +1,47 @@ +{% extends "object_form.html" %} + +{% block javascripts %} + {{ block.super }} + +{% endblock %} \ No newline at end of file diff --git a/component_catalog/templates/component_catalog/package_list.html b/component_catalog/templates/component_catalog/package_list.html new file mode 100644 index 00000000..f1eaeda1 --- /dev/null +++ b/component_catalog/templates/component_catalog/package_list.html @@ -0,0 +1,13 @@ +{% extends 'component_catalog/base_component_package_list.html' %} +{% load i18n %} + +{% block add_button %} + +{% endblock %} + +{% block content %} + {{ block.super }} + {% if has_add_package_permission %} + {% include 'component_catalog/includes/add_package_modal.html' %} + {% endif %} +{% endblock %} \ No newline at end of file diff --git a/component_catalog/templates/component_catalog/scan_list.html b/component_catalog/templates/component_catalog/scan_list.html new file mode 100644 index 00000000..fcf60104 --- /dev/null +++ b/component_catalog/templates/component_catalog/scan_list.html @@ -0,0 +1,45 @@ +{% extends 'object_list_base.html' %} +{% load i18n %} + +{% block page_title %}Scans{% endblock %} + +{% block top-right-buttons %} + {{ block.super }} + {% if has_add_package_permission %} +
    + +
    + {% endif %} +{% endblock %} + +{% block nav-list-head %} + + + +{% endblock %} + +{% block content %} + {{ block.super }} + {% if has_add_package_permission %} + {% include 'component_catalog/includes/add_package_modal.html' %} + {% endif %} + {% include 'component_catalog/includes/scan_delete_modal.html' %} +{% endblock %} + +{% block javascripts %} + {{ block.super }} + +{% endblock %} \ No newline at end of file diff --git a/component_catalog/templates/component_catalog/tabs/field_key_files_table.html b/component_catalog/templates/component_catalog/tabs/field_key_files_table.html new file mode 100644 index 00000000..3ca3175e --- /dev/null +++ b/component_catalog/templates/component_catalog/tabs/field_key_files_table.html @@ -0,0 +1,101 @@ +{% load static %} +{% if forloop.first %}
    {% endif %} +
    +
    + + + + + + + + + {% for key_file in values %} + + + + + {% endfor %} + +
    Key fileLicense expression
    + + + + {{ key_file.formatted_expression|safe }} + +
    + + +
    +{% if forloop.last %}
    {% endif %} + + + \ No newline at end of file diff --git a/component_catalog/templates/component_catalog/tabs/tab_hierarchy.html b/component_catalog/templates/component_catalog/tabs/tab_hierarchy.html new file mode 100644 index 00000000..755da460 --- /dev/null +++ b/component_catalog/templates/component_catalog/tabs/tab_hierarchy.html @@ -0,0 +1,26 @@ +
    + {% if values.related_parents or values.productcomponents %} +
    + {% for related_parent in values.related_parents %} + {% if forloop.first %}

    Parent components

    {% endif %} + {% include "component_catalog/includes/hierarchy_instance_box.html" with instance=related_parent.parent %} + {% endfor %} + {% for productcomponent in values.productcomponents %} + {% if forloop.first %}

    Products

    {% endif %} + {% include "component_catalog/includes/hierarchy_instance_box.html" with instance=productcomponent.product is_product=True %} + {% endfor %} +
    + {% endif %} +
    +

    Selected

    + {% include "component_catalog/includes/hierarchy_instance_box.html" with instance=object is_current=True %} +
    + {% if values.related_children %} +
    +

    Child components

    + {% for related_child in values.related_children %} + {% include "component_catalog/includes/hierarchy_instance_box.html" with instance=related_child.child subcomponent=related_child %} + {% endfor %} +
    + {% endif %} +
    \ No newline at end of file diff --git a/component_catalog/templates/component_catalog/tabs/tab_product_usage.html b/component_catalog/templates/component_catalog/tabs/tab_product_usage.html new file mode 100644 index 00000000..c6cd6560 --- /dev/null +++ b/component_catalog/templates/component_catalog/tabs/tab_product_usage.html @@ -0,0 +1,64 @@ +{% load i18n %} +{% load as_icon from dje_tags %} +{% spaceless %} + + + + + + + + + + + + + + + {% for productrelation in values %} + + + + + + + + + + + {% if productrelation.notes %} + + + + + {% endif %} + {% if productrelation.package_paths %} + + + + + {% endif %} + {% if productrelation.extra_attribution_text %} + + + + + {% endif %} + {% endfor %} + +
    {% trans 'Product name' %}{% trans 'Version' %}{% trans 'Owner' %}{% trans 'Purpose' %}{% trans 'License' %}{% trans 'Review status' %}{% trans 'Deployed' %}{% trans 'Modified' %}
    + + {{ productrelation.product.name }} + + {{ productrelation.product.version }}{{ productrelation.product.owner|default_if_none:"" }} + {% if productrelation.purpose %} +
    {{ productrelation.purpose.label_with_icon }}
    + {% endif %} +
    + {% if show_licenses_policy %} + {{ productrelation.get_license_expression_linked_with_policy|default_if_none:"" }} + {% else %} + {{ productrelation.license_expression_linked|default_if_none:"" }} + {% endif %} + {{ productrelation.review_status|default_if_none:'' }}{{ productrelation.is_deployed|as_icon }}{{ productrelation.is_modified|as_icon }}
     {% trans 'Notes' %}: {{ productrelation.notes }}
     {% trans 'Package paths' %}: {{ productrelation.package_paths }}
     {% trans 'Extra attribution text' %}: {{ productrelation.extra_attribution_text }}
    +{% endspaceless %} \ No newline at end of file diff --git a/component_catalog/templates/component_catalog/tabs/tab_scan.html b/component_catalog/templates/component_catalog/tabs/tab_scan.html new file mode 100644 index 00000000..a19f63dc --- /dev/null +++ b/component_catalog/templates/component_catalog/tabs/tab_scan.html @@ -0,0 +1,32 @@ +{% load static %} + +{% if show_scan_package_btn %} + +
    +
    +
    + +
    +
    +
    + {% include 'component_catalog/includes/scan_package_modal.html' %} + + +{% else %} + {% include 'tabs/tab_content.html' %} + {% if object.has_license_matches %} + {% include 'component_catalog/includes/scan_matches_modal.html' %} + {% endif %} + {% if scan_to_package_form %} + {% include 'component_catalog/includes/scan_to_package_modal.html' %} + {% endif %} + {% if scan_summary_to_package_form %} + {% include 'component_catalog/includes/scan_summary_to_package_modal.html' %} + {% endif %} +{% endif %} \ No newline at end of file diff --git a/component_catalog/templates/component_catalog/tabs/tab_subcomponents.html b/component_catalog/templates/component_catalog/tabs/tab_subcomponents.html new file mode 100644 index 00000000..389d6401 --- /dev/null +++ b/component_catalog/templates/component_catalog/tabs/tab_subcomponents.html @@ -0,0 +1,93 @@ +{% load i18n %} +{% load as_icon from dje_tags %} +{% spaceless %} + + + + + + + + + {% if values and 'usage_policy' in values.0.keys %} + + {% endif %} + + + + + + + {% for data in values %} + + + + + + + {% if data.usage_policy %} + + {% endif %} + + + + + {% if data.subcomponent.reference_notes %} + + + + + {% endif %} + {% if data.subcomponent.package_paths %} + + + + + {% endif %} + {% if data.subcomponent.extra_attribution_text %} + + + + + {% endif %} + {% if data.subcomponent.notes %} + + + + + {% endif %} + {% endfor %} + +
    + {% trans 'Name' %} +   + {% trans 'Version' %}{% trans 'Owner' %}{% trans 'Purpose' %}{% trans 'License' %}{% trans 'Subcomponent usage policy' %}{% trans 'Deployed' %}{% trans 'Modified' %} 
    + {% if 'usage_policy' in data and data.child.usage_policy %} + {{ data.child.get_usage_policy_as_icon }} + {% endif %} + {% if data.child.is_active %} + {{ data.child.name }} + {% else %} + {{ data.child.name }} + {% endif %} + {{ data.child.version }}{{ data.child.owner }}{{ data.subcomponent.purpose }} + {% if data.subcomponent.license_expression %} + {{ data.subcomponent.license_expression_linked }} + {% else %} +   + {% endif %} + {{ data.usage_policy }}{{ data.subcomponent.is_deployed|as_icon }}{{ data.subcomponent.is_modified|as_icon }} + {% if component.is_active %} +
      +
    • + +
    • + {% if request.user.is_authenticated and is_user_dataspace and data.child.request_count %} +
    • + R +
    • + {% endif %} +
    + {% endif %} +
    - {% trans 'Reference notes' %}:{{ data.subcomponent.reference_notes }}
    - {% trans 'Package paths' %}:{{ data.subcomponent.package_paths }}
    - {% trans 'Extra attribution text' %}:{{ data.subcomponent.extra_attribution_text }}
    - {% trans 'Notes' %}:{{ data.subcomponent.notes }}
    +{% endspaceless %} \ No newline at end of file diff --git a/component_catalog/tests/__init__.py b/component_catalog/tests/__init__.py new file mode 100644 index 00000000..7aa35de2 --- /dev/null +++ b/component_catalog/tests/__init__.py @@ -0,0 +1,7 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# DejaCode is a trademark of nexB Inc. +# SPDX-License-Identifier: AGPL-3.0-only +# See https://github.com/nexB/dejacode for support or download. +# See https://aboutcode.org for more information about AboutCode FOSS projects. +# diff --git a/component_catalog/tests/test_admin.py b/component_catalog/tests/test_admin.py new file mode 100644 index 00000000..e2c1abd2 --- /dev/null +++ b/component_catalog/tests/test_admin.py @@ -0,0 +1,3061 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# DejaCode is a trademark of nexB Inc. +# SPDX-License-Identifier: AGPL-3.0-only +# See https://github.com/nexB/dejacode for support or download. +# See https://aboutcode.org for more information about AboutCode FOSS projects. +# + +import datetime +import json +import urllib +import uuid +from unittest import mock + +from django.contrib.admin.options import IS_POPUP_VAR +from django.contrib.auth import get_user_model +from django.contrib.contenttypes.models import ContentType +from django.core.exceptions import NON_FIELD_ERRORS +from django.test import TestCase +from django.test import TransactionTestCase +from django.test.utils import override_settings +from django.urls import NoReverseMatch +from django.urls import reverse + +from guardian.shortcuts import assign_perm + +from component_catalog.admin import ComponentAdmin +from component_catalog.models import AcceptableLinkage +from component_catalog.models import Component +from component_catalog.models import ComponentAssignedPackage +from component_catalog.models import ComponentKeyword +from component_catalog.models import ComponentStatus +from component_catalog.models import ComponentType +from component_catalog.models import Package +from component_catalog.models import PackageAssignedLicense +from component_catalog.models import Subcomponent +from dje.copier import copy_object +from dje.filters import DataspaceFilter +from dje.models import Dataspace +from dje.models import ExternalReference +from dje.models import ExternalSource +from dje.models import History +from dje.tests import add_perm +from dje.tests import create_admin +from dje.tests import create_superuser +from license_library.models import License +from license_library.models import LicenseAssignedTag +from license_library.models import LicenseTag +from organization.models import Owner +from policy.models import AssociatedPolicy +from policy.models import UsagePolicy +from product_portfolio.models import Product +from product_portfolio.models import ProductComponent +from product_portfolio.models import ProductPackage + + +class ComponentAdminViewsTestCase(TestCase): + def setUp(self): + self.dataspace1 = Dataspace.objects.create(name="Dataspace") + self.alternate_dataspace = Dataspace.objects.create(name="Alternate") + + self.user = get_user_model().objects.create_superuser( + "test", "test@test.com", "secret", self.dataspace1 + ) + self.admin_user = create_admin("admin_user", self.dataspace1) + self.alternate_super_user = create_superuser("alternate_user", self.alternate_dataspace) + + self.type1 = ComponentType.objects.create( + label="Type1", notes="notes", dataspace=self.dataspace1 + ) + self.status1 = ComponentStatus.objects.create( + label="Status1", default_on_addition=False, dataspace=self.dataspace1 + ) + self.owner1 = Owner.objects.create( + name="owner1", + dataspace=self.dataspace1, + ) + self.license1 = License.objects.create( + key="license1", + name="License1", + short_name="License1", + is_active=True, + owner=self.owner1, + dataspace=self.dataspace1, + ) + self.license2 = License.objects.create( + key="license2", + name="License2", + short_name="License2", + is_active=True, + owner=self.owner1, + dataspace=self.dataspace1, + ) + self.component1 = Component.objects.create( + name="Component1", + version="0.1", + type=self.type1, + owner=self.owner1, + homepage_url="http://localhost.com", + dataspace=self.dataspace1, + ) + self.component2 = Component.objects.create( + name="Component2", + version="0.2", + type=self.type1, + owner=self.owner1, + homepage_url="http://localhost.com", + dataspace=self.dataspace1, + ) + self.component3 = Component.objects.create( + name="Component3", + version="r1", + type=self.type1, + owner=self.owner1, + dataspace=self.dataspace1, + ) + self.component4 = Component.objects.create( + name="Component4", + version="r3", + type=self.type1, + owner=self.owner1, + dataspace=self.dataspace1, + ) + self.component5 = Component.objects.create( + name="Component5", + version="r4", + type=self.type1, + owner=self.owner1, + dataspace=self.dataspace1, + ) + self.component6 = Component.objects.create( + name="Component6", + version="r5", + type=self.type1, + owner=self.owner1, + dataspace=self.dataspace1, + ) + + self.sub_component1 = Subcomponent.objects.create( + parent=self.component6, + child=self.component5, + dataspace=self.component6.dataspace, + notes="I have a parent 6, and child 5", + ) + self.sub_component2 = Subcomponent.objects.create( + parent=self.component5, + child=self.component4, + dataspace=self.component5.dataspace, + notes="I have a parent 5, and child 4", + ) + self.sub_component3 = Subcomponent.objects.create( + parent=self.component4, + child=self.component3, + dataspace=self.component4.dataspace, + notes="I have a parent 4, and child 3", + ) + self.sub_component4 = Subcomponent.objects.create( + parent=self.component3, + child=self.component2, + dataspace=self.component3.dataspace, + notes="I have a parent 3, and child 2", + ) + self.sub_component5 = Subcomponent.objects.create( + parent=self.component2, + child=self.component1, + dataspace=self.component2.dataspace, + notes="I have a parent 2, and child 1", + ) + self.dataspace_target = Dataspace.objects.create(name="target_org") + + self.product1 = Product.objects.create( + name="MyProduct", version="1.0", dataspace=self.dataspace1 + ) + + def test_component_admin_views(self): + self.client.login(username="test", password="secret") + # List view + url = reverse("admin:component_catalog_component_changelist") + response = self.client.get(url) + self.assertContains(response, "Component1") + # View on site link + self.assertContains(response, '
    View
    ') + self.assertContains( + response, + 'View'.format(self.component1.get_absolute_url()), + ) + # Details view + url = self.component1.get_admin_url() + response = self.client.get(url) + # Check if the Inlines are present + self.assertContains(response, '

    Child Components

    ') + self.assertContains( + response, + 'View'.format( + self.component1.get_absolute_url() + ), + ) + + def test_component_admin_form_clean(self): + self.client.login(username="test", password="secret") + url = self.component1.get_admin_url() + + # Using the name, organization and version from component2, to make + # sure POSTing this is raising the proper error and message. + data = { + "name": self.component2.name, + "version": self.component2.version, + "type": self.type1.id, + "curation_level": 0, + "related_children-INITIAL_FORMS": 0, + "related_children-TOTAL_FORMS": 0, + "componentassignedpackage_set-TOTAL_FORMS": 0, + "componentassignedpackage_set-INITIAL_FORMS": 0, + "dje-externalreference-content_type-object_id-TOTAL_FORMS": 0, + "dje-externalreference-content_type-object_id-INITIAL_FORMS": 0, + } + + response = self.client.post(url, data) + self.assertContains(response, '

    Please correct the error below.

    ') + self.assertContains( + response, "Component with this Dataspace, Name and Version already exists." + ) + expected = { + NON_FIELD_ERRORS: ["Component with this Dataspace, Name and Version already exists."] + } + self.assertEqual(expected, response.context_data["adminform"].form.errors) + + # Removing one of the required field and POST again + del data["name"] + response = self.client.post(url, data) + self.assertContains(response, '

    Please correct the error below.

    ') + self.assertContains(response, '
    • This field is required.
    • ') + + @override_settings(REFERENCE_DATASPACE="Dataspace") + def test_component_admin_form_clean_validate_against_reference_data(self): + self.client.login(username=self.alternate_super_user, password="secret") + url = reverse("admin:component_catalog_component_add") + + data = { + "name": self.component1.name, + "version": self.component1.version, + "curation_level": 0, + "related_children-INITIAL_FORMS": 0, + "related_children-TOTAL_FORMS": 0, + "componentassignedpackage_set-TOTAL_FORMS": 0, + "componentassignedpackage_set-INITIAL_FORMS": 0, + "dje-externalreference-content_type-object_id-TOTAL_FORMS": 0, + "dje-externalreference-content_type-object_id-INITIAL_FORMS": 0, + } + + response = self.client.post(url, data) + + absolute_link = self.component1.get_absolute_link(target="_blank") + copy_link = self.component1.get_html_link( + self.component1.get_copy_url(), value="Copy to my Dataspace", target="_blank" + ) + error = ( + f"The application object that you are creating already exists as " + f"{absolute_link} in the reference dataspace. {copy_link}" + ) + + expected = { + "version": [error], + "name": [error], + } + self.assertEqual(expected, response.context_data["adminform"].form.errors) + + @override_settings(REFERENCE_DATASPACE="Dataspace") + def test_component_admin_changelist_reference_data_link(self): + url = reverse("admin:component_catalog_component_changelist") + + reference_params = "?{}={}".format(DataspaceFilter.parameter_name, self.dataspace1.id) + reference_link = 'View Reference Data'.format( + reference_params + ) + my_dataspace_link = 'View My Data' + + self.client.login(username=self.alternate_super_user, password="secret") + response = self.client.get(url) + self.assertContains(response, reference_link) + self.assertNotContains(response, my_dataspace_link) + self.assertFalse(getattr(response.context_data["cl"], "my_dataspace_link", None)) + self.assertEqual( + reference_params, getattr(response.context_data["cl"], "reference_params", None) + ) + + # Not displayed in popup mode + response = self.client.get("{}?{}=1".format(url, IS_POPUP_VAR)) + self.assertNotContains(response, reference_link) + self.assertNotContains(response, my_dataspace_link) + self.assertFalse(getattr(response.context_data["cl"], "my_dataspace_link", None)) + self.assertFalse(getattr(response.context_data["cl"], "reference_params", None)) + + response = self.client.get("{}{}".format(url, reference_params)) + self.assertNotContains(response, reference_link) + self.assertContains(response, my_dataspace_link) + self.assertTrue(getattr(response.context_data["cl"], "my_dataspace_link", None)) + self.assertFalse(getattr(response.context_data["cl"], "reference_params", None)) + + with override_settings(REFERENCE_DATASPACE=""): + response = self.client.get(url) + self.assertNotContains(response, reference_link) + self.assertNotContains(response, my_dataspace_link) + + self.client.login(username=self.user.username, password="secret") + response = self.client.get(url) + self.assertNotContains(response, reference_link) + self.assertNotContains(response, my_dataspace_link) + + url = reverse("admin:component_catalog_subcomponent_changelist") + self.client.login(username=self.alternate_super_user, password="secret") + response = self.client.get("{}{}".format(url, reference_params)) + self.assertNotContains(response, "copy_to") + + def test_subcomponent_inline_admin_form_edit_clean(self): + # This is an EDITION case + self.client.login(username="test", password="secret") + url = self.component1.get_admin_url() + + dataspace = Dataspace.objects.create(name="Other") + other_org = Owner.objects.create(name="Other Org", dataspace=dataspace) + other_type = ComponentType.objects.create( + label="Other Type", notes="notes", dataspace=dataspace + ) + other_component = Component.objects.create( + name="Component1", version="0.1", type=other_type, owner=other_org, dataspace=dataspace + ) + + data = { + "related_children-INITIAL_FORMS": 0, + "related_children-TOTAL_FORMS": 1, + "related_children-0-notes": "Notes", + "related_children-0-parent": self.component1.id, + "related_children-0-child": other_component.id, + "componentassignedpackage_set-TOTAL_FORMS": 0, + "componentassignedpackage_set-INITIAL_FORMS": 0, + "dje-externalreference-content_type-object_id-TOTAL_FORMS": 0, + "dje-externalreference-content_type-object_id-INITIAL_FORMS": 0, + } + + response = self.client.post(url, data) + error = "Select a valid choice. That choice is not one of the available choices." + self.assertContains(response, error) + + other_component.name = "New Component Name" + other_component.dataspace = self.component1.dataspace + other_component.owner = self.component1.owner + other_component.type = self.component1.type + other_component.save() + + response = self.client.post(url, data) + self.assertNotContains(response, error) + + def test_component_available_actions(self): + delete_input = '' + compare_input = '' + copy_input = '' + add_to_product_input = ( + '' + ) + set_policy = '' + + self.client.login(username="test", password="secret") + url = reverse("admin:component_catalog_component_changelist") + + # Looking at my own Dataspace data (default) + response = self.client.get(url) + self.assertContains(response, delete_input) + self.assertNotContains(response, compare_input) + self.assertNotContains(response, copy_input) + self.assertContains(response, add_to_product_input) + self.assertContains(response, set_policy) + + # Looking at data from another Dataspace + data = {DataspaceFilter.parameter_name: self.alternate_dataspace.id} + response = self.client.get(url, data) + self.assertNotContains(response, delete_input) + self.assertContains(response, compare_input) + self.assertContains(response, copy_input) + self.assertNotContains(response, add_to_product_input) + self.assertNotContains(response, set_policy) + + def test_edit_component_assigned_package_replacement(self): + self.client.login(username="test", password="secret") + url = self.component1.get_admin_url() + + # Making sure we have unicode char in the name + package1 = Package.objects.create( + filename="\u02A0package1.zip", dataspace=self.component1.dataspace + ) + assigned_package1 = ComponentAssignedPackage.objects.create( + component=self.component1, package=package1, dataspace=self.component1.dataspace + ) + + params = { + "name": self.component1.name, + "curation_level": self.component1.curation_level, + "related_children-INITIAL_FORMS": 0, + "related_children-TOTAL_FORMS": 0, + "dje-externalreference-content_type-object_id-TOTAL_FORMS": "0", + "dje-externalreference-content_type-object_id-INITIAL_FORMS": "0", + "componentassignedpackage_set-TOTAL_FORMS": 1, + "componentassignedpackage_set-INITIAL_FORMS": 1, + "componentassignedpackage_set-0-id": assigned_package1.id, + "componentassignedpackage_set-0-component": self.component1.id, + "componentassignedpackage_set-0-package": package1.id, + } + + response = self.client.post(url, params, follow=True) + self.assertRedirects(response, reverse("admin:component_catalog_component_changelist")) + component = Component.objects.get(pk=self.component1.pk) + # Save went through, nothing changed so far. + self.assertEqual(package1, component.componentassignedpackage_set.get().package) + + # Replacing the existing file by a another one + package2 = Package.objects.create( + filename="\u02A0package2.zip", dataspace=self.component1.dataspace + ) + params["componentassignedpackage_set-0-package"] = package2.id + self.client.post(url, params) + component.refresh_from_db() + self.assertEqual(package2, component.componentassignedpackage_set.get().package) + + def test_component_admin_form_inline_formsets_data_tampered(self): + self.client.login(username="test", password="secret") + url = self.component1.get_admin_url() + + package1 = Package.objects.create(filename="p1.zip", dataspace=self.component1.dataspace) + assigned_package1 = ComponentAssignedPackage.objects.create( + component=self.component1, package=package1, dataspace=self.component1.dataspace + ) + + response = self.client.get(url) + self.assertContains(response, "componentassignedpackage_set-0") + self.assertContains( + response, + '', + html=True, + ) + self.assertContains( + response, + '', + html=True, + ) + + # Calling the changeform view, componentassignedpackage_set-0 are properly set + data = { + "name": self.component1.name, + "curation_level": 0, + "related_children-INITIAL_FORMS": 0, + "related_children-TOTAL_FORMS": 0, + "dje-externalreference-content_type-object_id-TOTAL_FORMS": "0", + "dje-externalreference-content_type-object_id-INITIAL_FORMS": "0", + "componentassignedpackage_set-TOTAL_FORMS": 1, + "componentassignedpackage_set-INITIAL_FORMS": 1, + "componentassignedpackage_set-0-id": assigned_package1.id, + "componentassignedpackage_set-0-component": self.component1.id, + "componentassignedpackage_set-0-package": package1.id, + } + + # Deletes the assigned_package1 relation, so the componentassignedpackage_set-0 are + # not valid anymore. + assigned_package1.delete() + + response = self.client.post(url, data, follow=True) + self.assertRedirects(response, url) + self.assertNotContains(response, "componentassignedpackage_set-0") + + self.assertContains( + response, + '', + html=True, + ) + self.assertContains( + response, + '', + html=True, + ) + self.assertContains(response, "Form data outdated or inconsistent.") + self.assertContains(response, "The form data has been refreshed.") + + messages = list(response.context["messages"]) + expected = "Form data outdated or inconsistent. The form data has been refreshed." + self.assertEqual(expected, str(messages[0])) + + def test_activity_log_activated(self): + self.client.login(username="test", password="secret") + response = self.client.get(reverse("admin:component_catalog_component_changelist")) + self.assertContains(response, "activity_log_link") + + def test_component_admin_changelist_list_display_as_popup(self): + self.client.login(username="test", password="secret") + url = reverse("admin:component_catalog_component_changelist") + expect1 = '' + expect2 = "get_hierarchy_link" + expect3 = "View" + expect4 = '' + expect5 = 'title="Hierarchy"' + expect6 = "view_on_site" + + response = self.client.get(url) + self.assertContains(response, expect1) + self.assertContains(response, expect2) + self.assertContains(response, expect3) + self.assertContains(response, expect4) + self.assertContains(response, expect5) + self.assertContains(response, expect6) + + response = self.client.get(url + "?{}=1".format(IS_POPUP_VAR)) + self.assertNotContains(response, expect1) + self.assertNotContains(response, expect2) + self.assertNotContains(response, expect3) + self.assertNotContains(response, expect4) + self.assertNotContains(response, expect5) + self.assertNotContains(response, expect6) + + def test_component_unicity_in_form_validation_on_addition(self): + # Let's take an existing Component an try to save a new one using the + # unique_together value from the existing one. + self.client.login(username="test", password="secret") + url = reverse("admin:component_catalog_component_add") + data = { + "name": self.component1.name, + "owner": self.component1.owner.id, + "version": self.component1.version, + "curation_level": 0, + "related_children-INITIAL_FORMS": 0, + "related_children-TOTAL_FORMS": 0, + "componentassignedpackage_set-TOTAL_FORMS": 0, + "componentassignedpackage_set-INITIAL_FORMS": 0, + "dje-externalreference-content_type-object_id-TOTAL_FORMS": 0, + "dje-externalreference-content_type-object_id-INITIAL_FORMS": 0, + } + + response = self.client.post(url, data) + # Error a the Field level in the Form + self.assertContains(response, "Please correct the error below.") + # Global Formset error + self.assertContains( + response, "Component with this Dataspace, Name and Version already exists." + ) + expected = { + NON_FIELD_ERRORS: ["Component with this Dataspace, Name and Version already exists."] + } + self.assertEqual(expected, response.context_data["adminform"].form.errors) + + def test_component_admin_add_is_active_true_by_default(self): + self.client.login(username="test", password="secret") + url = reverse("admin:component_catalog_component_add") + data = { + "name": "new component", + "curation_level": 0, + "related_children-INITIAL_FORMS": 0, + "related_children-TOTAL_FORMS": 0, + "componentassignedpackage_set-TOTAL_FORMS": 0, + "componentassignedpackage_set-INITIAL_FORMS": 0, + "dje-externalreference-content_type-object_id-TOTAL_FORMS": 0, + "dje-externalreference-content_type-object_id-INITIAL_FORMS": 0, + } + self.client.post(url, data) + component = Component.objects.get(name=data["name"]) + self.assertTrue(component.is_active) + + def test_component_adminform_validate_case_insensitive_uniqueness(self): + self.client.login(username="test", password="secret") + url = reverse("admin:component_catalog_component_add") + data = { + "name": self.component1.name.upper(), + "curation_level": 0, + "related_children-INITIAL_FORMS": 0, + "related_children-TOTAL_FORMS": 0, + "componentassignedpackage_set-TOTAL_FORMS": 0, + "componentassignedpackage_set-INITIAL_FORMS": 0, + "dje-externalreference-content_type-object_id-TOTAL_FORMS": 0, + "dje-externalreference-content_type-object_id-INITIAL_FORMS": 0, + } + + response = self.client.post(url, data) + expected = { + "name": [ + "The application object that you are creating already exists as " + '"Component1". Note that a different case in the object name is not ' + "sufficient to make it unique." + ], + } + self.assertEqual(expected, response.context_data["adminform"].form.errors) + + url = self.component2.get_admin_url() + response = self.client.post(url, data) + self.assertEqual(302, response.status_code) + + data["name"] = self.component1.name + response = self.client.post(url, data) + self.assertEqual(302, response.status_code) + + url = self.component1.get_admin_url() + data["version"] = self.component1.version + response = self.client.post(url, data) + self.assertEqual(302, response.status_code) + + def test_component_adminform_validate_version(self): + self.client.login(username="test", password="secret") + url = reverse("admin:component_catalog_component_add") + data = { + "name": "c:o:m:p", + "version": "1:0", + "curation_level": 0, + "related_children-INITIAL_FORMS": 0, + "related_children-TOTAL_FORMS": 0, + "componentassignedpackage_set-TOTAL_FORMS": 0, + "componentassignedpackage_set-INITIAL_FORMS": 0, + "dje-externalreference-content_type-object_id-TOTAL_FORMS": 0, + "dje-externalreference-content_type-object_id-INITIAL_FORMS": 0, + } + + response = self.client.post(url, data) + expected = { + "version": [ + "Enter a valid value consisting of spaces, periods, letters, numbers, " + "or !#\"',&()+_-." + ] + } + self.assertEqual(expected, response.context_data["adminform"].form.errors) + + def test_component_curation_level_validation(self): + self.client.login(username="test", password="secret") + url = reverse("admin:component_catalog_component_add") + data = { + "name": "some name", + "curation_level": 101, # Using a value greater than the max + "related_children-INITIAL_FORMS": 0, + "related_children-TOTAL_FORMS": 0, + "componentassignedpackage_set-TOTAL_FORMS": 0, + "componentassignedpackage_set-INITIAL_FORMS": 0, + "dje-externalreference-content_type-object_id-TOTAL_FORMS": 0, + "dje-externalreference-content_type-object_id-INITIAL_FORMS": 0, + } + + response = self.client.post(url, data) + self.assertContains(response, "Ensure this value is less than or equal to 100.") + + data["curation_level"] = 99 + response = self.client.post(url, data) + self.assertRedirects(response, reverse("admin:component_catalog_component_changelist")) + + def test_component_save_as_proper(self): + # Setting some value for further validation + self.component1.configuration_status = self.status1 + self.component1.save() + + self.client.login(username="test", password="secret") + url = self.component1.get_admin_url() + response = self.client.get(url) + # Save as button is present on the Edit page + save_as_input_html = ( + '' + ) + self.assertContains(response, save_as_input_html, html=True) + + # We need to set a different Component.name to avoid raising validation + # errors + new_name = "THIS IS A NEW NAME" + data = { + "name": new_name, + "owner": self.component1.owner.id, + "type": self.component1.type.id, + "curation_level": 0, + "configuration_status": self.component1.configuration_status.id, + "_saveasnew": "Save as new", + "related_children-INITIAL_FORMS": 0, + "related_children-TOTAL_FORMS": 0, + "componentassignedpackage_set-TOTAL_FORMS": 0, + "componentassignedpackage_set-INITIAL_FORMS": 0, + "dje-externalreference-content_type-object_id-TOTAL_FORMS": 0, + "dje-externalreference-content_type-object_id-INITIAL_FORMS": 0, + } + response = self.client.post(url, data) + # A new Component was created + new_component = Component.objects.get(name=new_name) + self.assertRedirects(response, new_component.get_admin_url()) + # Making sure the values of the Original Component were preserved + self.assertEqual(self.component1.owner, new_component.owner) + self.assertEqual(self.component1.type, new_component.type) + self.assertEqual(self.component1.is_active, new_component.is_active) + self.assertEqual(self.component1.configuration_status, new_component.configuration_status) + + def test_component_save_as_with_failing_validation(self): + self.client.login(username="test", password="secret") + url = self.component1.get_admin_url() + response = self.client.get(url) + self.assertContains(response, 'name="_saveasnew"') + # Since request.GET is empty + self.assertNotContains(response, 'value="Save and go to next"') + + response = self.client.get(url + "?_changelist_filters=o%3D3.4") + self.assertContains(response, 'name="_saveasnew"') + self.assertContains(response, 'value="Save and go to next"') + + data = { + "name": self.component1.name, + "owner": self.component1.owner.id, + "version": self.component1.version, + "type": self.component1.type.id, + "curation_level": 0, + "_saveasnew": "Save as new", + "related_children-INITIAL_FORMS": 0, + "related_children-TOTAL_FORMS": 0, + "componentassignedpackage_set-TOTAL_FORMS": 0, + "componentassignedpackage_set-INITIAL_FORMS": 0, + "dje-externalreference-content_type-object_id-TOTAL_FORMS": 0, + "dje-externalreference-content_type-object_id-INITIAL_FORMS": 0, + } + # Save as new with no data modification, raise a form validation error + response = self.client.post(url, data) + context_data = response.context_data + self.assertContains( + response, "Component with this Dataspace, Name and Version already exists." + ) + expected = { + NON_FIELD_ERRORS: ["Component with this Dataspace, Name and Version already exists."] + } + self.assertEqual(expected, context_data["adminform"].form.errors) + # We are now on a form that works like an ADDITION form + # Save as button is the only button present + self.assertContains(response, 'name="_saveasnew"') + self.assertNotContains(response, 'value="Save and go to next"') + self.assertEqual(context_data["save_as"], True) + self.assertEqual(context_data["add"], False) + self.assertEqual(context_data["change"], True) + + def test_component_save_as_with_external_reference(self): + self.client.login(username="test", password="secret") + url = self.component1.get_admin_url() + + ext_source1 = ExternalSource.objects.create( + label="GitHub", + dataspace=self.dataspace1, + ) + + er = ExternalReference.objects.create_for_content_object( + content_object=self.component1, external_source=ext_source1, external_id="external_id" + ) + + # We need to set a different Component.name to avoid raising validation errors + new_name = "THIS IS A NEW NAME" + data = { + "name": new_name, + "curation_level": 0, + "_saveasnew": "Save as new", + "related_children-INITIAL_FORMS": 0, + "related_children-TOTAL_FORMS": 0, + "componentassignedpackage_set-TOTAL_FORMS": 0, + "componentassignedpackage_set-INITIAL_FORMS": 0, + "dje-externalreference-content_type-object_id-TOTAL_FORMS": 1, + "dje-externalreference-content_type-object_id-INITIAL_FORMS": 1, + "dje-externalreference-content_type-object_id-0-id": er.id, + "dje-externalreference-content_type-object_id-0-external_source": er.external_source.id, + "dje-externalreference-content_type-object_id-0-external_id": er.external_id, + } + + response = self.client.post(url, data) + # A new Component was created + new_component = Component.objects.get(name=new_name) + self.assertRedirects(response, new_component.get_admin_url()) + # Making sure the values of the Original Component were preserved + self.assertEqual(self.component1.owner, self.owner1) + + er1 = ExternalReference.objects.get_for_content_object(self.component1).get() + er2 = ExternalReference.objects.get_for_content_object(new_component).get() + # Making sure a new ExternalReference was created + self.assertNotEqual(er1.id, er2.id) + + def test_component_edit_save_as_delete_inline(self): + self.client.login(username="test", password="secret") + url = self.component6.get_admin_url() + + extra_subcomponent = Subcomponent.objects.create( + parent=self.component6, child=self.component1, dataspace=self.component6.dataspace + ) + + self.assertEqual(2, self.component6.related_children.count()) + + new_name = "THIS IS A NEW NAME" + data = { + "name": new_name, + "curation_level": 0, + "_saveasnew": "Save+as+new", + "related_children-0-id": self.sub_component1.pk, + "related_children-0-DELETE": "1", + "related_children-1-id": extra_subcomponent.pk, + "related_children-1-child": extra_subcomponent.child.pk, + "related_children-1-parent": extra_subcomponent.parent.pk, + "related_children-INITIAL_FORMS": "2", + "related_children-TOTAL_FORMS": "2", + "componentassignedpackage_set-TOTAL_FORMS": 0, + "componentassignedpackage_set-INITIAL_FORMS": 0, + "dje-externalreference-content_type-object_id-TOTAL_FORMS": 0, + "dje-externalreference-content_type-object_id-INITIAL_FORMS": 0, + } + + response = self.client.post(url, data) + self.assertEqual(response.status_code, 302) + # Started with 2 children, one was deleted. + self.assertEqual(Component.objects.latest("id").related_children.count(), 1) + + @override_settings(REFERENCE_DATASPACE="Dataspace") + def test_component_save_as_deactivated_for_dataspace(self): + # The 'Save as' button is not available when editing an object from + # another Dataspace + dataspace = Dataspace.objects.create(name="Other") + other_org = Owner.objects.create(name="Other Org", dataspace=dataspace) + other_type = ComponentType.objects.create( + label="Other Type", notes="notes", dataspace=dataspace + ) + other_component = Component.objects.create( + name="Component1", version="0.1", type=other_type, owner=other_org, dataspace=dataspace + ) + + self.client.login(username="test", password="secret") + url = other_component.get_admin_url() + response = self.client.get(url) + # 'Save as' is not exposed in this case. + self.assertNotContains(response, 'name="_saveasnew"') + self.assertContains(response, "_addanother") + + def test_component_save_as_with_inlines(self): + self.client.login(username="test", password="secret") + + package1 = Package.objects.create(filename="p1.zip", dataspace=self.dataspace1) + assigned_package = ComponentAssignedPackage.objects.create( + component=self.component1, package=package1, dataspace=self.dataspace1 + ) + + package_count = Package.objects.count() + assigned_package_count = ComponentAssignedPackage.objects.count() + + self.component1.license_expression = self.license1.key + self.component1.save() + + url = self.component1.get_admin_url() + response = self.client.get(url) # Make a GET request to get the csrf token + + # We need to set a different Component.name to avoid raising validation errors + new_name = "THIS IS A NEW NAME" + data = { + "csrfmiddlewaretoken": str(response.context["csrf_token"]), + "name": new_name, + "version": self.component1.version, + "curation_level": 0, + "license_expression": self.component1.license_expression, + "_saveasnew": "Save as new", + "related_children-INITIAL_FORMS": 0, + "related_children-TOTAL_FORMS": 0, + "componentassignedpackage_set-TOTAL_FORMS": 1, + "componentassignedpackage_set-INITIAL_FORMS": 1, + "componentassignedpackage_set-0-package": assigned_package.package.id, + "componentassignedpackage_set-0-component": assigned_package.component.id, + "componentassignedpackage_set-0-id": assigned_package.id, + "dje-externalreference-content_type-object_id-TOTAL_FORMS": 0, + "dje-externalreference-content_type-object_id-INITIAL_FORMS": 0, + } + response = self.client.post(url, data) + # print response.context_data['adminform'].form.errors + self.assertEqual(302, response.status_code) + + new_component = Component.objects.get(name=new_name, dataspace=self.dataspace1) + self.assertRedirects(response, new_component.get_admin_url()) + self.assertEqual(1, new_component.packages.count()) + self.assertEqual(package_count, Package.objects.count()) + self.assertEqual(assigned_package_count + 1, ComponentAssignedPackage.objects.count()) + # The assigned license was copied too + self.assertEqual(1, new_component.licenses.count()) + self.assertEqual(self.component1.license_expression, new_component.license_expression) + + def test_add_to_product_action_proper_component(self): + self.client.login(username=self.user.username, password="secret") + + self.component1.license_expression = self.license1.key + self.component1.save() + + ids = ",".join([str(self.component1.pk), str(self.component2.pk), str(self.component3.pk)]) + add_to_product_url = reverse("admin:component_catalog_component_add_to_product") + + # Simulate displaying the page + response = self.client.get("{}?ids={}".format(add_to_product_url, ids)) + self.assertEqual(200, response.status_code) + self.assertTrue("form" in response.context) + + response = self.client.post( + add_to_product_url, {"product": self.product1.pk, "ids": ids}, follow=True + ) + self.assertRedirects(response, reverse("admin:component_catalog_component_changelist")) + self.assertEqual(3, self.product1.productcomponents.count()) + + # pc.license_expression is taken from the Component + pc = ProductComponent.objects.get(product=self.product1, component=self.component1) + self.assertEqual(self.license1.key, pc.license_expression) + self.assertEqual(self.user, pc.created_by) + self.assertEqual(self.user, pc.last_modified_by) + self.assertEqual(26, len(str(pc.created_date))) + self.assertEqual(26, len(str(pc.last_modified_date))) + + self.assertFalse(History.objects.get_for_object(pc).exists()) + self.assertEqual(self.user, pc.created_by) + self.assertTrue(pc.created_date) + + expected = "3 component(s) added to MyProduct." + self.assertEqual(expected, list(response.context["messages"])[0].message) + + response = self.client.post( + add_to_product_url, {"product": self.product1.pk, "ids": ids}, follow=True + ) + expected = "0 component(s) added to MyProduct. 3 component(s) were already assigned." + self.assertEqual(expected, list(response.context["messages"])[0].message) + + self.product1.refresh_from_db() + history_entries = History.objects.get_for_object(self.product1) + expected_messages = sorted( + [ + 'Added component "Component3 r1"', + 'Added component "Component2 0.2"', + 'Added component "Component1 0.1"', + ] + ) + self.assertEqual( + expected_messages, sorted([entry.change_message for entry in history_entries]) + ) + self.assertEqual(self.user, self.product1.last_modified_by) + + def test_add_to_product_action_proper_package(self): + self.client.login(username=self.user.username, password="secret") + package1 = Package.objects.create( + filename="p1.zip", dataspace=self.dataspace1, license_expression=self.license1.key + ) + + ids = str(package1.pk) + add_to_product_url = reverse("admin:component_catalog_package_add_to_product") + + # Simulate displaying the page + response = self.client.get("{}?ids={}".format(add_to_product_url, ids)) + self.assertEqual(200, response.status_code) + self.assertTrue("form" in response.context) + + response = self.client.post( + add_to_product_url, {"product": self.product1.pk, "ids": ids}, follow=True + ) + self.assertRedirects(response, reverse("admin:component_catalog_package_changelist")) + self.assertEqual(1, self.product1.productpackages.count()) + + # pc.license_expression is taken from the Package + pp = ProductPackage.objects.get(product=self.product1, package=package1) + self.assertEqual(self.license1.key, pp.license_expression) + self.assertEqual(self.user, pp.created_by) + self.assertEqual(self.user, pp.last_modified_by) + self.assertEqual(26, len(str(pp.created_date))) + self.assertEqual(26, len(str(pp.last_modified_date))) + + self.assertFalse(History.objects.get_for_object(pp).exists()) + self.assertEqual(self.user, pp.created_by) + self.assertTrue(pp.created_date) + + expected = "1 package(s) added to MyProduct." + self.assertEqual(expected, list(response.context["messages"])[0].message) + + response = self.client.post( + add_to_product_url, {"product": self.product1.pk, "ids": ids}, follow=True + ) + expected = "0 package(s) added to MyProduct. 1 package(s) were already assigned." + self.assertEqual(expected, list(response.context["messages"])[0].message) + + self.product1.refresh_from_db() + history_entries = History.objects.get_for_object(self.product1) + expected_messages = ['Added package "p1.zip"'] + self.assertEqual(expected_messages, [entry.change_message for entry in history_entries]) + self.assertEqual(self.user, self.product1.last_modified_by) + + def test_add_to_product_action_permission(self): + self.client.login(username=self.user.username, password="secret") + + ids = ",".join([str(self.component1.pk), str(self.component2.pk), str(self.component3.pk)]) + add_to_product_url = reverse("admin:component_catalog_component_add_to_product") + component_changelist_url = reverse("admin:component_catalog_component_changelist") + + self.user.is_superuser = False + self.user.save() + + self.assertFalse(self.user.has_perm("product_portfolio.add_productcomponent")) + response = self.client.get("{}?ids={}".format(add_to_product_url, ids)) + self.assertEqual(404, response.status_code) + self.user = add_perm(self.user, "change_component") + self.assertNotContains(self.client.get(component_changelist_url), "add_to_product") + + self.user = add_perm(self.user, "add_productcomponent") + self.assertTrue(self.user.has_perm("product_portfolio.add_productcomponent")) + response = self.client.get("{}?ids={}".format(add_to_product_url, ids)) + self.assertEqual(200, response.status_code) + self.assertContains(self.client.get(component_changelist_url), "add_to_product") + + def test_add_to_product_action_omitting_ids_returns_404(self): + self.client.login(username="test", password="secret") + url = reverse("admin:component_catalog_component_add_to_product") + response = self.client.post(url, {"product": self.product1.pk}) + self.assertEqual(404, response.status_code) + + def test_add_to_product_action_submitting_no_product_shows_error(self): + self.client.login(username="test", password="secret") + ids = ",".join([str(self.component1.pk), str(self.component2.pk), str(self.component3.pk)]) + url = reverse("admin:component_catalog_component_add_to_product") + response = self.client.post(url, {"ids": ids}) + self.assertEqual(200, response.status_code) + self.assertContains(response, "This field is required.") + + def test_add_to_product_action_when_product_already_contains_component_has_no_effect(self): + self.client.login(username="test", password="secret") + ProductComponent.objects.create( + product=self.product1, component=self.component1, dataspace=self.component1.dataspace + ) + + ids = ",".join([str(self.component1.pk), str(self.component2.pk), str(self.component3.pk)]) + url = reverse("admin:component_catalog_component_add_to_product") + response = self.client.post(url, {"product": self.product1.pk, "ids": ids}) + self.assertRedirects(response, reverse("admin:component_catalog_component_changelist")) + self.assertTrue( + ProductComponent.objects.get(product=self.product1, component=self.component1) + ) + + def test_add_to_product_action_checks_dataspace(self): + component = Component.objects.create( + name="A Component", version="1.0", dataspace=self.alternate_dataspace + ) + + self.client.login(username="test", password="secret") + ids = ",".join([str(component.pk)]) + url = reverse("admin:component_catalog_component_add_to_product") + response = self.client.post(url, {"product": self.product1.pk, "ids": ids}, follow=True) + self.assertRedirects(response, reverse("admin:component_catalog_component_changelist")) + self.assertEqual( + "The dataspace of the selected objects did not match your dataspace.", + list(response.context["messages"])[0].message, + ) + + def test_add_to_product_action_product_secured_qs(self): + component = Component.objects.create(name="c", version="1.0", dataspace=self.dataspace1) + ids = ",".join([str(component.pk)]) + add_to_product_url = reverse("admin:component_catalog_component_add_to_product") + + self.admin_user = add_perm(self.admin_user, "change_component") + self.admin_user = add_perm(self.admin_user, "add_productcomponent") + + self.client.login(username=self.admin_user.username, password="secret") + response = self.client.get("{}?ids={}".format(add_to_product_url, ids)) + self.assertEqual(200, response.status_code) + self.assertEqual(0, response.context_data["form"].fields["product"]._queryset.count()) + + assign_perm("view_product", self.admin_user, self.product1) + assign_perm("change_product", self.admin_user, self.product1) + response = self.client.get("{}?ids={}".format(add_to_product_url, ids)) + self.assertEqual(1, response.context_data["form"].fields["product"]._queryset.count()) + self.assertIn(self.product1, response.context_data["form"].fields["product"]._queryset) + + def test_add_package_page_has_no_inlines_when_it_is_a_popup(self): + self.client.login(username="test", password="secret") + + url = reverse("admin:component_catalog_package_add") + response = self.client.get(url) + self.assertContains(response, "Associated Components") + + params = "?" + urllib.parse.urlencode({IS_POPUP_VAR: "1"}) + response = self.client.get(url + params) + self.assertNotContains(response, "Associated Components") + response = self.client.post(url + params, data={"filename": "AAA"}) + self.assertEqual(response.status_code, 302) + + params = "?" + urllib.parse.urlencode({"_changelist_filters": "_to_field=id&_popup=1"}) + response = self.client.get(url + params) + self.assertNotContains(response, "Associated Components") + response = self.client.post(url + params, data={"filename": "BBB"}) + self.assertEqual(response.status_code, 302) + + def test_component_changelist_set_policy_action_proper(self): + self.client.login(username=self.user.username, password="secret") + + license_policy = UsagePolicy.objects.create( + label="LicensePolicy", + icon="icon", + content_type=ContentType.objects.get_for_model(License), + dataspace=self.dataspace1, + ) + component_policy = UsagePolicy.objects.create( + label="ComponentPolicy", + icon="icon", + content_type=ContentType.objects.get_for_model(Component), + dataspace=self.dataspace1, + ) + AssociatedPolicy.objects.create( + from_policy=license_policy, + to_policy=component_policy, + dataspace=self.dataspace1, + ) + + self.license1.usage_policy = license_policy + self.license1.save() + + self.component1.license_expression = "{} AND {}".format( + self.license1.key, self.license2.key + ) + self.component1.save() + self.assertEqual(component_policy, self.component1.get_policy_from_primary_license()) + + ids = str(self.component1.pk) + set_policy_url = reverse("admin:component_catalog_component_set_policy") + + response = self.client.get("{}?ids={}".format(set_policy_url, ids)) + self.assertEqual(200, response.status_code) + self.assertTrue("form" in response.context) + expected = """ + + Component1 0.1 + + ComponentPolicy + license1 + license1 AND license2 + + """.format( + self.component1.get_admin_url(), self.component1.id + ) + self.assertContains(response, expected, html=True) + + self.assertIsNone(self.component1.usage_policy) + data = { + "checked_id_{}".format(self.component1.id): "on", + "ids": ids, + } + response = self.client.post(set_policy_url, data) + self.assertRedirects(response, reverse("admin:component_catalog_component_changelist")) + self.component1 = Component.objects.get(id=self.component1.id) + self.assertEqual(component_policy, self.component1.usage_policy) + + def test_component_name_cannot_contain_slash(self): + self.client.login(username="test", password="secret") + url = reverse("admin:component_catalog_component_add") + data = { + "name": "Name with /", + "owner": self.owner1.id, + "type": self.type1.id, + "curation_level": 0, + "related_children-INITIAL_FORMS": 0, + "related_children-TOTAL_FORMS": 0, + "componentassignedpackage_set-TOTAL_FORMS": 0, + "componentassignedpackage_set-INITIAL_FORMS": 0, + "dje-externalreference-content_type-object_id-TOTAL_FORMS": 0, + "dje-externalreference-content_type-object_id-INITIAL_FORMS": 0, + } + + response = self.client.post(url, data) + expected = ( + "Enter a valid value consisting of spaces, periods, letters, numbers," + " or !#"':,&()+_-." + ) + self.assertContains(response, expected) + + def test_component_external_reference_inline_in_changeform(self): + self.client.login(username="test", password="secret") + ext_source1 = ExternalSource.objects.create(label="GitHub", dataspace=self.dataspace1) + url = self.component1.get_admin_url() + data = { + "name": self.component1.name, + "curation_level": 0, + "related_children-INITIAL_FORMS": 0, + "related_children-TOTAL_FORMS": 0, + "componentassignedpackage_set-TOTAL_FORMS": 0, + "componentassignedpackage_set-INITIAL_FORMS": 0, + "dje-externalreference-content_type-object_id-TOTAL_FORMS": 1, + "dje-externalreference-content_type-object_id-INITIAL_FORMS": 0, + "dje-externalreference-content_type-object_id-0-external_source": ext_source1.id, + "dje-externalreference-content_type-object_id-0-external_id": "external_id", + "dje-externalreference-content_type-object_id-0-external_url": "", + "dje-externalreference-content_type-object_id-0-id": "", + } + + response = self.client.post(url, data) + self.assertEqual(302, response.status_code) + self.assertTrue(ExternalReference.objects.get_for_content_object(self.component1).exists()) + + def test_component_external_reference_inline_dataspace_scope(self): + self.client.login(username="test", password="secret") + + ext_source1 = ExternalSource.objects.create( + label="GitHub", + dataspace=self.dataspace1, + ) + ext_source2 = ExternalSource.objects.create( + label="GitHubCopy", + dataspace=self.alternate_dataspace, + ) + + url = self.component1.get_admin_url() + response = self.client.get(url) + self.assertContains(response, ext_source1.label) + self.assertNotContains(response, ext_source2.label) + + def test_error_when_duplicate_component_assigned_package(self): + self.client.login(username="test", password="secret") + package1 = Package.objects.create(filename="file.zip", dataspace=self.dataspace1) + url = reverse("admin:component_catalog_component_add") + params = { + "name": "My Component", + "owner": self.owner1.id, + "is_active": 1, + "curation_level": 0, + "related_children-INITIAL_FORMS": 0, + "related_children-TOTAL_FORMS": 0, + "componentassignedpackage_set-TOTAL_FORMS": 2, + "componentassignedpackage_set-INITIAL_FORMS": 0, + "componentassignedpackage_set-0-package": package1.id, + "componentassignedpackage_set-1-package": package1.id, + "dje-externalreference-content_type-object_id-TOTAL_FORMS": "0", + "dje-externalreference-content_type-object_id-INITIAL_FORMS": "0", + } + + response = self.client.post(url, params) + error_msg = "Please correct the duplicate data for package." + self.assertContains(response, error_msg) + + # Same from the Package addition form. + url = reverse("admin:component_catalog_package_add") + params = { + "name": "some-file.zip", + "componentassignedpackage_set-TOTAL_FORMS": 2, + "componentassignedpackage_set-INITIAL_FORMS": 0, + "componentassignedpackage_set-0-component": self.component1.id, + "componentassignedpackage_set-1-component": self.component1.id, + } + + response = self.client.post(url, params) + error_msg = "Please correct the duplicate data for component." + self.assertContains(response, error_msg) + + def test_component_admin_form_clean_license_expression_add(self): + self.client.login(username="test", password="secret") + url = reverse("admin:component_catalog_component_add") + data = { + "name": "ComponentName", + "owner": self.owner1.id, + "type": self.type1.id, + "curation_level": 0, + "license_expression": "invalid", + "related_children-INITIAL_FORMS": 0, + "related_children-TOTAL_FORMS": 0, + "componentassignedpackage_set-TOTAL_FORMS": 0, + "componentassignedpackage_set-INITIAL_FORMS": 0, + "dje-externalreference-content_type-object_id-TOTAL_FORMS": 0, + "dje-externalreference-content_type-object_id-INITIAL_FORMS": 0, + } + + response = self.client.post(url, data) + expected_errors = [["Unknown license key(s): invalid"]] + self.assertEqual(expected_errors, response.context_data["errors"]) + + data["license_expression"] = "{} AND {}".format(self.license1.key, self.license2.key) + response = self.client.post(url, data) + self.assertEqual(302, response.status_code) + + # Lower-case operators are normalized + data["license_expression"] = "{} and {}".format(self.license1.key, self.license2.key) + data["name"] = "SomethingElse" + response = self.client.post(url, data) + self.assertEqual(302, response.status_code) + component = Component.objects.latest("id") + self.assertEqual("license1 AND license2", component.license_expression) + self.assertEqual(2, component.licenses.count()) + self.assertIn(self.license1, component.licenses.all()) + self.assertIn(self.license2, component.licenses.all()) + + def test_component_admin_form_clean_license_expression_change(self): + self.client.login(username="test", password="secret") + url = self.component1.get_admin_url() + + data = { + "name": self.component1.name, + "owner": self.component1.owner.id, + "curation_level": "0", + "license_expression": "invalid", + "related_children-INITIAL_FORMS": "0", + "related_children-TOTAL_FORMS": "0", + "componentassignedpackage_set-TOTAL_FORMS": "0", + "componentassignedpackage_set-INITIAL_FORMS": "0", + "dje-externalreference-content_type-object_id-TOTAL_FORMS": 0, + "dje-externalreference-content_type-object_id-INITIAL_FORMS": 0, + } + + response = self.client.post(url, data) + expected_errors = [["Unknown license key(s): invalid"]] + self.assertEqual(expected_errors, response.context_data["errors"]) + + data["license_expression"] = self.license1.key + response = self.client.post(url, data) + self.assertEqual(302, response.status_code) + self.assertEqual(1, self.component1.licenses.count()) + self.assertEqual(self.license1, self.component1.licenses.get()) + + def test_component_admin_form_license_expression_change_impact_on_relation(self): + self.client.login(username="test", password="secret") + url = self.component1.get_admin_url() + + self.assertTrue(self.component1.related_parents.exists()) + subcomponent_relation = self.component1.related_parents.all()[0] + subcomponent_relation.license_expression = self.license1.key + subcomponent_relation.save() + + data = { + "name": self.component1.name, + "curation_level": "0", + "license_expression": "", + "related_children-INITIAL_FORMS": 0, + "related_children-TOTAL_FORMS": 0, + "componentassignedpackage_set-TOTAL_FORMS": 0, + "componentassignedpackage_set-INITIAL_FORMS": 0, + "dje-externalreference-content_type-object_id-TOTAL_FORMS": 0, + "dje-externalreference-content_type-object_id-INITIAL_FORMS": 0, + } + + response = self.client.post(url, data, follow=True) + expected = """ +
    • + This license change impacts component usage in a Product or in another Component.
      + + See the + 1 subcomponent relationships + in changelist + +
    • + """.format( + subcomponent_relation.id + ) + self.assertContains(response, expected, html=True) + + def test_component_admin_form_subcomponent_inline_clean_license_expression(self): + self.client.login(username="test", password="secret") + url = self.component6.get_admin_url() + self.component6.related_children.all().delete() + + data = { + "name": self.component6.name, + "curation_level": "0", + "related_children-INITIAL_FORMS": 0, + "related_children-TOTAL_FORMS": 1, + "related_children-0-child": self.component1.id, + "related_children-0-license_expression": "invalid", + "componentassignedpackage_set-TOTAL_FORMS": 0, + "componentassignedpackage_set-INITIAL_FORMS": 0, + "dje-externalreference-content_type-object_id-TOTAL_FORMS": 0, + "dje-externalreference-content_type-object_id-INITIAL_FORMS": 0, + } + + response = self.client.post(url, data) + expected_errors = [["Unknown license key(s): invalid"]] + self.assertEqual(expected_errors, response.context_data["errors"]) + + self.assertFalse(self.component1.licenses.exists()) + data["related_children-0-license_expression"] = self.license1.key + self.client.post(url, data) + subcomponent = Subcomponent.objects.latest("id") + self.assertEqual(1, subcomponent.licenses.count()) + self.assertEqual(self.license1, subcomponent.licenses.get()) + + self.component6.related_children.all().delete() + self.component1.license_expression = self.license1.key + self.component1.save() + data["related_children-0-license_expression"] = self.license2.key + response = self.client.post(url, data) + expected_errors = [["Unknown license key(s): license2
      Available licenses: license1"]] + self.assertEqual(expected_errors, response.context_data["errors"]) + + @override_settings(REFERENCE_DATASPACE="Dataspace") + def test_component_admin_form_license_expression_builder_data_scoping(self): + self.client.login(username="test", password="secret") + + License.objects.create( + key="l3", name="l3", short_name="l3", dataspace=self.dataspace1, owner=self.owner1 + ) + + response = self.client.get(self.component1.get_admin_url()) + self.assertContains(response, "awesomplete-1.1.5.css") + self.assertContains(response, "awesomplete-1.1.5.min.js") + self.assertContains(response, "license_expression_builder.js") + expected = [("License1 (license1)", "license1"), ("License2 (license2)", "license2")] + self.assertEqual(expected, response.context["client_data"]["license_data"]) + + copied_component = copy_object(self.component1, self.dataspace_target, self.user) + response = self.client.get(copied_component.get_admin_url()) + self.assertEqual([], response.context["client_data"]["license_data"]) + copied_license = copy_object(self.license1, self.dataspace_target, self.user) + copied_license.short_name = "Copied" + copied_license.save() + response = self.client.get(copied_component.get_admin_url()) + expected = [("Copied (license1)", "license1")] + self.assertEqual(expected, response.context["client_data"]["license_data"]) + + self.assertContains(response, 'related_model_name="Component" ') + self.assertContains(response, 'related_api_url="/api/v2/components/"') + + def test_subcomponent_admin_license_expression_and_reference_notes_from_child_component(self): + self.client.login(username="test", password="secret") + + url = reverse("admin:component_catalog_subcomponent_add") + data = { + "parent": self.component6.id, + "child": self.component1.id, + } + response = self.client.post(url, data) + self.assertEqual(response.status_code, 302) + sub = Subcomponent.objects.latest("id") + self.assertEqual("", self.component1.license_expression) + self.assertEqual("", sub.license_expression) + self.assertFalse(self.component1.licenses.exists()) + self.assertFalse(sub.licenses.exists()) + sub.delete() + + self.component1.license_expression = self.license1.key + self.component1.reference_notes = "Reference notes" + self.component1.save() + + # license_expression empty, value taken from Component + response = self.client.post(url, data) + self.assertEqual(response.status_code, 302) + self.assertEqual(self.license1.key, self.component1.license_expression) + sub = Subcomponent.objects.latest("id") + self.assertEqual(self.license1.key, self.component1.license_expression) + self.assertEqual(self.license1.key, sub.license_expression) + self.assertTrue(self.component1.licenses.exists()) + self.assertTrue(sub.licenses.exists()) + self.assertEqual(self.component1.license_expression, sub.license_expression) + self.assertEqual(self.component1.reference_notes, sub.reference_notes) + sub.delete() + + def test_subcomponent_admin_license_expression_validation(self): + self.client.login(username="test", password="secret") + + url = reverse("admin:component_catalog_subcomponent_add") + data = { + "parent": self.component6.id, + "child": self.component1.id, + "license_expression": "invalid", + } + + response = self.client.post(url, data) + expected = {"license_expression": ["Unknown license key(s): invalid"]} + self.assertEqual(expected, response.context_data["adminform"].form.errors) + + data["license_expression"] = self.license1.key + response = self.client.post(url, data) + self.assertEqual(302, response.status_code) + sub = Subcomponent.objects.latest("id") + self.assertEqual(self.license1.key, sub.license_expression) + self.assertFalse(self.component1.licenses.exists()) + self.assertTrue(sub.licenses.exists()) + + sub.delete() + self.component1.license_expression = self.license1.key + self.component1.save() + self.client.post(url, data) + sub = Subcomponent.objects.latest("id") + self.assertEqual(self.license1.key, self.component1.license_expression) + self.assertEqual(self.license1.key, sub.license_expression) + self.assertTrue(self.component1.licenses.exists()) + self.assertTrue(sub.licenses.exists()) + self.assertEqual(self.component1.license_expression, sub.license_expression) + + sub.delete() + data["license_expression"] = self.license2.key + response = self.client.post(url, data) + expected = { + "license_expression": [ + "Unknown license key(s): license2
      " "Available licenses: license1" + ] + } + self.assertEqual(expected, response.context_data["adminform"].form.errors) + + def test_subcomponent_mass_update_action(self): + self.client.login(username="test", password="secret") + url = reverse("admin:component_catalog_subcomponent_changelist") + data = { + "_selected_action": self.sub_component1.id, + "action": "mass_update", + "select_across": 0, + } + response = self.client.post(url, data) + self.assertContains(response, "

      Mass update Subcomponent relationships

      ") + self.assertContains(response, '') + self.assertContains(response, '') + + def test_subcomponent_changelist_parent_related_lookup_list_filter(self): + self.client.login(username="test", password="secret") + url = reverse("admin:component_catalog_subcomponent_changelist") + + response = self.client.get(url) + expected1 = ( + '' + ) + expected2 = ( + '' + ) + expected3 = ( + '' + ) + self.assertContains(response, expected1) + self.assertContains(response, expected2) + self.assertContains(response, expected3) + self.assertEqual(5, response.context_data["cl"].result_count) + + data = {"parent__id__exact": self.sub_component1.parent.id} + response = self.client.get(url, data) + expected1 = ( + f'' + ) + expected2 = ( + ''.format(self.sub_component1.parent.id) + ) + expected3 = ( + '' + ) + expected4 = '' + self.assertContains(response, expected1) + self.assertContains(response, expected2) + self.assertContains(response, expected3) + self.assertContains(response, expected4) + self.assertEqual(1, response.context_data["cl"].result_count) + + data = {DataspaceFilter.parameter_name: self.alternate_dataspace.id} + response = self.client.get(url, data) + expected1 = ''.format( + DataspaceFilter.parameter_name, self.alternate_dataspace.id + ) + expected2 = ( + ''.format( + DataspaceFilter.parameter_name, self.alternate_dataspace.id + ) + ) + self.assertContains(response, expected1) + self.assertContains(response, expected2) + + url = reverse("admin:component_catalog_component_changelist") + response = self.client.get(url + "?_filter_lookup=1") + self.assertNotContains(response, reverse("admin:component_catalog_component_add")) + + def test_package_changelist_component_hierarchy_related_lookup_list_filter(self): + self.client.login(username="test", password="secret") + package1 = Package.objects.create(filename="p1.zip", dataspace=self.dataspace1) + ComponentAssignedPackage.objects.create( + component=self.component1, package=package1, dataspace=self.dataspace1 + ) + package2 = Package.objects.create(filename="p2.zip", dataspace=self.dataspace1) + ComponentAssignedPackage.objects.create( + component=self.component2, package=package2, dataspace=self.dataspace1 + ) + package3 = Package.objects.create(filename="p3.zip", dataspace=self.dataspace1) + ComponentAssignedPackage.objects.create( + component=self.component3, package=package3, dataspace=self.dataspace1 + ) + + self.assertEqual(1, self.component2.children.count()) + self.assertIn(self.component1, self.component2.children.all()) + + url = reverse("admin:component_catalog_package_changelist") + response = self.client.get(url) + self.assertEqual(3, response.context_data["cl"].result_count) + + response = self.client.get(url, {"component__id__exact": self.component1.id}) + self.assertEqual(1, response.context_data["cl"].result_count) + self.assertIn(package1, response.context_data["cl"].queryset) + self.assertNotIn(package2, response.context_data["cl"].queryset) + self.assertNotIn(package3, response.context_data["cl"].queryset) + + response = self.client.get(url, {"component__id__exact": self.component2.id}) + self.assertEqual(2, response.context_data["cl"].result_count) + self.assertIn(package1, response.context_data["cl"].queryset) + self.assertIn(package2, response.context_data["cl"].queryset) + self.assertNotIn(package3, response.context_data["cl"].queryset) + + response = self.client.get(url, {"component__id__exact": self.component3.id}) + self.assertEqual(2, response.context_data["cl"].result_count) + self.assertNotIn(package1, response.context_data["cl"].queryset) + self.assertIn(package2, response.context_data["cl"].queryset) + self.assertIn(package3, response.context_data["cl"].queryset) + + # A proper related_lookup was set then the dataspace filter changed + data = { + "component__id__exact": self.component1.id, + DataspaceFilter.parameter_name: self.alternate_dataspace.id, + } + response = self.client.get(url, data, follow=True) + expected = [("/admin/component_catalog/package/?e=1", 302)] + self.assertEqual(expected, response.redirect_chain) + expected = ( + '
    • Set the Dataspace filter before using the' + " Component lookup filter
    • " + ) + self.assertContains(response, expected) + + def test_package_changelist_has_component_list_filter(self): + self.client.login(username="test", password="secret") + + package1 = Package.objects.create(filename="p1.zip", dataspace=self.dataspace1) + ComponentAssignedPackage.objects.create( + component=self.component1, package=package1, dataspace=self.dataspace1 + ) + + package2 = Package.objects.create(filename="p2.zip", dataspace=self.dataspace1) + + url = reverse("admin:component_catalog_package_changelist") + response = self.client.get(url) + self.assertEqual(2, response.context_data["cl"].result_count) + + expected = """ +
      + + +
      + """ + self.assertContains(response, expected, html=True) + + response = self.client.get(url, {"component__isnull": 0}) + self.assertEqual(1, response.context_data["cl"].result_count) + self.assertIn(package1, response.context_data["cl"].queryset) + self.assertNotIn(package2, response.context_data["cl"].queryset) + + response = self.client.get(url, {"component__isnull": 1}) + self.assertEqual(1, response.context_data["cl"].result_count) + self.assertNotIn(package1, response.context_data["cl"].queryset) + self.assertIn(package2, response.context_data["cl"].queryset) + + def test_package_changelist_advanced_search_on_protocol(self): + Package.objects.create(filename="p1.zip", dataspace=self.dataspace1) + p2 = Package.objects.create( + filename="p2", download_url="https://url.com/p2.zip", dataspace=self.dataspace1 + ) + + self.client.login(username="test", password="secret") + changelist_url = reverse("admin:component_catalog_package_changelist") + response = self.client.get(changelist_url + "?q=https://url.com/") + self.assertEqual(200, response.status_code) + self.assertEqual(1, response.context_data["cl"].result_count) + self.assertIn(p2, response.context_data["cl"].result_list) + + response = self.client.get(changelist_url + "?q=download_url=https://url.com/p2.zip") + self.assertEqual(200, response.status_code) + self.assertEqual(1, response.context_data["cl"].result_count) + self.assertIn(p2, response.context_data["cl"].result_list) + + def test_package_changelist_set_policy_action_proper(self): + self.client.login(username=self.user.username, password="secret") + p1 = Package.objects.create(filename="p1.zip", dataspace=self.dataspace1) + + license_policy = UsagePolicy.objects.create( + label="LicensePolicy", + icon="icon", + content_type=ContentType.objects.get_for_model(License), + dataspace=self.dataspace1, + ) + package_policy = UsagePolicy.objects.create( + label="PackagePolicy", + icon="icon", + content_type=ContentType.objects.get_for_model(Package), + dataspace=self.dataspace1, + ) + AssociatedPolicy.objects.create( + from_policy=license_policy, + to_policy=package_policy, + dataspace=self.dataspace1, + ) + + self.license1.usage_policy = license_policy + self.license1.save() + + p1.license_expression = "{} AND {}".format(self.license1.key, self.license2.key) + p1.save() + self.assertEqual(package_policy, p1.get_policy_from_primary_license()) + + ids = str(p1.pk) + set_policy_url = reverse("admin:component_catalog_package_set_policy") + + response = self.client.get("{}?ids={}".format(set_policy_url, ids)) + self.assertEqual(200, response.status_code) + self.assertTrue("form" in response.context) + expected = """ + + {} + + PackagePolicy + license1 + license1 AND license2 + + """.format( + p1.get_admin_url(), p1.filename, p1.id + ) + self.assertContains(response, expected, html=True) + + self.assertIsNone(p1.usage_policy) + data = { + "checked_id_{}".format(p1.id): "on", + "ids": ids, + } + response = self.client.post(set_policy_url, data) + self.assertRedirects(response, reverse("admin:component_catalog_package_changelist")) + p1 = Package.objects.get(id=p1.id) + self.assertEqual(package_policy, p1.usage_policy) + + def test_package_changelist_set_purl_action(self): + self.client.login(username=self.user.username, password="secret") + package = Package.objects.create(dataspace=self.dataspace1, filename="p1.zip") + package.download_url = "http://repo1.maven.org/maven2/jdbm/jdbm/0.20-dev/" + package.save() + self.assertEqual("", package.package_url) + + changelist_url = reverse("admin:component_catalog_package_changelist") + data = { + "_selected_action": [package.id], + "action": "set_purl", + } + + response = self.client.post(changelist_url, data, follow=True) + expected = "1 Package(s) updated with a Package URL." + self.assertContains(response, expected) + + package.refresh_from_db() + self.assertEqual("pkg:maven/jdbm/jdbm@0.20-dev", package.package_url) + + history_entry = History.objects.get_for_object(package).get() + expected_messages = "Set Package URL from Download URL" + self.assertEqual(expected_messages, history_entry.change_message) + + def test_package_admin_changeform_usage_policy_widget_wrapper(self): + self.client.login(username="test", password="secret") + p1 = Package.objects.create(filename="p1.zip", dataspace=self.dataspace1) + + license_policy = UsagePolicy.objects.create( + label="LicensePolicy", + icon="icon", + content_type=ContentType.objects.get_for_model(License), + dataspace=self.dataspace1, + ) + package_policy = UsagePolicy.objects.create( + label="PackagePolicy", + icon="icon", + content_type=ContentType.objects.get_for_model(Package), + dataspace=self.dataspace1, + ) + AssociatedPolicy.objects.create( + from_policy=license_policy, + to_policy=package_policy, + dataspace=self.dataspace1, + ) + + self.license1.usage_policy = license_policy + self.license1.save() + p1.license_expression = self.license1.key + p1.save() + + self.assertEqual(package_policy, p1.get_policy_from_primary_license()) + self.assertEqual(package_policy, p1.policy_from_primary_license) + self.assertIsNone(p1.usage_policy) + + response = self.client.get(p1.get_admin_url()) + expected = ( + '
      Value from primary license license1: PackagePolicy
      ' + ) + self.assertContains(response, expected, html=True) + + def test_package_admin_changeform_usage_policy_on_license_expression_changed(self): + self.client.login(username="test", password="secret") + p1 = Package.objects.create(filename="p1.zip", dataspace=self.dataspace1) + + license_policy = UsagePolicy.objects.create( + label="LicensePolicy", + icon="icon", + content_type=ContentType.objects.get_for_model(License), + dataspace=self.dataspace1, + ) + package_policy = UsagePolicy.objects.create( + label="PackagePolicy", + icon="icon", + content_type=ContentType.objects.get_for_model(Package), + dataspace=self.dataspace1, + ) + package_policy2 = UsagePolicy.objects.create( + label="PackagePolicy2", + icon="icon", + content_type=ContentType.objects.get_for_model(Package), + dataspace=self.dataspace1, + ) + AssociatedPolicy.objects.create( + from_policy=license_policy, + to_policy=package_policy, + dataspace=self.dataspace1, + ) + + self.license1.usage_policy = license_policy + self.license1.save() + p1.usage_policy = package_policy2 + p1.save() + + self.assertIsNone(p1.get_policy_from_primary_license()) + self.assertIsNone(p1.policy_from_primary_license) + + data = { + "filename": p1.filename, + "license_expression": self.license1.key, + "componentassignedpackage_set-TOTAL_FORMS": 0, + "componentassignedpackage_set-INITIAL_FORMS": 0, + "dje-externalreference-content_type-object_id-TOTAL_FORMS": 0, + "dje-externalreference-content_type-object_id-INITIAL_FORMS": 0, + } + + response = self.client.post(p1.get_admin_url(), data, follow=True) + expected = ( + "The changed license assignment does not match the currently assigned usage " + 'policy: "PackagePolicy2" != "PackagePolicy" from license1' + ) + self.assertEqual(expected, list(response.context["messages"])[0].message) + + def test_package_changeform_inline_component_trigger_update_completion_level(self): + self.client.login(username="test", password="secret") + package1 = Package.objects.create(filename="p1.zip", dataspace=self.dataspace1) + url = package1.get_admin_url() + + self.assertEqual(0, package1.componentassignedpackage_set.count()) + self.assertEqual(0, self.component1.componentassignedpackage_set.count()) + self.assertEqual(0, self.component2.componentassignedpackage_set.count()) + self.assertEqual(15, self.component1.compute_completion_level()) + self.component1.update_completion_level() + self.component1.refresh_from_db() + self.assertEqual(15, self.component1.completion_level) + + # Addition + data = { + "filename": "a.zip", + "componentassignedpackage_set-0-component": self.component1.id, + "componentassignedpackage_set-TOTAL_FORMS": "1", + "componentassignedpackage_set-INITIAL_FORMS": "0", + "dje-externalreference-content_type-object_id-TOTAL_FORMS": 0, + "dje-externalreference-content_type-object_id-INITIAL_FORMS": 0, + } + response = self.client.post(url, data) + self.assertEqual(302, response.status_code) + self.assertEqual(1, package1.componentassignedpackage_set.count()) + self.assertEqual(1, self.component1.componentassignedpackage_set.count()) + self.component1.refresh_from_db() + self.assertEqual(22, self.component1.completion_level) + + # Edition + data["componentassignedpackage_set-INITIAL_FORMS"] = "1" + data["componentassignedpackage_set-0-id"] = package1.componentassignedpackage_set.get().id + data["componentassignedpackage_set-0-component"] = (self.component2.id,) + response = self.client.post(url, data) + self.assertEqual(302, response.status_code) + self.assertEqual(1, package1.componentassignedpackage_set.count()) + self.assertEqual(0, self.component1.componentassignedpackage_set.count()) + self.component1.refresh_from_db() + self.assertEqual(15, self.component1.completion_level) + self.assertEqual(1, self.component2.componentassignedpackage_set.count()) + self.component2.refresh_from_db() + self.assertEqual(22, self.component2.completion_level) + + # Deletion + data["componentassignedpackage_set-0-DELETE"] = "on" + response = self.client.post(url, data) + self.assertEqual(302, response.status_code) + self.assertEqual(0, package1.componentassignedpackage_set.count()) + self.assertEqual(0, self.component2.componentassignedpackage_set.count()) + self.component2.refresh_from_db() + self.assertEqual(15, self.component2.completion_level) + + def test_package_changeform_inline_remove_component_value_and_save_as(self): + self.client.login(username="test", password="secret") + package1 = Package.objects.create(filename="p1.zip", dataspace=self.dataspace1) + url = package1.get_admin_url() + + data = { + "filename": "a.zip", + "_saveasnew": "Save as new", + "componentassignedpackage_set-0-component": "", + "componentassignedpackage_set-0-id": "", + "componentassignedpackage_set-0-package": "", + "componentassignedpackage_set-TOTAL_FORMS": "1", + "componentassignedpackage_set-INITIAL_FORMS": "0", + "dje-externalreference-content_type-object_id-TOTAL_FORMS": 0, + "dje-externalreference-content_type-object_id-INITIAL_FORMS": 0, + } + + response = self.client.post(url, data) + self.assertEqual(302, response.status_code) + + def test_package_changeform_unique_constraint_validation(self): + self.client.login(username="test", password="secret") + package1 = Package.objects.create(filename="p1.zip", dataspace=self.dataspace1) + add_url = reverse("admin:component_catalog_package_add") + + data = { + "filename": package1.filename, + "componentassignedpackage_set-TOTAL_FORMS": 0, + "componentassignedpackage_set-INITIAL_FORMS": 0, + "dje-externalreference-content_type-object_id-TOTAL_FORMS": 0, + "dje-externalreference-content_type-object_id-INITIAL_FORMS": 0, + } + + response = self.client.post(add_url, data) + self.assertEqual(200, response.status_code) + error = ( + "Package with this Dataspace, Type, Namespace, Name, Version, Qualifiers," + " Subpath, Download URL and Filename already exists." + ) + expected = f"
    • {error}
    • " + self.assertContains(response, expected, html=True) + errors = {"__all__": [error]} + self.assertEqual(errors, response.context_data["adminform"].form.errors) + + package1.download_url = "http://url.com/p1.zip" + package1.save() + + data["download_url"] = package1.download_url + response = self.client.post(add_url, data) + self.assertEqual(200, response.status_code) + expected = f"
    • {error}
    • " + self.assertContains(response, expected, html=True) + errors = {"__all__": [error]} + self.assertEqual(errors, response.context_data["adminform"].form.errors) + + # Making sure the validation is scoped by dataspace + other_package = Package.objects.create( + filename="p2.zip", download_url="http://url2.zip", dataspace=self.dataspace_target + ) + data["filename"] = other_package.filename + data["download_url"] = other_package.download_url + response = self.client.post(add_url, data) + self.assertEqual(302, response.status_code) + Package.objects.get( + filename=data["filename"], download_url=data["download_url"], dataspace=self.dataspace1 + ) + + def test_package_changeform_filename_validation(self): + self.client.login(username="test", password="secret") + add_url = reverse("admin:component_catalog_package_add") + + data = { + "filename": "pack/age.zip", + "componentassignedpackage_set-TOTAL_FORMS": 0, + "componentassignedpackage_set-INITIAL_FORMS": 0, + "dje-externalreference-content_type-object_id-TOTAL_FORMS": 0, + "dje-externalreference-content_type-object_id-INITIAL_FORMS": 0, + } + + response = self.client.post(add_url, data) + self.assertEqual(200, response.status_code) + errors = { + "filename": ["Enter a valid filename: slash, backslash, or colon are not allowed."] + } + self.assertEqual(errors, response.context_data["adminform"].form.errors) + + data["filename"] = "pack\\age.zip" + response = self.client.post(add_url, data) + self.assertEqual(200, response.status_code) + self.assertEqual(errors, response.context_data["adminform"].form.errors) + + data["filename"] = "pack:age.zip" + response = self.client.post(add_url, data) + self.assertEqual(200, response.status_code) + self.assertEqual(errors, response.context_data["adminform"].form.errors) + + def test_component_changelist_advanced_search_on_empty_space(self): + self.client.login(username="test", password="secret") + changelist_url = reverse("admin:component_catalog_component_changelist") + # '?q=+' + means empty space + response = self.client.get(changelist_url + "?q=+") + self.assertEqual(200, response.status_code) + expected = Component.objects.scope(self.dataspace1).count() + self.assertEqual(expected, response.context_data["cl"].result_count) + + def test_component_changelist_advanced_search_with_apostrophe(self): + self.client.login(username="test", password="secret") + changelist_url = reverse("admin:component_catalog_component_changelist") + + self.component1.name = "Programmer's Notepad" + self.component1.save() + response = self.client.get(changelist_url + "?q=Programmer%27s+Notepad") + self.assertEqual(200, response.status_code) + self.assertEqual(1, response.context_data["cl"].result_count) + self.assertIn(self.component1, response.context_data["cl"].queryset) + + response = self.client.get(changelist_url + '?q=%3D"google+toolkit') + self.assertEqual(200, response.status_code) + expected = '
    • Search terms error: No closing quotation
    • ' + self.assertContains(response, expected) + + def test_component_changelist_search_distinct_applied_on_m2m_fields(self): + self.client.login(username="test", password="secret") + changelist_url = reverse("admin:component_catalog_component_changelist") + + package1 = Package.objects.create(filename="package1.zip", dataspace=self.dataspace1) + ComponentAssignedPackage.objects.create( + component=self.component1, package=package1, dataspace=self.dataspace1 + ) + package2 = Package.objects.create(filename="package2.zip", dataspace=self.dataspace1) + ComponentAssignedPackage.objects.create( + component=self.component1, package=package2, dataspace=self.dataspace1 + ) + + self.assertEqual(2, self.component1.packages.count()) + self.assertIn("packages__filename", ComponentAdmin.search_fields) # m2m field in search + + response = self.client.get(changelist_url + f"?q={self.component1.name}") + self.assertEqual(200, response.status_code) + self.assertEqual(1, response.context_data["cl"].result_count) + + def test_component_changelist_reporting_query_list_filter(self): + from reporting.models import OrderField + from reporting.models import Query + + component_ct = ContentType.objects.get_for_model(Component) + query1 = Query.objects.create( + dataspace=self.dataspace1, name="Q1", content_type=component_ct, operator="and" + ) + OrderField.objects.create( + dataspace=self.dataspace1, query=query1, field_name="license_expression", seq=0 + ) + query2 = Query.objects.create( + dataspace=self.dataspace1, name="Q2", content_type=component_ct, operator="and" + ) + + self.client.login(username=self.user.username, password="secret") + url = reverse("admin:component_catalog_component_changelist") + response = self.client.get(url) + + expected = f""" + + """ + self.assertContains(response, expected, html=True) + + def test_component_admin_changeform_usage_policy_widget_wrapper(self): + self.client.login(username="test", password="secret") + + license_policy = UsagePolicy.objects.create( + label="LicensePolicy", + icon="icon", + content_type=ContentType.objects.get_for_model(License), + dataspace=self.dataspace1, + ) + component_policy = UsagePolicy.objects.create( + label="ComponentPolicy", + icon="icon", + content_type=ContentType.objects.get_for_model(Component), + dataspace=self.dataspace1, + ) + AssociatedPolicy.objects.create( + from_policy=license_policy, + to_policy=component_policy, + dataspace=self.dataspace1, + ) + + self.license1.usage_policy = license_policy + self.license1.save() + self.component1.license_expression = self.license1.key + self.component1.save() + + self.assertEqual(component_policy, self.component1.get_policy_from_primary_license()) + self.assertEqual(component_policy, self.component1.policy_from_primary_license) + self.assertIsNone(self.component1.usage_policy) + + response = self.client.get(self.component1.get_admin_url()) + expected = ( + '
      Value from primary license license1: ComponentPolicy
      ' + ) + self.assertContains(response, expected, html=True) + + def test_component_admin_changeform_usage_policy_on_license_expression_changed(self): + self.client.login(username="test", password="secret") + + license_policy = UsagePolicy.objects.create( + label="LicensePolicy", + icon="icon", + content_type=ContentType.objects.get_for_model(License), + dataspace=self.dataspace1, + ) + component_policy = UsagePolicy.objects.create( + label="ComponentPolicy", + icon="icon", + content_type=ContentType.objects.get_for_model(Component), + dataspace=self.dataspace1, + ) + component_policy2 = UsagePolicy.objects.create( + label="ComponentPolicy2", + icon="icon", + content_type=ContentType.objects.get_for_model(Component), + dataspace=self.dataspace1, + ) + AssociatedPolicy.objects.create( + from_policy=license_policy, + to_policy=component_policy, + dataspace=self.dataspace1, + ) + + self.license1.usage_policy = license_policy + self.license1.save() + self.component1.usage_policy = component_policy2 + self.component1.save() + + self.assertIsNone(self.component1.get_policy_from_primary_license()) + self.assertIsNone(self.component1.policy_from_primary_license) + + data = { + "name": self.component1.name, + "curation_level": "0", + "license_expression": self.license1.key, + "related_children-INITIAL_FORMS": 0, + "related_children-TOTAL_FORMS": 0, + "componentassignedpackage_set-TOTAL_FORMS": 0, + "componentassignedpackage_set-INITIAL_FORMS": 0, + "dje-externalreference-content_type-object_id-TOTAL_FORMS": 0, + "dje-externalreference-content_type-object_id-INITIAL_FORMS": 0, + } + + response = self.client.post(self.component1.get_admin_url(), data, follow=True) + expected = ( + "The changed license assignment does not match the currently assigned usage " + 'policy: "ComponentPolicy2" != "ComponentPolicy" from license1' + ) + self.assertEqual(expected, list(response.context["messages"])[0].message) + + def test_subcomponent_admin_changelist_available_actions(self): + self.client.login(username="test", password="secret") + + url = reverse("admin:component_catalog_subcomponent_changelist") + response = self.client.get(url) + expected = [ + ("", "---------"), + ("set_policy", "Set usage policy from components"), + ("mass_update", "Mass update"), + ] + self.assertEqual(expected, response.context_data["action_form"].fields["action"].choices) + + with self.assertRaises(NoReverseMatch): + reverse("admin:component_catalog_subcomponent_copy") + + with self.assertRaises(NoReverseMatch): + reverse("admin:component_catalog_subcomponent_compare") + + def test_component_admin_delete_confirmation_include_associated_packages(self): + self.client.login(username="test", password="secret") + delete_url = reverse("admin:component_catalog_component_delete", args=[self.component1.pk]) + self.assertFalse(self.component1.packages.exists()) + response = self.client.get(delete_url) + expected = "Would you also like to delete Packages associated with this Component" + self.assertNotContains(response, expected) + + package1 = Package.objects.create(filename="package1.zip", dataspace=self.dataspace1) + ComponentAssignedPackage.objects.create( + component=self.component1, package=package1, dataspace=self.dataspace1 + ) + package2 = Package.objects.create(filename="package2.zip", dataspace=self.dataspace1) + ComponentAssignedPackage.objects.create( + component=self.component1, package=package2, dataspace=self.dataspace1 + ) + + self.assertTrue(self.component1.packages.exists()) + response = self.client.get(delete_url) + self.assertContains(response, expected) + + data = { + "post": "yes", + "delete_packages": "yes", + } + response = self.client.post(delete_url, data=data, follow=True) + self.assertContains(response, "was deleted successfully.") + self.assertFalse(Component.objects.filter(pk=self.component1.pk).exists()) + self.assertFalse(Package.objects.filter(pk__in=[package1.pk, package2.pk]).exists()) + + package3 = Package.objects.create(filename="package3.zip", dataspace=self.dataspace1) + ComponentAssignedPackage.objects.create( + component=self.component2, package=package3, dataspace=self.dataspace1 + ) + data = { + "post": "yes", + } + delete_url = reverse("admin:component_catalog_component_delete", args=[self.component2.pk]) + response = self.client.post(delete_url, data=data, follow=True) + self.assertContains(response, "was deleted successfully.") + self.assertFalse(Component.objects.filter(pk=self.component2.pk).exists()) + self.assertTrue(Package.objects.filter(pk=package3.pk).exists()) + + def test_component_admin_delete_selected_action_include_associated_packages(self): + self.client.login(username="test", password="secret") + url = reverse("admin:component_catalog_component_changelist") + + package1 = Package.objects.create(filename="package1.zip", dataspace=self.dataspace1) + ComponentAssignedPackage.objects.create( + component=self.component1, package=package1, dataspace=self.dataspace1 + ) + package2 = Package.objects.create(filename="package2.zip", dataspace=self.dataspace1) + ComponentAssignedPackage.objects.create( + component=self.component1, package=package2, dataspace=self.dataspace1 + ) + + data = { + "_selected_action": [self.component1.id], + "action": "delete_selected", + } + + response = self.client.post(url, data) + expected = "Would you also like to delete Packages associated with these Components" + self.assertContains(response, expected) + + data.update( + { + "post": "yes", + "delete_packages": "yes", + } + ) + + response = self.client.post(url, data, follow=True) + msg = '
    • Successfully deleted 1 component.
    • ' + self.assertContains(response, msg) + self.assertFalse(Component.objects.filter(pk=self.component1.pk).exists()) + self.assertFalse(Package.objects.filter(pk__in=[package1.pk, package2.pk]).exists()) + + def test_component_changeform_primary_language_autocomplete_field(self): + self.client.login(username="test", password="secret") + url = self.component1.get_admin_url() + response = self.client.get(url) + + expected = ( + '' + self.assertContains(response, expected, html=True) + + expected = '' + self.assertContains(response, expected, html=True) + + def test_component_changeform_package_inline_autocomplete_field(self): + Package.objects.create( + type="pypi", + namespace="djangoproject", + name="django", + version="3.1", + dataspace=self.dataspace1, + ) + + self.client.login(username="test", password="secret") + url = reverse("grp_autocomplete_lookup") + url += "?app_label=component_catalog&model_name=package" + + search_queries = [ + "pkg:pypi/djangoproject/django@3.1", + "pypi/django@3.1", + "django@3.1", + "django 3.1", + "pypi django 3.1", + ] + + for term in search_queries: + response = self.client.get(url + f"&term={term}") + results = json.loads(response.content.decode()) + self.assertEqual("pypi/djangoproject/django@3.1", results[0].get("label")) + + def test_component_admin_get_initial_from_related_instance(self): + release_date = datetime.datetime(2018, 6, 21, 3, 38, 24, 139528) + package1 = Package.objects.create( + filename="p1.zip", + release_date=release_date, + primary_language="Python", + project="Project", + license_expression="bsd", + copyright="Copyright", + notice_text="Notice", + homepage_url="http://url.com", + reference_notes="Reference notes", + dataspace=self.dataspace1, + ) + initial = ComponentAdmin._get_initial_from_related_instance(package1) + expected = { + "release_date": release_date, + "primary_language": "Python", + "project": "Project", + "license_expression": "bsd", + "copyright": "Copyright", + "notice_text": "Notice", + "homepage_url": "http://url.com", + "reference_notes": "Reference notes", + } + self.assertEqual(expected, initial) + + def test_component_admin_get_changeform_initial_data(self): + self.client.login(username="test", password="secret") + + package1 = Package.objects.create( + filename="p1.zip", + copyright="Copyright from Package", + dataspace=self.dataspace1, + ) + + expected = package1.copyright + component_add_url = reverse("admin:component_catalog_component_add") + + response = self.client.get(component_add_url) + self.assertNotContains(response, expected) + + response = self.client.get(component_add_url, HTTP_REFERER=package1.get_admin_url()) + self.assertContains(response, expected) + + def test_component_admin_form_acceptable_linkages(self): + self.client.login(username="test", password="secret") + + AcceptableLinkage.objects.create(label="linkage1", dataspace=self.dataspace1) + AcceptableLinkage.objects.create(label="linkage2", dataspace=self.dataspace1) + AcceptableLinkage.objects.create(label="linkage3", dataspace=self.dataspace_target) + + expected_choices = [ + ("linkage1", "linkage1"), + ("linkage2", "linkage2"), + ] + + component_add_url = reverse("admin:component_catalog_component_add") + response = self.client.get(component_add_url) + form = response.context_data["adminform"].form + self.assertEqual(expected_choices, form.fields["acceptable_linkages"].choices) + + component_edit_url = self.component1.get_admin_url() + response = self.client.get(component_edit_url) + form = response.context_data["adminform"].form + self.assertEqual(expected_choices, form.fields["acceptable_linkages"].choices) + + # Edit + data = { + "name": self.component1.name, + "curation_level": "0", + "acceptable_linkages": "linkage1", + "related_children-INITIAL_FORMS": 0, + "related_children-TOTAL_FORMS": 0, + "componentassignedpackage_set-TOTAL_FORMS": 0, + "componentassignedpackage_set-INITIAL_FORMS": 0, + "dje-externalreference-content_type-object_id-TOTAL_FORMS": 0, + "dje-externalreference-content_type-object_id-INITIAL_FORMS": 0, + } + self.client.post(component_edit_url, data, follow=True) + self.component1.refresh_from_db() + self.assertEqual(["linkage1"], self.component1.acceptable_linkages) + + data.update( + { + "name": "new", + "acceptable_linkages": "not valid", + } + ) + response = self.client.post(component_add_url, data) + expected = "
    • Select a valid choice. not valid is not one of the available choices.
    • " + self.assertContains(response, expected, html=True) + + data["acceptable_linkages"] = ["linkage1", "linkage2"] + response = self.client.post(component_add_url, data) + added_component = Component.objects.latest("id") + self.assertEqual(data["acceptable_linkages"], added_component.acceptable_linkages) + + def test_component_admin_form_configuration_setting(self): + self.client.login(username="test", password="secret") + component_add_url = reverse("admin:component_catalog_component_add") + component_edit_url = self.component1.get_admin_url() + + response = self.client.get(component_add_url) + form = response.context_data["adminform"].form + self.assertIn("project", form.fields) + self.assertIn("is_active", form.fields) + + response = self.client.get(component_edit_url) + form = response.context_data["adminform"].form + self.assertIn("project", form.fields) + self.assertIn("is_active", form.fields) + + component_form_config = { + "component": { + "exclude": ["project", "is_active"], + }, + } + with override_settings(ADMIN_FORMS_CONFIGURATION=component_form_config): + response = self.client.get(component_add_url) + form = response.context_data["adminform"].form + self.assertNotIn("project", form.fields) + self.assertNotIn("is_active", form.fields) + + response = self.client.get(component_edit_url) + form = response.context_data["adminform"].form + self.assertNotIn("project", form.fields) + self.assertNotIn("is_active", form.fields) + + +class PackageDataCollectionTestCase(TransactionTestCase): + """ + Using a TransactionTestCase since we want to test the results of an on_commit() callback. + https://docs.djangoproject.com/en/dev/topics/db/transactions/#use-in-tests + """ + + def setUp(self): + # Workaround a side effect of running test_component_save_as_with_external_reference + # Before a TransactionTestCase test. + # https://code.djangoproject.com/ticket/10827#comment:19 + ContentType.objects.clear_cache() + + self.dataspace1 = Dataspace.objects.create(name="Dataspace") + self.user = get_user_model().objects.create_superuser( + "test", "test@test.com", "secret", self.dataspace1 + ) + + @mock.patch("requests.get") + def test_package_changeform_save_and_collect_data_on_addition(self, mock_get): + self.client.login(username="test", password="secret") + + add_url = reverse("admin:component_catalog_package_add") + response = self.client.get(add_url) + expected = "Save and collect data" + self.assertContains(response, expected) + + package1 = Package.objects.create(filename="p1.zip", dataspace=self.dataspace1) + response = self.client.get(package1.get_admin_url()) + self.assertContains(response, expected) + + data = { + "filename": "a.zip", + "download_url": "http://domain.com/a.zip", + "_collectdata": "Save and collect data", + "componentassignedpackage_set-TOTAL_FORMS": 0, + "componentassignedpackage_set-INITIAL_FORMS": 0, + "dje-externalreference-content_type-object_id-TOTAL_FORMS": 0, + "dje-externalreference-content_type-object_id-INITIAL_FORMS": 0, + } + + mock_get.return_value = mock.Mock( + content=b"\x00", headers={"content-length": 1}, status_code=200 + ) + + response = self.client.post(add_url, data, follow=True) + expected = ( + f'The SHA1, MD5, and Size fields collection from {data["download_url"]} is in progress.' + ) + self.assertEqual(expected, list(response.context["messages"])[0].message) + + package1 = Package.objects.get(filename=data["filename"]) + self.assertEqual("93b885adfe0da089cdf634904fd59f71", package1.md5) + self.assertEqual("5ba93c9db0cff93f52b521d7420e43f6eda2784f", package1.sha1) + self.assertEqual(1, package1.size) + + data["download_url"] = "ftp://ftp.denx.de/pub/u-boot/u-boot-2017.11.tar.bz2" + response = self.client.post(add_url, data, follow=True) + expected = ( + 'The SHA1, MD5, and Size fields collection is not supported for the "ftp" scheme.' + ) + self.assertEqual(expected, list(response.context["messages"])[0].message) + + data["download_url"] = "git://github.com:nexB/aboutcode-toolkit.git" + response = self.client.post(add_url, data, follow=True) + expected = ( + 'The SHA1, MD5, and Size fields collection is not supported for the "git" scheme.' + ) + self.assertEqual(expected, list(response.context["messages"])[0].message) + + +class ComponentKeywordAdminViewsTestCase(TestCase): + def setUp(self): + self.dataspace = Dataspace.objects.create(name="nexB") + self.other_dataspace = Dataspace.objects.create(name="otherOO") + self.owner = Owner.objects.create(name="Owner", dataspace=self.dataspace) + self.owner2 = Owner.objects.create(name="Organization2", dataspace=self.other_dataspace) + self.component = Component.objects.create( + owner=self.owner, name="a", dataspace=self.dataspace + ) + self.component_keyword = ComponentKeyword.objects.create( + dataspace=self.dataspace, label="the_keyword", description="Blah blah blah" + ) + self.component_keyword2 = ComponentKeyword.objects.create( + dataspace=self.other_dataspace, label="foobar", description="" + ) + + self.user = get_user_model().objects.create_superuser( + "nexb_user", "test@test.com", "secret", self.dataspace + ) + + def test_keywords_do_not_cross_dataspaces(self): + self.client.login(username="nexb_user", password="secret") + response = self.client.get(self.component.get_admin_url()) + expected = ( + '' + ) + self.assertContains(response, expected, html=True) + self.assertNotContains(response, self.component_keyword2.label) + + def test_keywords_addition_and_duplicate_validation(self): + self.client.login(username="nexb_user", password="secret") + + url = reverse("admin:component_catalog_componentkeyword_add") + + data = {"label": "A Keyword"} + response = self.client.post(url, data) + self.assertRedirects( + response, reverse("admin:component_catalog_componentkeyword_changelist") + ) + + # Post the same label again + response = self.client.post(url, data) + self.assertContains( + response, "
    • Component keyword with this Dataspace and Label already exists.
    • " + ) + expected = { + NON_FIELD_ERRORS: ["Component keyword with this Dataspace and Label already exists."] + } + self.assertEqual(expected, response.context_data["adminform"].form.errors) + + +class ComponentCopyTestCase(TestCase): + def setUp(self): + self.dataspace1 = Dataspace.objects.create(name="nexB") + self.dataspace_target = Dataspace.objects.create(name="target_org") + self.user = get_user_model().objects.create_superuser( + "test", "test@test.com", "secret", self.dataspace1 + ) + + self.type1 = ComponentType.objects.create( + label="Type1", notes="notes", dataspace=self.dataspace1 + ) + self.status1 = ComponentStatus.objects.create( + label="Status1", default_on_addition=False, dataspace=self.dataspace1 + ) + self.owner1 = Owner.objects.create(name="owner1", dataspace=self.dataspace1) + self.license1 = License.objects.create( + key="license1", + name="License1", + short_name="License1", + dataspace=self.dataspace1, + owner=self.owner1, + ) + + self.license_tag1 = LicenseTag.objects.create( + label="Tag 1", text="Text for tag1", dataspace=self.dataspace1 + ) + self.license_assigned_tag1 = LicenseAssignedTag.objects.create( + license=self.license1, + license_tag=self.license_tag1, + value=True, + dataspace=self.dataspace1, + ) + + self.component1 = Component.objects.create( + name="Component1", + version="0.1", + type=self.type1, + owner=self.owner1, + homepage_url="http://localhost.com", + dataspace=self.dataspace1, + ) + self.component2 = Component.objects.create( + name="Component2", + version="0.2", + type=self.type1, + owner=self.owner1, + dataspace=self.dataspace1, + ) + self.component3 = Component.objects.create( + name="Component3", + version="r1", + type=self.type1, + owner=self.owner1, + dataspace=self.dataspace1, + ) + self.component4 = Component.objects.create( + name="Component4", + version="r3", + type=self.type1, + owner=self.owner1, + dataspace=self.dataspace1, + ) + self.component5 = Component.objects.create( + name="Component5", + version="r4", + type=self.type1, + owner=self.owner1, + dataspace=self.dataspace1, + ) + self.component6 = Component.objects.create( + name="Component6", + version="r5", + type=self.type1, + owner=self.owner1, + dataspace=self.dataspace1, + ) + + self.sub_component1 = Subcomponent.objects.create( + parent=self.component6, + child=self.component5, + dataspace=self.component6.dataspace, + notes="I have a parent 6, and child 5", + ) + self.sub_component2 = Subcomponent.objects.create( + parent=self.component5, + child=self.component4, + dataspace=self.component5.dataspace, + notes="I have a parent 5, and child 4", + ) + self.sub_component3 = Subcomponent.objects.create( + parent=self.component4, + child=self.component3, + dataspace=self.component4.dataspace, + notes="I have a parent 4, and child 3", + ) + self.sub_component4 = Subcomponent.objects.create( + parent=self.component3, + child=self.component2, + dataspace=self.component3.dataspace, + notes="I have a parent 3, and child 2", + ) + self.sub_component5 = Subcomponent.objects.create( + parent=self.component2, + child=self.component1, + dataspace=self.component2.dataspace, + notes="I have a parent 2, and child 1", + ) + + def test_copy_component_type(self): + component_type_original_count = ComponentType.objects.count() + copied_object = copy_object(self.type1, self.dataspace_target, self.user) + self.assertEqual(component_type_original_count + 1, ComponentType.objects.count()) + self.assertEqual("Type1", copied_object.label) + self.assertEqual("notes", copied_object.notes) + + def test_copy_component_exclude_request_count(self): + from workflow.models import Request + from workflow.models import RequestTemplate + + component_ct = ContentType.objects.get(app_label="component_catalog", model="component") + request_template1 = RequestTemplate.objects.create( + name="T1", description="Desc1", dataspace=self.dataspace1, content_type=component_ct + ) + Request.objects.create( + title="Title1", + request_template=request_template1, + requester=self.user, + content_object=self.component1, + dataspace=self.dataspace1, + content_type=component_ct, + ) + self.assertEqual(1, self.component1.count_requests()) + self.component1.refresh_from_db() + self.assertEqual(1, self.component1.request_count) + + copied_object = copy_object(self.component1, self.dataspace_target, self.user) + self.assertIsNone(copied_object.request_count) + self.assertEqual(0, copied_object.count_requests()) + + def test_copy_component_common(self): + copied_object = copy_object(self.component1, self.dataspace_target, self.user) + + # Making sure the Type was copied too. + self.assertEqual(self.component1.type.uuid, copied_object.type.uuid) + self.assertEqual("0.1", copied_object.version) + self.assertEqual(self.dataspace_target, copied_object.owner.dataspace) + self.assertEqual("owner1", copied_object.owner.name) + self.assertTrue(copied_object.is_active) + + def test_copy_component_no_default_status(self): + # There is no default status set in the target dataspace + self.assertFalse(ComponentStatus.objects.get_default_on_addition_qs(self.dataspace_target)) + copied_object = copy_object(self.component1, self.dataspace_target, self.user) + self.assertFalse(copied_object.configuration_status) + + def test_copy_component_with_default_status(self): + # Creating a default status in the target dataspace + default_in_target = ComponentStatus.objects.create( + label="Default", + text="This is the default status.", + default_on_addition=True, + dataspace=self.dataspace_target, + ) + copied_object = copy_object(self.component1, self.dataspace_target, self.user) + self.assertEqual(default_in_target, copied_object.configuration_status) + + def test_copy_component_with_assigned_licenses(self): + self.component1.license_expression = self.license1.key + self.component1.save() + self.assertEqual(1, self.component1.licenses.count()) + + copied_object = copy_object(self.component1, self.dataspace_target, self.user) + copied_license = copied_object.licenses.all()[0] + self.assertEqual(self.license1.key, copied_license.key) + # Object are not the same DB entry but they share the same UUID + self.assertNotEqual(self.license1.id, copied_license.id) + self.assertEqual(self.license1.uuid, copied_license.uuid) + + def test_copy_component_with_license_expression_and_assigned_licenses(self): + # Same referenced license in both expression + self.component1.license_expression = self.license1.key + self.component1.save() + self.assertEqual(1, self.component1.licenses.count()) + self.component2.license_expression = self.license1.key + self.component2.save() + self.assertEqual(1, self.component2.licenses.count()) + + # No license in the the target + self.assertFalse(License.objects.scope(self.dataspace_target).exists()) + + copied_object = copy_object(self.component1, self.dataspace_target, self.user) + copied_license = copied_object.licenses.all()[0] + self.assertEqual(self.license1.key, copied_license.key) + # Object are not the same DB entry but they share the same UUID + self.assertNotEqual(self.license1.id, copied_license.id) + self.assertEqual(self.license1.uuid, copied_license.uuid) + + copy_object(self.component2, self.dataspace_target, self.user) + matched_license = copied_object.licenses.all()[0] + self.assertNotEqual(self.license1.id, matched_license.id) + self.assertEqual(self.license1.uuid, matched_license.uuid) + + def test_copy_update_component_with_license_expression_and_assigned_licenses(self): + self.assertFalse(self.component1.license_expression) + copied_component = copy_object(self.component1, self.dataspace_target, self.user) + copied_license = copy_object(self.license1, self.dataspace_target, self.user) + + # Same referenced license in both expression + self.component1.license_expression = self.license1.key + self.component1.save() + self.assertEqual(1, self.component1.licenses.count()) + + copied_component.license_expression = copied_license.key + copied_component.save() + self.assertEqual(1, copied_component.licenses.count()) + + # It is important that the ComponentAssignedLicense do not share the same UUID + # for the purpose of this test. + self.assertNotEqual( + self.component1.componentassignedlicense_set.get().uuid, + copied_component.componentassignedlicense_set.get().uuid, + ) + + license2 = License.objects.create( + key="l2", name="l2", short_name="l2", dataspace=self.dataspace1, owner=self.owner1 + ) + self.component1.license_expression = license2.key + self.component1.save() + # License is not available in the target, will be copy along update. + updated_component = copy_object( + self.component1, self.dataspace_target, self.user, update=True + ) + # License is copied along the update. + self.assertEqual(1, updated_component.licenses.count()) + copied_license = updated_component.licenses.all()[0] + self.assertEqual(license2.uuid, copied_license.uuid) + self.assertEqual(self.component1.license_expression, updated_component.license_expression) + + license3 = License.objects.create( + key="l3", name="l3", short_name="l3", dataspace=self.dataspace1, owner=self.owner1 + ) + self.component1.license_expression = license3.key + self.component1.save() + # Copy the license in the target before running the update + copy_object(license3, self.dataspace_target, self.user) + updated_component = copy_object( + self.component1, self.dataspace_target, self.user, update=True + ) + self.assertEqual(1, updated_component.licenses.count()) + self.assertEqual(license3.uuid, updated_component.licenses.all()[0].uuid) + self.assertEqual(self.component1.license_expression, updated_component.license_expression) + + def test_copy_update_subcomponent_with_license_expression_and_uuid_conflict(self): + self.assertFalse(Component.objects.scope(self.dataspace_target).exists()) + self.assertFalse(Subcomponent.objects.scope(self.dataspace_target).exists()) + + self.sub_component1.license_expression = self.license1.key + self.sub_component1.save() + + copy_object(self.sub_component1.parent, self.dataspace_target, self.user) + copied_sub = Subcomponent.objects.latest("id") + self.assertEqual(self.dataspace_target, copied_sub.dataspace) + self.assertEqual(self.sub_component1.license_expression, copied_sub.license_expression) + self.assertEqual(self.license1.uuid, copied_sub.licenses.get().uuid) + self.assertEqual( + self.sub_component1.subcomponentassignedlicense_set.get().uuid, + copied_sub.subcomponentassignedlicense_set.get().uuid, + ) + + # Same UUID: update runs fine + copy_object(self.sub_component1.parent, self.dataspace_target, self.user, update=True) + + copied_assigned_license = copied_sub.subcomponentassignedlicense_set.get() + copied_assigned_license.uuid = uuid.uuid4() + copied_assigned_license.save() + + copy_object(self.sub_component1.parent, self.dataspace_target, self.user, update=True) + + def test_object_copy_component_with_fields_exclude_propagation(self): + # We want to make sure that a child Component copied during a Component + # copy inherit from the same excluded_field as its parent. + self.component1.admin_notes = "Admin notes 1" + self.component1.configuration_status = self.status1 + self.component1.guidance = "Guidance 1" + self.component1.save() + + self.component2.admin_notes = "Admin notes 2" + self.component2.configuration_status = self.status1 + self.component2.guidance = "Guidance 2" + self.component2.save() + + excluded_fields = ["configuration_status", "guidance"] + exclude = {self.component1.__class__: excluded_fields} + + # self.sub_component5 is (parent=self.component2, child=self.component1) + # so component1 is copied as the child of component2 + copied_object = copy_object( + self.component2, self.dataspace_target, self.user, exclude=exclude + ) + + self.assertFalse(copied_object.configuration_status) + self.assertFalse(copied_object.guidance) + self.assertEqual("Admin notes 2", copied_object.admin_notes) + + # Now let's make sure those exclude were propagated to the m2m copy + copied_child = Component.objects.get( + uuid=self.component1.uuid, dataspace=self.dataspace_target + ) + + self.assertFalse(copied_child.configuration_status) + self.assertFalse(copied_child.guidance) + self.assertEqual("Admin notes 1", copied_child.admin_notes) + + def test_copy_component_with_assigned_licenses_m2m_already_in_target_with_different_uuid(self): + self.client.login(username="test", password="secret") + + # Remove the type to limit the cascade copy + self.component1.type = None + self.component1.license_expression = self.license1.key + self.component1.save() + + owner1_in_target = Owner.objects.create( + uuid=self.owner1.uuid, name=self.owner1.name, dataspace=self.dataspace_target + ) + + # Also creating a License in the target, using the license1 unique + # key but not sharing the same UUID + License.objects.create( + key=self.license1.key, + name="License1", + short_name="License1", + owner=owner1_in_target, + dataspace=self.dataspace_target, + ) + + url = reverse("admin:component_catalog_component_copy") + data = { + "ct": str(ContentType.objects.get_for_model(Component).pk), + "copy_candidates": str(self.component1.id), + "source": self.component1.dataspace.id, + "targets": [self.dataspace_target.id], + "form-TOTAL_FORMS": 1, + "form-INITIAL_FORMS": 1, + "form-0-ct": ContentType.objects.get_for_model(Subcomponent).pk, + } + + response = self.client.post(url, data) + self.assertContains(response, "

      Errors for the following Components.

      ", html=True) + self.assertContains(response, "duplicate key value violates unique constraint") + + def test_copy_component_with_assigned_licenses_m2m_not_in_target(self): + self.client.login(username="test", password="secret") + + self.component1.license_expression = self.license1.key + self.component1.save() + + self.assertFalse(License.objects.scope(self.dataspace_target).exists()) + url = reverse("admin:component_catalog_component_copy") + + data = { + "ct": str(ContentType.objects.get_for_model(Component).pk), + "copy_candidates": str(self.component1.id), + "source": self.component1.dataspace.id, + "targets": [self.dataspace_target.id], + "form-TOTAL_FORMS": 1, + "form-INITIAL_FORMS": 1, + "form-0-ct": ContentType.objects.get_for_model(Subcomponent).pk, + } + + self.client.post(url, data) + copied_object = Component.objects.get( + uuid=self.component1.uuid, dataspace=self.dataspace_target + ) + self.assertEqual(self.component1.license_expression, copied_object.license_expression) + self.assertEqual(self.component1.licenses.count(), copied_object.licenses.count()) + + def test_copy_component_with_parentchild1(self): + children_count = self.component3.get_children().count() + parents_count = self.component3.get_parents().count() + # Making sure the Component is involve in a Subcomponent relations as + # a parent and as a child. + self.assertTrue(children_count) + self.assertTrue(parents_count) + + copied_object = copy_object(self.component3, self.dataspace_target, self.user) + + # We do not copied parents along Component copy + self.assertFalse(copied_object.get_parents()) + self.assertEqual(children_count, copied_object.get_children().count()) + + def test_copy_component_ticket6635(self): + component_type_original_count = ComponentType.objects.count() + owner_original_count = Owner.objects.count() + + Component.objects.create( + name="Component_duplicate1", + version="0.1", + type=self.type1, + owner=self.owner1, + dataspace=self.dataspace1, + ) + type1_duplicate = ComponentType.objects.create( + label="Type1", notes="notes", dataspace=self.dataspace_target, uuid=self.type1.uuid + ) + owner1_duplicate = Owner.objects.create( + name="owner1", dataspace=self.dataspace_target, uuid=self.owner1.uuid + ) + Component.objects.create( + name="Component_duplicate1", + version="0.1", + type=type1_duplicate, + owner=owner1_duplicate, + dataspace=self.dataspace_target, + ) + + copy_object(self.component1, self.dataspace_target, self.user) + self.assertEqual(component_type_original_count + 1, ComponentType.objects.count()) + self.assertEqual(owner_original_count + 1, Owner.objects.count()) + + def test_copy_component_with_uuid_matching_type(self): + # The purpose of thus is to copy a Component which has a Type that will + # be matched in the target as it share the same UUID + self.client.login(username="test", password="secret") + + # The copied Type needs to have the same UUID as the reference one + ComponentType.objects.create( + label="Type1", notes="notes", uuid=self.type1.uuid, dataspace=self.dataspace_target + ) + + type_original_count = ComponentType.objects.count() + owner_original_count = Owner.objects.count() + + copy_object(self.component1, self.dataspace_target, self.user) + + # Same as it was matched and not copied + self.assertEqual(type_original_count, ComponentType.objects.count()) + # +1 as the Owner was copied along the Component + self.assertEqual(owner_original_count + 1, Owner.objects.count()) + + def test_copy_component_with_assigned_package(self): + package1 = Package.objects.create(filename="file.zip", dataspace=self.component2.dataspace) + ComponentAssignedPackage.objects.create( + component=self.component2, package=package1, dataspace=self.component2.dataspace + ) + + package_original_count = Package.objects.count() + assigned_package_original_count = ComponentAssignedPackage.objects.count() + + copied_object = copy_object(self.component2, self.dataspace_target, self.user) + + self.assertTrue(copied_object.packages.all()) + self.assertEqual(package_original_count + 1, Package.objects.count()) + self.assertEqual( + assigned_package_original_count + 1, ComponentAssignedPackage.objects.count() + ) + + def test_component_copy_m2m_update_depth(self): + self.component1.license_expression = self.license1.key + self.component1.save() + + # Let's copy self.component1 in the target + copied_object = copy_object(self.component1, self.dataspace_target, self.user) + self.assertEqual(copied_object.licenses.count(), self.component1.licenses.count()) + # The License, LicenseTag, LicenseAssignedTag were copied along. + copied_assigned_tag = LicenseAssignedTag.objects.get( + uuid=self.license_assigned_tag1.uuid, dataspace=self.dataspace_target + ) + self.assertEqual(self.license_assigned_tag1.value, copied_assigned_tag.value) + + # Changing the reference License and AssignedTag + self.license1.name = "NEW NAME" + self.license1.save() + self.license_assigned_tag1.value = False + self.license_assigned_tag1.save() + + # Copy again with update + copy_object(self.component1, self.dataspace_target, self.user, update=True) + copied_license = License.objects.get( + uuid=self.license1.uuid, dataspace=self.dataspace_target + ) + copied_assigned_tag = LicenseAssignedTag.objects.get( + uuid=self.license_assigned_tag1.uuid, dataspace=self.dataspace_target + ) + + # Change on m2m related object was not impacted + self.assertEqual("License1", copied_license.name) + # No impact on deeper m2m relation neither + self.assertEqual(True, copied_assigned_tag.value) + + def test_copy_package_excluding_identifier(self): + self.client.login(username="test", password="secret") + + package1 = Package.objects.create(filename="p1.zip", dataspace=self.dataspace1) + url = reverse("admin:component_catalog_package_copy") + + data = { + "ct": str(ContentType.objects.get_for_model(Package).pk), + "copy_candidates": str(package1.id), + "source": package1.dataspace.id, + "targets": [self.dataspace_target.id], + "exclude_copy": ["filename"], + "form-TOTAL_FORMS": 1, + "form-INITIAL_FORMS": 1, + "form-0-ct": ContentType.objects.get_for_model(PackageAssignedLicense).pk, + } + + response = self.client.post(url, data) + self.assertContains( + response, '
    • 1 object was not copied/updated.
    • ', html=True + ) + self.assertContains(response, "
    • ['package_url or filename required']
    • ", html=True) diff --git a/component_catalog/tests/test_api.py b/component_catalog/tests/test_api.py new file mode 100644 index 00000000..4e3336f3 --- /dev/null +++ b/component_catalog/tests/test_api.py @@ -0,0 +1,1718 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# DejaCode is a trademark of nexB Inc. +# SPDX-License-Identifier: AGPL-3.0-only +# See https://github.com/nexB/dejacode for support or download. +# See https://aboutcode.org for more information about AboutCode FOSS projects. +# + +import datetime +import json +import uuid +from unittest import mock + +from django.contrib.contenttypes.models import ContentType +from django.core import mail +from django.test import TestCase +from django.test import override_settings +from django.urls import reverse + +from rest_framework import status +from rest_framework.exceptions import ErrorDetail +from rest_framework.test import APIClient + +from component_catalog.api import PackageAPIFilterSet +from component_catalog.fuzzy import FuzzyPackageNameSearch +from component_catalog.models import Component +from component_catalog.models import ComponentAssignedPackage +from component_catalog.models import ComponentKeyword +from component_catalog.models import ComponentStatus +from component_catalog.models import ComponentType +from component_catalog.models import Package +from component_catalog.models import Subcomponent +from dje.models import Dataspace +from dje.models import ExternalReference +from dje.models import ExternalSource +from dje.models import History +from dje.tests import MaxQueryMixin +from dje.tests import add_perm +from dje.tests import add_perms +from dje.tests import create_admin +from dje.tests import create_superuser +from dje.tests import create_user +from license_library.models import License +from license_library.models import LicenseCategory +from license_library.models import LicenseChoice +from organization.models import Owner +from policy.models import UsagePolicy + + +@override_settings( + EMAIL_HOST_USER="user", + EMAIL_HOST_PASSWORD="password", + EMAIL_HOST="localhost", + EMAIL_PORT=25, + DEFAULT_FROM_EMAIL="webmaster@localhost", + SITE_URL="server.dejacode.nexb", +) +class ComponentAPITestCase(TestCase): + def setUp(self): + self.dataspace = Dataspace.objects.create(name="nexB") + self.alternate_dataspace = Dataspace.objects.create(name="Alternate") + + self.base_user = create_user("base_user", self.dataspace) + self.super_user = create_superuser( + "super_user", self.dataspace, data_email_notification=True + ) + self.admin_user = create_admin("admin_user", self.dataspace) + self.alternate_super_user = create_superuser("alternate_user", self.alternate_dataspace) + + self.type1 = ComponentType.objects.create(label="Type1", dataspace=self.dataspace) + self.status1 = ComponentStatus.objects.create( + label="Status1", default_on_addition=True, dataspace=self.dataspace + ) + + self.component_list_url = reverse("api_v2:component-list") + + self.component1 = Component.objects.create( + name="c1", dataspace=self.dataspace, legal_reviewed=True + ) + self.component1_detail_url = reverse("api_v2:component-detail", args=[self.component1.uuid]) + + self.component2 = Component.objects.create(name="c2", dataspace=self.dataspace) + self.component2_detail_url = reverse("api_v2:component-detail", args=[self.component2.uuid]) + + self.component3 = Component.objects.create(name="c3", dataspace=self.dataspace) + self.component3_detail_url = reverse("api_v2:component-detail", args=[self.component3.uuid]) + + ext_source1 = ExternalSource.objects.create(label="GitHub", dataspace=self.dataspace) + + ExternalReference.objects.create_for_content_object(self.component1, ext_source1, "REF1") + ExternalReference.objects.create_for_content_object(self.component1, ext_source1, "REF2") + ExternalReference.objects.create_for_content_object(self.component1, ext_source1, "REF3") + + def test_api_component_list_endpoint_results(self): + self.client.login(username="super_user", password="secret") + # with self.assertNumQueries(10): + response = self.client.get(self.component_list_url) + + self.assertContains(response, '"count":3,') + self.assertContains(response, self.component1_detail_url) + self.assertContains(response, self.component2_detail_url) + self.assertEqual(3, response.data["count"]) + + def test_api_component_list_endpoint_options(self): + UsagePolicy.objects.create( + label="LicensePolicy", + icon="icon", + content_type=ContentType.objects.get_for_model(License), + dataspace=self.dataspace, + ) + + UsagePolicy.objects.create( + label="ComponentPolicy", + icon="icon", + content_type=ContentType.objects.get_for_model(Component), + dataspace=self.dataspace, + ) + + client = APIClient() + client.login(username="super_user", password="secret") + response = client.options(self.component_list_url, format="json") + actions_post = response.data["actions"]["POST"] + + self.assertFalse(bool(actions_post["owner"].get("choices"))) + self.assertEqual("field", actions_post["owner"].get("type")) + + self.assertFalse(bool(actions_post["type"].get("choices"))) + self.assertFalse(bool(actions_post["configuration_status"].get("choices"))) + self.assertFalse(bool(actions_post["usage_policy"].get("choices"))) + + def test_api_component_list_endpoint_search(self): + self.client.login(username="super_user", password="secret") + data = {"search": self.component1.name} + response = self.client.get(self.component_list_url, data) + self.assertEqual(1, response.data["count"]) + self.assertContains(response, self.component1_detail_url) + self.assertNotContains(response, self.component2_detail_url) + + # Search is case in-sensitive + data = {"search": self.component1.name.upper()} + response = self.client.get(self.component_list_url, data) + self.assertEqual(1, response.data["count"]) + self.assertContains(response, self.component1_detail_url) + self.assertNotContains(response, self.component2_detail_url) + + def test_api_component_list_endpoint_filters(self): + self.client.login(username="super_user", password="secret") + data = {"is_active": True} + response = self.client.get(self.component_list_url, data) + self.assertEqual(3, response.data["count"]) + self.assertContains(response, self.component1_detail_url) + self.assertContains(response, self.component2_detail_url) + self.assertContains(response, self.component3_detail_url) + + data = {"is_active": False} + response = self.client.get(self.component_list_url, data) + self.assertEqual(0, response.data["count"]) + self.assertNotContains(response, self.component1_detail_url) + self.assertNotContains(response, self.component2_detail_url) + self.assertNotContains(response, self.component3_detail_url) + + data = {"legal_reviewed": True} + response = self.client.get(self.component_list_url, data) + self.assertEqual(1, response.data["count"]) + self.assertContains(response, self.component1_detail_url) + self.assertNotContains(response, self.component2_detail_url) + + self.component1.license_expression = "mit AND apache-2.0" + self.component1.save() + data = {"license_expression": "mit"} + response = self.client.get(self.component_list_url, data) + self.assertEqual(1, response.data["count"]) + self.assertContains(response, self.component1_detail_url) + self.assertNotContains(response, self.component2_detail_url) + data = {"license_expression": "apache"} + response = self.client.get(self.component_list_url, data) + self.assertEqual(1, response.data["count"]) + self.assertContains(response, self.component1_detail_url) + self.assertNotContains(response, self.component2_detail_url) + + self.component1.keywords = ["Application", "Security"] + self.component1.save() + data = {"keywords": "application"} + response = self.client.get(self.component_list_url, data) + self.assertEqual(1, response.data["count"]) + self.assertContains(response, self.component1_detail_url) + self.assertNotContains(response, self.component2_detail_url) + + def test_api_component_list_endpoint_version_filters(self): + self.client.login(username="super_user", password="secret") + + self.component1.version = "1.0" + self.component1.save() + self.component2.version = "2.0" + self.component2.save() + self.component3.version = "3.0" + self.component3.save() + + data = {"version": "1.0"} + response = self.client.get(self.component_list_url, data) + self.assertEqual(1, response.data["count"]) + self.assertContains(response, self.component1_detail_url) + self.assertNotContains(response, self.component2_detail_url) + self.assertNotContains(response, self.component3_detail_url) + + data = {"version__gt": "1.0"} + response = self.client.get(self.component_list_url, data) + self.assertEqual(2, response.data["count"]) + self.assertNotContains(response, self.component1_detail_url) + self.assertContains(response, self.component2_detail_url) + self.assertContains(response, self.component3_detail_url) + + data = {"version__lt": "2.0"} + response = self.client.get(self.component_list_url, data) + self.assertEqual(1, response.data["count"]) + self.assertContains(response, self.component1_detail_url) + self.assertNotContains(response, self.component2_detail_url) + self.assertNotContains(response, self.component3_detail_url) + + data = {"version__gt": "1.0", "version__lt": "3.0"} + response = self.client.get(self.component_list_url, data) + self.assertEqual(1, response.data["count"]) + self.assertNotContains(response, self.component1_detail_url) + self.assertContains(response, self.component2_detail_url) + self.assertNotContains(response, self.component3_detail_url) + + def test_api_component_list_endpoint_multiple_char_filters(self): + self.client.login(username="super_user", password="secret") + owner1 = Owner.objects.create(name="Owner1", dataspace=self.dataspace) + self.component1.owner = owner1 + self.component1.save() + owner2 = Owner.objects.create(name="Owner2", dataspace=self.dataspace) + self.component2.owner = owner2 + self.component2.save() + owner3 = Owner.objects.create(name="Own , er3", dataspace=self.dataspace) + self.component3.owner = owner3 + self.component3.save() + + filters = "?owner={}&owner={}".format(owner1.name, owner3.name) + response = self.client.get(self.component_list_url + filters) + self.assertEqual(2, response.data["count"]) + self.assertContains(response, self.component1_detail_url) + self.assertNotContains(response, self.component2_detail_url) + self.assertContains(response, self.component3_detail_url) + + def test_api_component_list_endpoint_name_version_filter(self): + self.client.login(username="super_user", password="secret") + + filters = "?name_version={}:{}".format(self.component1.name, self.component1.version) + response = self.client.get(self.component_list_url + filters) + self.assertEqual(1, response.data["count"]) + self.assertContains(response, self.component1_detail_url) + self.assertNotContains(response, self.component2_detail_url) + self.assertNotContains(response, self.component3_detail_url) + + # Supports multi-value + filters += "&name_version={}:{}".format(self.component3.name, self.component3.version) + response = self.client.get(self.component_list_url + filters) + self.assertEqual(2, response.data["count"]) + self.assertContains(response, self.component1_detail_url) + self.assertNotContains(response, self.component2_detail_url) + self.assertContains(response, self.component3_detail_url) + + def test_api_component_list_endpoint_uuid_filter(self): + self.client.login(username="super_user", password="secret") + data = {"uuid": ""} + response = self.client.get(self.component_list_url, data) + self.assertEqual(3, response.data["count"]) + response = self.client.get(self.component_list_url + "?uuid=") + self.assertEqual(3, response.data["count"]) + + data = {"uuid": "invalid"} + expected = { + "uuid": [ + ErrorDetail( + string="Select a valid choice. invalid is not one of " "the available choices.", + code="invalid_choice", + ) + ] + } + response = self.client.get(self.component_list_url, data) + self.assertEqual(expected, response.data) + response = self.client.get(self.component_list_url + "?uuid=invalid") + self.assertEqual(expected, response.data) + + data = {"uuid": self.component1.uuid} + response = self.client.get(self.component_list_url, data) + self.assertEqual(1, response.data["count"]) + response = self.client.get(self.component_list_url + f"?uuid={self.component1.uuid}") + self.assertEqual(1, response.data["count"]) + + data = {"uuid": [self.component1.uuid, self.component2.uuid]} + response = self.client.get(self.component_list_url, data) + self.assertEqual(2, response.data["count"]) + response = self.client.get( + self.component_list_url + f"?uuid={self.component1.uuid}&uuid={self.component2.uuid}" + ) + self.assertEqual(2, response.data["count"]) + + def test_api_component_detail_endpoint(self): + package1 = Package.objects.create(filename="file.zip", dataspace=self.dataspace) + ComponentAssignedPackage.objects.create( + component=self.component1, package=package1, dataspace=self.dataspace + ) + package1_detail_url = reverse("api_v2:package-detail", args=[package1.uuid]) + owner = Owner.objects.create(name="Owner1", dataspace=self.dataspace) + self.component1.owner = owner + self.component1.save() + owner_detail_url = reverse("api_v2:owner-detail", args=[owner.uuid]) + self.client.login(username="super_user", password="secret") + + # with self.assertNumQueries(9): + response = self.client.get(self.component1_detail_url) + + self.assertContains(response, self.component1_detail_url) + self.assertEqual(str(self.component1), response.data["display_name"]) + self.assertIn(self.component1_detail_url, response.data["api_url"]) + self.assertEqual(str(self.component1.uuid), response.data["uuid"]) + self.assertEqual(self.component1.name, response.data["name"]) + self.assertEqual(self.component1.type, response.data["type"]) + self.assertEqual(0, response.data["curation_level"]) + self.assertIn(package1_detail_url, response.data["packages"][0]) + self.assertEqual(3, len(response.data["external_references"])) + self.assertIn(owner_detail_url, response.data["owner"]) + self.assertEqual(owner.name, response.data["owner_name"]) + self.assertEqual(owner.name, response.data["owner_abcd"]["name"]) + self.assertEqual(str(owner.uuid), response.data["owner_abcd"]["uuid"]) + self.assertIn(owner_detail_url, response.data["owner_abcd"]["api_url"]) + self.assertEqual(self.component1.urn, response.data["urn"]) + + package_abcd = response.data["packages_abcd"][0] + self.assertEqual(package1.download_url, package_abcd["download_url"]) + self.assertEqual(package1.filename, package_abcd["filename"]) + self.assertEqual(str(package1.uuid), package_abcd["uuid"]) + self.assertIn(package1_detail_url, package_abcd["api_url"]) + + def test_api_component_licenses_summary_field(self): + self.client.login(username="super_user", password="secret") + + owner = Owner.objects.create(name="Owner1", dataspace=self.dataspace) + license1 = License.objects.create( + key="license1", name="License1", short_name="L1", owner=owner, dataspace=self.dataspace + ) + license2 = License.objects.create( + key="license2", name="License2", short_name="L2", owner=owner, dataspace=self.dataspace + ) + + self.component1.license_expression = license1.key + self.component1.save() + + response = self.client.get(self.component1_detail_url) + expected = { + "category": None, + "is_primary": True, + "key": license1.key, + "name": license1.name, + "short_name": license1.short_name, + "type": None, + } + self.assertEqual([expected], response.data["licenses_summary"]) + + category1 = LicenseCategory.objects.create(label="C1", text="a", dataspace=self.dataspace) + license1.category = category1 + license1.save() + response = self.client.get(self.component1_detail_url) + expected["category"] = category1.label + self.assertEqual([expected], response.data["licenses_summary"]) + + category1.license_type = "type1" + category1.save() + response = self.client.get(self.component1_detail_url) + expected["type"] = category1.license_type + self.assertEqual([expected], response.data["licenses_summary"]) + + self.component1.license_expression = "({} OR {})".format(license2.key, license1.key) + self.component1.save() + response = self.client.get(self.component1_detail_url) + licenses_summary = response.data["licenses_summary"] + self.assertEqual(2, len(licenses_summary)) + self.assertEqual(license1.key, licenses_summary[0]["key"]) + self.assertFalse(licenses_summary[0]["is_primary"]) + self.assertEqual(license2.key, licenses_summary[1]["key"]) + self.assertTrue(licenses_summary[1]["is_primary"]) + + def test_api_component_license_choices_fields(self): + self.client.login(username="super_user", password="secret") + + owner = Owner.objects.create(name="Owner1", dataspace=self.dataspace) + license1 = License.objects.create( + key="license1", name="License1", short_name="L1", owner=owner, dataspace=self.dataspace + ) + license2 = License.objects.create( + key="license2", name="License2", short_name="L2", owner=owner, dataspace=self.dataspace + ) + + response = self.client.get(self.component1_detail_url) + self.assertEqual([], response.data["license_choices"]) + self.assertIsNone(response.data["license_choices_expression"]) + + self.component1.license_expression = license1.key + self.component1.save() + response = self.client.get(self.component1_detail_url) + self.assertEqual([], response.data["license_choices"]) + self.assertIsNone(response.data["license_choices_expression"]) + + LicenseChoice.objects.create( + from_expression=license1.key, to_expression=license2.key, dataspace=self.dataspace + ) + response = self.client.get(self.component1_detail_url) + expected = [{"key": "license2", "short_name": "L2"}] + self.assertEqual(expected, response.data["license_choices"]) + self.assertEqual(license2.key, response.data["license_choices_expression"]) + + def test_api_component_list_endpoint_filter_by_last_modified_date(self): + self.client.login(username="super_user", password="secret") + now = datetime.datetime.now() + one_hour = datetime.timedelta(hours=1) + + # No filters + response = self.client.get(self.component_list_url) + self.assertEqual(3, response.data["count"]) + + # 1 hour in the past + data = {"last_modified_date": self.component1.last_modified_date - one_hour} + response = self.client.get(self.component_list_url, data) + self.assertEqual(3, response.data["count"]) + self.assertContains(response, self.component1_detail_url) + self.assertContains(response, self.component2_detail_url) + + # 1 hour in the future + data = {"last_modified_date": self.component1.last_modified_date + one_hour} + response = self.client.get(self.component_list_url, data) + self.assertEqual(0, response.data["count"]) + + # Modified after now + data = {"last_modified_date": now} + response = self.client.get(self.component_list_url, data) + self.assertEqual(0, response.data["count"]) + + self.component1.save() + response = self.client.get(self.component_list_url, data) + self.assertEqual(1, response.data["count"]) + self.assertContains(response, self.component1_detail_url) + self.assertNotContains(response, self.component2_detail_url) + + # Same date as last_modified_date + data = {"last_modified_date": self.component1.last_modified_date} + response = self.client.get(self.component_list_url, data) + self.assertEqual(1, response.data["count"]) + self.assertContains(response, self.component1_detail_url) + self.assertNotContains(response, self.component2_detail_url) + + # "YYYY-MM-DD" and "YYYY-MM-DD HH:MM" formats supported + data = {"last_modified_date": "2000-01-01"} + response = self.client.get(self.component_list_url, data) + self.assertEqual(3, response.data["count"]) + self.assertContains(response, self.component1_detail_url) + self.assertContains(response, self.component2_detail_url) + data = {"last_modified_date": "9999-01-01"} + response = self.client.get(self.component_list_url, data) + self.assertEqual(0, response.data["count"]) + data = {"last_modified_date": "2000-01-01 10:10"} + response = self.client.get(self.component_list_url, data) + self.assertEqual(3, response.data["count"]) + self.assertContains(response, self.component1_detail_url) + self.assertContains(response, self.component2_detail_url) + + def test_api_component_list_endpoint_filter_by_curation_level(self): + self.client.login(username="super_user", password="secret") + data = {"curation_level": 90} + response = self.client.get(self.component_list_url, data) + self.assertEqual(0, response.data["count"]) + + self.component1.curation_level = 11 + self.component1.save() + self.component2.curation_level = 9 + self.component2.save() + data = {"curation_level": 10} + response = self.client.get(self.component_list_url, data) + self.assertEqual(1, response.data["count"]) + self.assertContains(response, self.component1_detail_url) + self.assertNotContains(response, self.component2_detail_url) + + def test_api_component_list_endpoint_filter_by_is_active(self): + self.client.login(username="super_user", password="secret") + + self.component3.is_active = False + self.component3.save() + + self.assertEqual(2, Component.objects.filter(is_active=True).count()) + self.assertEqual(1, Component.objects.filter(is_active=False).count()) + + for value in ["on", "2", True, "True", "true", "t", "T", "yes", "Yes", "y", "Y"]: + response = self.client.get(self.component_list_url, {"is_active": value}) + self.assertEqual(2, response.data["count"]) + + for value in ["3", "False", False, "false", "f", "F", "no", "No", "n", "N"]: + response = self.client.get(self.component_list_url, {"is_active": value}) + self.assertEqual(1, response.data["count"]) + + def test_api_component_endpoint_create_minimal(self): + self.client.login(username="super_user", password="secret") + data = {"name": "RapidJSON"} + response = self.client.post(self.component_list_url, data) + self.assertEqual(status.HTTP_201_CREATED, response.status_code) + component = Component.objects.get(name="RapidJSON") + self.assertTrue(component.is_active) + self.assertTrue(response.data["is_active"]) + + def test_api_component_endpoint_create_update_keywords(self): + keyword1 = ComponentKeyword.objects.create(label="Keyword1", dataspace=self.dataspace) + keyword2 = ComponentKeyword.objects.create(label="Keyword2", dataspace=self.dataspace) + # For scoping sanity check + ComponentKeyword.objects.create(label=keyword1.label, dataspace=self.alternate_dataspace) + + self.client.login(username="super_user", password="secret") + data = { + "name": "Comp1", + "keywords": [keyword1.label, keyword2.label], + } + response = self.client.post(self.component_list_url, data) + self.assertEqual(status.HTTP_201_CREATED, response.status_code) + c1 = Component.objects.get(name="Comp1") + self.assertEqual(2, len(c1.keywords)) + self.assertEqual(7, c1.completion_level) + + data = { + "name": "Comp3", + "keywords": ["non-existing"], + } + response = self.client.post(self.component_list_url, data) + self.assertEqual(201, response.status_code) + new_keyword = ComponentKeyword.objects.get(label="non-existing") + self.assertTrue(new_keyword) + self.assertEqual([new_keyword.label], Component.objects.get(name="Comp3").keywords) + + # No keywords + data = { + "name": "Comp2", + "keywords": [], + } + response = self.client.post(self.component_list_url, data) + self.assertEqual(status.HTTP_201_CREATED, response.status_code) + c2 = Component.objects.get(name="Comp2") + self.assertEqual([], c2.keywords) + + data = { + "name": "CompNoKeywords", + "keywords": "", + } + response = self.client.post(self.component_list_url, data) + self.assertEqual(status.HTTP_201_CREATED, response.status_code) + self.assertEqual([], Component.objects.get(name=data["name"]).keywords) + + # Update + data = json.dumps({"keywords": [keyword1.label]}) + c2_api_url = reverse("api_v2:component-detail", args=[c2.uuid]) + response = self.client.patch(c2_api_url, data=data, content_type="application/json") + self.assertEqual(status.HTTP_200_OK, response.status_code) + c2.refresh_from_db() + self.assertEqual([keyword1.label], c2.keywords) + + data = json.dumps({"keywords": [keyword2.label]}) + response = self.client.patch(c2_api_url, data=data, content_type="application/json") + self.assertEqual(status.HTTP_200_OK, response.status_code) + c2.refresh_from_db() + self.assertEqual([keyword2.label], c2.keywords) + + data = json.dumps({"keywords": [keyword1.label, keyword2.label]}) + response = self.client.patch(c2_api_url, data=data, content_type="application/json") + self.assertEqual(status.HTTP_200_OK, response.status_code) + c2.refresh_from_db() + self.assertEqual([keyword1.label, keyword2.label], c2.keywords) + + def test_api_component_endpoint_create_update_m2m_packages(self): + package1 = Package.objects.create(filename="package1.zip", dataspace=self.dataspace) + package1_detail_url = reverse("api_v2:package-detail", args=[package1.uuid]) + package2 = Package.objects.create(filename="package2.zip", dataspace=self.dataspace) + # For scoping sanity check + Package.objects.create(filename="package1.zip", dataspace=self.alternate_dataspace) + + self.client.login(username="super_user", password="secret") + data = { + "name": "Comp1", + # api url and uuid are supported + "packages": [package1_detail_url, package2.uuid], + } + response = self.client.post(self.component_list_url, data) + self.assertEqual(status.HTTP_201_CREATED, response.status_code) + c1 = Component.objects.get(name="Comp1") + self.assertEqual(2, c1.packages.count()) + self.assertEqual(7, c1.completion_level) + + data = { + "name": "Comp2", + "packages": ["non-existing"], + } + response = self.client.post(self.component_list_url, data) + self.assertEqual(400, response.status_code) + expected = {"packages": ["Invalid hyperlink - No URL match."]} + self.assertEqual(expected, response.data) + + # No packages + data = { + "name": "Comp2", + "packages": [], + } + response = self.client.post(self.component_list_url, data) + self.assertEqual(status.HTTP_201_CREATED, response.status_code) + c2 = Component.objects.get(name="Comp2") + self.assertEqual(0, c2.packages.count()) + + # Update + data = json.dumps({"packages": [package1_detail_url]}) + c2_api_url = reverse("api_v2:component-detail", args=[c2.uuid]) + response = self.client.patch(c2_api_url, data=data, content_type="application/json") + self.assertEqual(status.HTTP_200_OK, response.status_code) + self.assertEqual(1, c2.packages.count()) + + data = json.dumps({"packages": [str(package2.uuid)]}) + response = self.client.patch(c2_api_url, data=data, content_type="application/json") + self.assertEqual(status.HTTP_200_OK, response.status_code) + self.assertEqual(2, c2.packages.count()) + + def test_api_component_endpoint_create_update_package1(self): + package1 = Package.objects.create(filename="package1.zip", dataspace=self.dataspace) + package1_detail_url = reverse("api_v2:package-detail", args=[package1.uuid]) + package2 = Package.objects.create(filename="package2.zip", dataspace=self.dataspace) + + self.client.login(username="super_user", password="secret") + data = { + "name": "Comp1", + # api url and uuid are supported + "packages": [package1_detail_url, package2.uuid], + } + response = self.client.post(self.component_list_url, data) + self.assertEqual(status.HTTP_201_CREATED, response.status_code) + c1 = Component.objects.get(name="Comp1") + self.assertEqual(2, c1.packages.count()) + + def test_api_component_endpoint_create(self): + self.client.login(username="super_user", password="secret") + owner = Owner.objects.create(name="Owner1", dataspace=self.dataspace) + license1 = License.objects.create( + key="license1", name="License1", short_name="L1", owner=owner, dataspace=self.dataspace + ) + license2 = License.objects.create( + key="license2", name="License2", short_name="L2", owner=owner, dataspace=self.dataspace + ) + policy = UsagePolicy.objects.create( + label="ComponentPolicy", + icon="icon", + content_type=ContentType.objects.get_for_model(Component), + dataspace=self.dataspace, + ) + data = { + "name": "RapidJSON", + "version": "1.0.2", + "owner": owner.name, + "copyright": "Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip.", + "license_expression": "{} AND {}".format(license1.key, license2.key), + "reference_notes": "", + "release_date": "2015-05-14", + "description": "RapidJSON is a JSON parser and generator for C++." + " It was inspired by RapidXml.", + "homepage_url": "http://rapidjson.org/", + "vcs_url": "https://github.com/miloyip/rapidjson.git", + "code_view_url": "https://github.com/miloyip/rapidjson", + "bug_tracking_url": "https://github.com/miloyip/rapidjson/issues", + "primary_language": "C++", + "type": "", + "notice_text": "Tencent is pleased to support the open source community by" + " making RapidJSON available.", + "is_license_notice": True, + "is_copyright_notice": True, + "is_notice_in_codebase": True, + "notice_filename": "license.txt", + "notice_url": "https://github.com/miloyip/rapidjson/blob/master/license.txt", + "dependencies": [], + "configuration_status": "", + "is_active": False, + "usage_policy": policy.label, + "curation_level": 45, + "guidance": "There are two files licensed under BSD-Modified", + "admin_notes": "", + "approval_reference": "", + } + response = self.client.post(self.component_list_url, data) + self.assertEqual(status.HTTP_201_CREATED, response.status_code) + + component = Component.objects.latest("id") + self.assertEqual(83, component.completion_level) + self.assertEqual(2, component.licenses.count()) + + self.assertFalse(History.objects.get_for_object(component).exists()) + self.assertEqual(self.super_user, component.created_by) + self.assertTrue(component.created_date) + + for field_name, value in data.items(): + if field_name in ["last_modified_date", "type"]: + continue + if field_name == "configuration_status": # default_on_addition + self.assertEqual(self.status1, getattr(component, field_name)) + continue + self.assertEqual(str(value), str(getattr(component, field_name)), msg=field_name) + + expected = 'Added Component: "RapidJSON 1.0.2"' + self.assertEqual(expected, mail.outbox[0].subject) + body = mail.outbox[0].body + self.assertIn(component.get_admin_url(), body) + + def test_api_component_endpoint_create_acceptable_linkages(self): + self.client.login(username="super_user", password="secret") + + data = { + "name": "comp1", + "acceptable_linkages": "Dynamic Linkage", + } + response = self.client.post(self.component_list_url, data) + self.assertEqual(status.HTTP_201_CREATED, response.status_code) + component = Component.objects.latest("id") + self.assertEqual(["Dynamic Linkage"], component.acceptable_linkages) + + data = { + "name": "comp2", + "acceptable_linkages": ["Dynamic Linkage"], + } + response = self.client.post(self.component_list_url, data) + self.assertEqual(status.HTTP_201_CREATED, response.status_code) + component = Component.objects.latest("id") + self.assertEqual(["Dynamic Linkage"], component.acceptable_linkages) + + data = { + "name": "comp3", + "acceptable_linkages": ["Dynamic Linkage", "Any Linkage Allowed"], + } + response = self.client.post(self.component_list_url, data) + self.assertEqual(status.HTTP_201_CREATED, response.status_code) + component = Component.objects.latest("id") + self.assertEqual(["Dynamic Linkage", "Any Linkage Allowed"], component.acceptable_linkages) + + data = { + "name": "comp4", + "acceptable_linkages": [], + } + response = self.client.post(self.component_list_url, data) + self.assertEqual(status.HTTP_201_CREATED, response.status_code) + component = Component.objects.latest("id") + self.assertIsNone(component.acceptable_linkages) + + def test_api_component_endpoint_create_dependencies(self): + self.client.login(username="super_user", password="secret") + + data = { + "name": "comp1", + "dependencies": "simple string", + } + response = self.client.post(self.component_list_url, data) + expected_error = b'{"dependencies":["Value must be valid JSON."]}' + self.assertEqual(status.HTTP_400_BAD_REQUEST, response.status_code) + self.assertEqual(expected_error, response.content) + + data = { + "name": "comp2", + "dependencies": "{'json': 'yes'}", + } + response = self.client.post(self.component_list_url, data) + self.assertEqual(status.HTTP_400_BAD_REQUEST, response.status_code) + self.assertEqual(expected_error, response.content) + + data = { + "name": "comp3", + "dependencies": json.dumps([{"json": "yes"}]), + } + response = self.client.post(self.component_list_url, data) + self.assertEqual(status.HTTP_201_CREATED, response.status_code) + component = Component.objects.latest("id") + self.assertEqual([{"json": "yes"}], component.dependencies) + + data = json.dumps( + { + "name": "comp4", + "dependencies": {"json": "yes"}, + } + ) + response = self.client.post( + self.component_list_url, data=data, content_type="application/json" + ) + self.assertEqual(status.HTTP_201_CREATED, response.status_code) + component = Component.objects.latest("id") + self.assertEqual({"json": "yes"}, component.dependencies) + + def test_api_component_endpoint_create_clean_validate_against_reference_data(self): + self.client.login(username=self.alternate_super_user, password="secret") + data = {"name": self.component1.name} + response = self.client.post(self.component_list_url, data) + self.assertEqual(status.HTTP_400_BAD_REQUEST, response.status_code) + copy_to_my_dataspace_url = self.component1.get_api_copy_to_my_dataspace_url() + expected = { + "error": [ + ErrorDetail( + string=f"The application object that you are creating already " + f"exists as c1 in the reference dataspace. Use the " + f"following URL to copy the reference object to your local " + f"Dataspace: {copy_to_my_dataspace_url}", + code="invalid", + ) + ], + "copy_url": [ErrorDetail(string=copy_to_my_dataspace_url, code="invalid")], + } + self.assertEqual(expected, response.data) + + def test_api_component_endpoint_copy_to_my_dataspace_action(self): + self.client.login(username=self.alternate_super_user, password="secret") + copy_to_my_dataspace_url = self.component1.get_api_copy_to_my_dataspace_url() + self.assertTrue(self.component1.dataspace.is_reference) + + response = self.client.post(copy_to_my_dataspace_url) + self.assertEqual("Alternate", response.data["dataspace"]) + self.assertEqual("c1", response.data["display_name"]) + + response = self.client.post(copy_to_my_dataspace_url) + self.assertEqual(status.HTTP_400_BAD_REQUEST, response.status_code) + expected = {"error": "The object already exists in your local Dataspace."} + self.assertEqual(expected, response.data) + + not_found_url = reverse("api_v2:component-copy-to-my-dataspace", args=[uuid.uuid4()]) + response = self.client.post(not_found_url) + self.assertEqual(status.HTTP_404_NOT_FOUND, response.status_code) + + self.client.login(username=self.super_user, password="secret") + response = self.client.post(copy_to_my_dataspace_url) + self.assertEqual(status.HTTP_400_BAD_REQUEST, response.status_code) + expected = {"error": "Target dataspace cannot be the reference one."} + self.assertEqual(expected, response.data) + + with override_settings(REFERENCE_DATASPACE="non_existing"): + response = self.client.post(copy_to_my_dataspace_url) + self.assertEqual(status.HTTP_400_BAD_REQUEST, response.status_code) + expected = {"error": "You do not have rights to execute this action."} + self.assertEqual(expected, response.data) + + def test_api_component_endpoint_update_put(self): + self.client.login(username="super_user", password="secret") + + put_data = json.dumps({"name": "Updated Name"}) + response = self.client.put( + self.component1_detail_url, data=put_data, content_type="application/json" + ) + self.assertEqual(status.HTTP_200_OK, response.status_code) + + history = History.objects.get_for_object( + self.component1, action_flag=History.CHANGE + ).latest("id") + self.assertEqual("Changed name.", history.get_change_message()) + + self.assertEqual('Updated Component: "Updated Name"', mail.outbox[0].subject) + body = mail.outbox[0].body + self.assertIn('Changes details for Component "Updated Name"', body) + self.assertIn(self.component1.get_admin_url(), body) + self.assertIn("Changed name.", body) + self.assertIn("Old value: c1", body) + self.assertIn("New value: Updated Name", body) + + def test_api_component_protected_fields_as_read_only(self): + component_policy = UsagePolicy.objects.create( + label="ComponentPolicy", + icon="icon", + content_type=ContentType.objects.get_for_model(Component), + dataspace=self.dataspace, + ) + + client = APIClient() + client.login(username="admin_user", password="secret") + + response = client.get(self.component_list_url, format="json") + c1 = response.data["results"][0] + self.assertIn("usage_policy", c1.keys()) + self.assertIsNone(c1["usage_policy"]) + + self.component1.usage_policy = component_policy + self.component1.save() + response = client.get(self.component1_detail_url, format="json") + self.assertEqual(component_policy.label, response.data["usage_policy"]) + + self.admin_user = add_perms(self.admin_user, ["add_component", "change_component"]) + data = { + "name": "comp1", + "usage_policy": component_policy.label, + } + response = client.post(self.component_list_url, data) + self.assertEqual(status.HTTP_201_CREATED, response.status_code) + component = Component.objects.latest("id") + self.assertIsNone(component.usage_policy) + + detail_url = reverse("api_v2:component-detail", args=[component.uuid]) + response = client.put(detail_url, data) + self.assertEqual(status.HTTP_200_OK, response.status_code) + component = Component.objects.latest("id") + self.assertIsNone(component.usage_policy) + + response = client.patch(detail_url, data) + self.assertEqual(status.HTTP_200_OK, response.status_code) + component = Component.objects.latest("id") + self.assertIsNone(component.usage_policy) + + self.admin_user = add_perm(self.admin_user, "change_usage_policy_on_component") + response = client.put(detail_url, data) + self.assertEqual(status.HTTP_200_OK, response.status_code) + component = Component.objects.latest("id") + self.assertEqual(component_policy, component.usage_policy) + + data["name"] = "comp2" + response = client.post(self.component_list_url, data) + self.assertEqual(status.HTTP_201_CREATED, response.status_code) + component = Component.objects.latest("id") + self.assertEqual(component_policy, component.usage_policy) + + +@override_settings( + EMAIL_HOST_USER="user", + EMAIL_HOST_PASSWORD="password", + EMAIL_HOST="localhost", + EMAIL_PORT=25, + DEFAULT_FROM_EMAIL="webmaster@localhost", + SITE_URL="server.dejacode.nexb", +) +class PackageAPITestCase(MaxQueryMixin, TestCase): + def setUp(self): + self.dataspace = Dataspace.objects.create(name="nexB") + self.other_dataspace = Dataspace.objects.create(name="Other") + + self.base_user = create_user("base_user", self.dataspace) + self.admin_user = create_admin("admin_user", self.dataspace) + self.super_user = create_superuser( + "super_user", self.dataspace, data_email_notification=True + ) + + self.package_list_url = reverse("api_v2:package-list") + + self.package1 = Package.objects.create( + filename="package1.zip", + dataspace=self.dataspace, + size=1024, + md5="6eed96478ad6ff01b97b7edf005b58ea", + sha1="96b63269c5c5762b005a59a4e8ae57f453df3865", + ) + self.package1_detail_url = reverse("api_v2:package-detail", args=[self.package1.uuid]) + + self.package2 = Package.objects.create( + filename="package2.zip", + dataspace=self.dataspace, + md5="e555c0900d1debb26ea463c747de169d", + sha1="d7b3679279b3d1bb1044a63232e1b309790a99ac", + ) + self.package2_detail_url = reverse("api_v2:package-detail", args=[self.package2.uuid]) + + self.package3 = Package.objects.create( + filename="package3.zip", + dataspace=self.dataspace, + md5="92d15ca3cf229be08c2cf5da1b4ab74b", + sha1="f379606577b3a6f05b19a0162b53e0e1228c4e16", + ) + self.package3_detail_url = reverse("api_v2:package-detail", args=[self.package3.uuid]) + + def test_api_package_list_endpoint_results(self): + self.client.login(username="super_user", password="secret") + # with self.assertNumQueries(9): + response = self.client.get(self.package_list_url) + + self.assertContains(response, '"count":3,') + self.assertContains(response, self.package1_detail_url) + self.assertContains(response, self.package2_detail_url) + self.assertEqual(3, response.data["count"]) + + def test_api_package_list_endpoint_search(self): + self.client.login(username="super_user", password="secret") + data = {"search": self.package1.filename} + response = self.client.get(self.package_list_url, data) + self.assertEqual(1, response.data["count"]) + self.assertContains(response, self.package1_detail_url) + self.assertNotContains(response, self.package2_detail_url) + + # Search is case in-sensitive + data = {"search": self.package1.filename.upper()} + response = self.client.get(self.package_list_url, data) + self.assertEqual(1, response.data["count"]) + self.assertContains(response, self.package1_detail_url) + self.assertNotContains(response, self.package2_detail_url) + + def test_api_package_list_endpoint_autocomplete_search(self): + self.client.login(username="super_user", password="secret") + + django = Package.objects.create( + type="pypi", + namespace="djangoproject", + name="django", + version="3.1", + dataspace=self.dataspace, + ) + django_detail_url = reverse("api_v2:package-detail", args=[django.uuid]) + + search_queries = [ + "pkg:pypi/djangoproject/django@3.1", + "pypi/djangoproject/django@3.1", + "pypi/django@3.1", + "django@3.1", + "django 3.1", + "pypi django 3.1", + ] + + for query in search_queries: + data = {"search": query, "autocomplete": 1} + response = self.client.get(self.package_list_url, data) + self.assertEqual(1, response.data["count"]) + self.assertContains(response, django_detail_url) + + def test_api_package_list_endpoint_filters(self): + self.client.login(username="super_user", password="secret") + data = { + "filename": self.package1.filename, + "size": self.package1.size, + } + response = self.client.get(self.package_list_url, data) + self.assertEqual(1, response.data["count"]) + self.assertContains(response, self.package1_detail_url) + self.assertNotContains(response, self.package2_detail_url) + + self.package1.keywords = ["Application", "Security"] + self.package1.save() + data = {"keywords": "application"} + response = self.client.get(self.package_list_url, data) + self.assertEqual(1, response.data["count"]) + self.assertContains(response, self.package1_detail_url) + self.assertNotContains(response, self.package2_detail_url) + + def test_api_package_list_endpoint_multiple_char_filters(self): + self.client.login(username="super_user", password="secret") + filters = "?md5={}&md5={}".format(self.package1.md5, self.package2.md5) + response = self.client.get(self.package_list_url + filters) + self.assertEqual(2, response.data["count"]) + self.assertContains(response, self.package1_detail_url) + self.assertContains(response, self.package2_detail_url) + self.assertNotContains(response, self.package3_detail_url) + + filters = "?sha1={}&sha1={}".format(self.package2.sha1, self.package3.sha1) + response = self.client.get(self.package_list_url + filters) + self.assertEqual(2, response.data["count"]) + self.assertNotContains(response, self.package1_detail_url) + self.assertContains(response, self.package2_detail_url) + self.assertContains(response, self.package3_detail_url) + + def test_api_package_detail_endpoint(self): + component1 = Component.objects.create(name="c1", dataspace=self.dataspace) + ComponentAssignedPackage.objects.create( + component=component1, package=self.package1, dataspace=self.dataspace + ) + component1_detail_url = reverse("api_v2:component-detail", args=[component1.uuid]) + + # Make sure the protected_fields does not break when a protected field is + # not available on the serializer, here `usage_policy` on `ComponentEmbeddedSerializer` + self.client.login(username=self.base_user, password="secret") + response = self.client.get(self.package_list_url) + self.assertEqual(200, response.status_code) + + self.client.login(username="super_user", password="secret") + with self.assertMaxQueries(10): + response = self.client.get(self.package1_detail_url) + + self.assertContains(response, self.package1_detail_url) + self.assertIn(self.package1_detail_url, response.data["api_url"]) + self.assertEqual(str(self.package1.uuid), response.data["uuid"]) + self.assertEqual(self.package1.filename, response.data["filename"]) + self.assertEqual(self.package1.size, response.data["size"]) + self.assertEqual( + list(self.package1.external_references.all()), response.data["external_references"] + ) + + component_data = response.data["components"][0] + self.assertEqual(str(component1), component_data["display_name"]) + self.assertEqual(component1.name, component_data["name"]) + self.assertEqual(component1.version, component_data["version"]) + self.assertEqual(str(component1.uuid), component_data["uuid"]) + self.assertIn(component1_detail_url, component_data["api_url"]) + + def test_api_package_endpoint_create_minimal(self): + self.client.login(username="super_user", password="secret") + data = {"filename": "package.tar.gz"} + response = self.client.post(self.package_list_url, data) + self.assertEqual(status.HTTP_201_CREATED, response.status_code) + + def test_api_package_endpoint_create_identifier_validation(self): + self.client.login(username="super_user", password="secret") + + data = {"download_url": "https://download.url"} + response = self.client.post(self.package_list_url, data) + self.assertEqual(status.HTTP_400_BAD_REQUEST, response.status_code) + expected = [ErrorDetail(string="['package_url or filename required']", code="invalid")] + self.assertEqual(expected, response.data) + + data = { + "type": "type", + "name": "name", + } + response = self.client.post(self.package_list_url, data) + self.assertEqual(status.HTTP_201_CREATED, response.status_code) + + def test_api_package_endpoint_create_filename_validation(self): + self.client.login(username="super_user", password="secret") + data = {"filename": "pack/age.tar.gz"} + response = self.client.post(self.package_list_url, data) + expected = { + "filename": [ + ErrorDetail( + string="Enter a valid filename: slash, backslash, or colon are not allowed.", + code="invalid", + ) + ] + } + self.assertEqual(status.HTTP_400_BAD_REQUEST, response.status_code) + self.assertEqual(expected, response.data) + + data = {"filename": "pack\\age.tar.gz"} + response = self.client.post(self.package_list_url, data) + self.assertEqual(status.HTTP_400_BAD_REQUEST, response.status_code) + self.assertEqual(expected, response.data) + + data = {"filename": "pack:age.tar.gz"} + response = self.client.post(self.package_list_url, data) + self.assertEqual(status.HTTP_400_BAD_REQUEST, response.status_code) + self.assertEqual(expected, response.data) + + def test_api_package_endpoint_create_with_license_expression(self): + self.client.login(username="super_user", password="secret") + data = { + "filename": "package.tar.gz", + "license_expression": "", + } + response = self.client.post(self.package_list_url, data) + self.assertEqual(status.HTTP_201_CREATED, response.status_code) + + data["license_expression"] = "non-existing-license" + response = self.client.post(self.package_list_url, data) + expected = {"license_expression": ["Unknown license key(s): non-existing-license"]} + self.assertEqual(status.HTTP_400_BAD_REQUEST, response.status_code) + self.assertEqual(expected, response.data) + + owner = Owner.objects.create(name="Owner1", dataspace=self.dataspace) + license1 = License.objects.create( + key="l1", name="L1", short_name="L1", owner=owner, dataspace=self.dataspace + ) + data = { + "filename": "package2.tar.gz", + "license_expression": license1.key, + } + response = self.client.post(self.package_list_url, data) + self.assertEqual(status.HTTP_201_CREATED, response.status_code) + + def test_api_package_endpoint_create(self): + self.client.login(username="super_user", password="secret") + + policy = UsagePolicy.objects.create( + label="PackagePolicy", + icon="icon", + content_type=ContentType.objects.get_for_model(Package), + dataspace=self.dataspace, + ) + + data = { + "download_url": "https://github.com/django/django/archive/1.9.6.tar.gz", + "filename": "django-1.9.6.tar.gz", + "sha1": "60f00300d0171ba4ac63f14121d2b6d60dfc5fd0", + "md5": "1cb693d3f3872b1056766e4a973b750d", + "size": 7443963, + "release_date": "1984-10-10", + "notes": "Notes", + "type": "deb", + "namespace": "debian", + "name": "curl", + "version": "7.50.3-1", + "qualifiers": "arch=i386", + "subpath": "googleapis/api/annotations", + "usage_policy": policy.label, + } + + response = self.client.post(self.package_list_url, data) + self.assertEqual(status.HTTP_201_CREATED, response.status_code) + + package = Package.objects.get(download_url=data["download_url"]) + self.assertFalse(History.objects.get_for_object(package).exists()) + self.assertEqual(self.super_user, package.created_by) + self.assertTrue(package.created_date) + + for field_name, value in data.items(): + self.assertEqual(str(value), str(getattr(package, field_name))) + + expected = 'Added Package: "deb/debian/curl@7.50.3-1"' + self.assertEqual(expected, mail.outbox[0].subject) + body = mail.outbox[0].body + self.assertIn(package.get_admin_url(), body) + + expected = "pkg:deb/debian/curl@7.50.3-1?arch=i386#googleapis/api/annotations" + self.assertEqual(expected, package.package_url) + + def test_api_package_endpoint_create_unicity_validation(self): + self.client.login(username="super_user", password="secret") + + self.package1.download_url = "http://url.com/a.zip" + self.package1.save() + + # Same download_url, different filename + data = {"filename": self.package1.filename} + response = self.client.post(self.package_list_url, data) + self.assertEqual(status.HTTP_201_CREATED, response.status_code) + + # Same download_url, same filename + data["download_url"] = self.package1.download_url + response = self.client.post(self.package_list_url, data) + expected1 = "duplicate key value violates unique constraint" + expected2 = ( + "Key (dataspace_id, type, namespace, name, version, qualifiers, subpath," + " download_url, filename)" + ) + self.assertContains(response, expected1, status_code=400) + self.assertContains(response, expected2, status_code=400) + + # Same download_url, same filename, name + data["name"] = "Name" + response = self.client.post(self.package_list_url, data) + self.assertEqual(status.HTTP_201_CREATED, response.status_code) + + # Same download_url, same filename, same name + response = self.client.post(self.package_list_url, data) + self.assertContains(response, expected1, status_code=400) + self.assertContains(response, expected2, status_code=400) + + def test_api_package_endpoint_create_update_keywords(self): + keyword1 = ComponentKeyword.objects.create(label="Keyword1", dataspace=self.dataspace) + keyword2 = ComponentKeyword.objects.create(label="Keyword2", dataspace=self.dataspace) + # For scoping sanity check + ComponentKeyword.objects.create(label=keyword1.label, dataspace=self.other_dataspace) + + self.client.login(username="super_user", password="secret") + data = { + "filename": "filename.ext", + "keywords": [keyword1.label, keyword2.label], + } + response = self.client.post(self.package_list_url, data) + self.assertEqual(status.HTTP_201_CREATED, response.status_code) + p1 = Package.objects.get(filename="filename.ext") + self.assertEqual([keyword1.label, keyword2.label], p1.keywords) + + data = { + "filename": "filename2.ext", + "keywords": ["non-existing"], + } + response = self.client.post(self.package_list_url, data) + self.assertEqual(201, response.status_code) + new_keyword = ComponentKeyword.objects.get(label="non-existing") + self.assertTrue(new_keyword) + p2 = Package.objects.get(filename="filename2.ext") + self.assertEqual([new_keyword.label], p2.keywords) + history = History.objects.get_for_object(new_keyword).latest("id") + self.assertEqual("Added.", history.get_change_message()) + + data = { + "filename": "filename3.ext", + "keywords": "", + } + response = self.client.post(self.package_list_url, data) + self.assertEqual(status.HTTP_201_CREATED, response.status_code) + self.assertEqual([], Package.objects.get(filename=data["filename"]).keywords) + + # Update + data = json.dumps({"keywords": [keyword1.label]}) + p2_api_url = reverse("api_v2:package-detail", args=[p2.uuid]) + response = self.client.patch(p2_api_url, data=data, content_type="application/json") + self.assertEqual(status.HTTP_200_OK, response.status_code) + p2.refresh_from_db() + self.assertEqual([keyword1.label], p2.keywords) + + data = json.dumps({"keywords": [keyword2.label]}) + p2_api_url = reverse("api_v2:package-detail", args=[p2.uuid]) + response = self.client.patch(p2_api_url, data=data, content_type="application/json") + self.assertEqual(status.HTTP_200_OK, response.status_code) + p2.refresh_from_db() + self.assertEqual([keyword2.label], p2.keywords) + + data = json.dumps({"keywords": [keyword1.label, keyword2.label]}) + p2_api_url = reverse("api_v2:package-detail", args=[p2.uuid]) + response = self.client.patch(p2_api_url, data=data, content_type="application/json") + self.assertEqual(status.HTTP_200_OK, response.status_code) + p2.refresh_from_db() + self.assertEqual([keyword1.label, keyword2.label], p2.keywords) + + def test_api_package_endpoint_update_put(self): + self.package1.created_by = self.base_user + self.package1.save() + + self.client.login(username="super_user", password="secret") + + put_data = json.dumps({"filename": "Updated Name"}) + response = self.client.put( + self.package1_detail_url, data=put_data, content_type="application/json" + ) + self.assertEqual(status.HTTP_200_OK, response.status_code) + + history = History.objects.get_for_object(self.package1, action_flag=History.CHANGE).latest( + "id" + ) + self.assertEqual("Changed filename.", history.get_change_message()) + self.assertIn("package1.zip", history.serialized_data) + + self.assertEqual('Updated Package: "Updated Name"', mail.outbox[0].subject) + body = mail.outbox[0].body + self.assertIn('Changes details for Package "Updated Name"', body) + self.assertIn(self.package1.get_admin_url(), body) + self.assertIn("Changed filename.", body) + self.assertIn("Old value: package1.zip", body) + self.assertIn("New value: Updated Name", body) + + self.package1.refresh_from_db() + self.assertEqual(self.base_user, self.package1.created_by) + self.assertEqual(self.super_user, self.package1.last_modified_by) + + def test_api_package_license_choices_fields(self): + self.client.login(username="super_user", password="secret") + + owner = Owner.objects.create(name="Owner1", dataspace=self.dataspace) + license1 = License.objects.create( + key="license1", name="License1", short_name="L1", owner=owner, dataspace=self.dataspace + ) + license2 = License.objects.create( + key="license2", name="License2", short_name="L2", owner=owner, dataspace=self.dataspace + ) + + response = self.client.get(self.package1_detail_url) + self.assertEqual([], response.data["license_choices"]) + self.assertIsNone(response.data["license_choices_expression"]) + + self.package1.license_expression = license1.key + self.package1.save() + response = self.client.get(self.package1_detail_url) + self.assertEqual([], response.data["license_choices"]) + self.assertIsNone(response.data["license_choices_expression"]) + + LicenseChoice.objects.create( + from_expression=license1.key, to_expression=license2.key, dataspace=self.dataspace + ) + response = self.client.get(self.package1_detail_url) + expected = [{"key": "license2", "short_name": "L2"}] + self.assertEqual(expected, response.data["license_choices"]) + self.assertEqual(license2.key, response.data["license_choices_expression"]) + + def test_api_package_viewset_add_action(self): + add_url = reverse("api_v2:package-add") + + response = self.client.get(add_url) + self.assertEqual(403, response.status_code) + response = self.client.post(add_url) + self.assertEqual(403, response.status_code) + + self.client.login(username=self.base_user.username, password="secret") + response = self.client.get(add_url) + self.assertEqual(405, response.status_code) + response = self.client.post(add_url) + # DjangoModelPermissions: POST requires add_{model_name} + self.assertEqual(403, response.status_code) + + add_perm(self.base_user, "add_package") + response = self.client.get(add_url) + self.assertEqual(405, response.status_code) + + data = {} + response = self.client.post(add_url, data) + expected = {"download_url": "This field is required."} + self.assertEqual(status.HTTP_400_BAD_REQUEST, response.status_code) + self.assertEqual(expected, response.json()) + + data = {"download_url": "http://bad_url"} + response = self.client.post(add_url, data) + self.assertEqual(200, response.status_code) + expected = {"failed": ["http://bad_url"]} + self.assertEqual(expected, response.json()) + + data = {"download_url": ["http://bad_url", "http://bad_url2"]} + response = self.client.post(add_url, data) + self.assertEqual(200, response.status_code) + expected = {"failed": ["http://bad_url", "http://bad_url2"]} + self.assertEqual(expected, response.json()) + + with mock.patch("component_catalog.api.collect_create_scan") as collect: + collect.return_value = True + data = {"download_url": "http://url.com/package.zip"} + response = self.client.post(add_url, data) + self.assertEqual(200, response.status_code) + expected = {"added": ["http://url.com/package.zip"]} + self.assertEqual(expected, response.json()) + + def test_api_package_viewset_about_action(self): + about_url = reverse("api_v2:package-about", args=[self.package1.uuid]) + + response = self.client.get(about_url) + self.assertEqual(403, response.status_code) + response = self.client.post(about_url) + self.assertEqual(403, response.status_code) + + self.client.login(username=self.base_user.username, password="secret") + response = self.client.get(about_url) + self.assertEqual(200, response.status_code) + expected = { + "about_data": ( + "about_resource: package1.zip\n" + "checksum_md5: 6eed96478ad6ff01b97b7edf005b58ea\n" + "checksum_sha1: 96b63269c5c5762b005a59a4e8ae57f453df3865\n" + ) + } + self.assertEqual(expected, response.json()) + + def test_api_package_viewset_about_files_action(self): + about_url = reverse("api_v2:package-about-files", args=[self.package1.uuid]) + + response = self.client.get(about_url) + self.assertEqual(403, response.status_code) + response = self.client.post(about_url) + self.assertEqual(403, response.status_code) + + self.client.login(username=self.base_user.username, password="secret") + response = self.client.get(about_url) + self.assertEqual(200, response.status_code) + self.assertEqual("application/zip", response["content-type"]) + self.assertEqual( + 'attachment; filename="package1.zip_about.zip"', response["content-disposition"] + ) + + def test_api_package_protected_fields_as_read_only(self): + policy = UsagePolicy.objects.create( + label="PackagePolicy", + icon="icon", + content_type=ContentType.objects.get_for_model(Package), + dataspace=self.dataspace, + ) + + client = APIClient() + client.login(username=self.admin_user.username, password="secret") + + response = client.get(self.package_list_url, format="json") + p1 = response.data["results"][0] + self.assertIn("usage_policy", p1.keys()) + self.assertIsNone(p1["usage_policy"]) + + self.package1.usage_policy = policy + self.package1.save() + response = client.get(self.package1_detail_url, format="json") + self.assertEqual(policy.label, response.data["usage_policy"]) + + self.admin_user = add_perms(self.admin_user, ["add_package", "change_package"]) + data = { + "filename": "pack1", + "usage_policy": policy.label, + } + response = client.post(self.package_list_url, data) + self.assertEqual(status.HTTP_201_CREATED, response.status_code) + package = Package.objects.latest("id") + self.assertIsNone(package.usage_policy) + + detail_url = reverse("api_v2:package-detail", args=[package.uuid]) + response = client.put(detail_url, data) + self.assertEqual(status.HTTP_200_OK, response.status_code) + package = Package.objects.latest("id") + self.assertIsNone(package.usage_policy) + + response = client.patch(detail_url, data) + self.assertEqual(status.HTTP_200_OK, response.status_code) + package = Package.objects.latest("id") + self.assertIsNone(package.usage_policy) + + self.admin_user = add_perm(self.admin_user, "change_usage_policy_on_package") + response = client.put(detail_url, data) + self.assertEqual(status.HTTP_200_OK, response.status_code) + package = Package.objects.latest("id") + self.assertEqual(policy, package.usage_policy) + + data["filename"] = "pack2" + response = client.post(self.package_list_url, data) + self.assertEqual(status.HTTP_201_CREATED, response.status_code) + package = Package.objects.latest("id") + self.assertEqual(policy, package.usage_policy) + + +class PackageAPIFilterSetTest(TestCase): + def setUp(self): + self.dataspace = Dataspace.objects.create(name="nexB") + + def create_package(**kwargs): + return Package.objects.create(dataspace=self.dataspace, **kwargs) + + self.package1 = create_package( + filename="apache-log4j.zip", + type="maven", + namespace="org.apache.commons", + name="io", + version="1.3.4", + download_url="http://example1.com", + ) + self.package2 = create_package( + filename="jna-4.0.zip", + type="maven", + namespace="org.apache.commons", + name="io", + version="2.3.4", + download_url="http://example2.com", + ) + self.package3 = create_package( + filename="jna-src-3.3.0.tar.xz", + type="github", + ) + self.package4 = create_package( + filename="logjnative-4.3.2.tar.gz", + namespace="namespace", + ) + + def test_package_api_filterset_fuzzy_filter(self): + data = {"fuzzy": "jna.jar"} + package_filterset = PackageAPIFilterSet(data) + expected = [ + "jna-4.0.zip", + "jna-src-3.3.0.tar.xz", + ] + self.assertEqual(expected, [package.filename for package in package_filterset.qs]) + + def test_package_api_filterset_fuzzy_filter_create_search_names_no_version(self): + filename = "jna.jar" + expected = ( + filename, + "jna", + ) + self.assertEqual(expected, FuzzyPackageNameSearch.create_search_names(filename)) + + def test_package_api_filterset_fuzzy_filter_create_search_names_version(self): + create_search_names = FuzzyPackageNameSearch.create_search_names + + filename_version = "jna-1.6.3.jar" + expected = ( + filename_version, + "jna 1.6.3", + "jna", + ) + self.assertEqual(expected, create_search_names(filename_version)) + + filename_version_alphanumeric = "jna-1.6.3-RELEASE.jar" + expected = ( + filename_version_alphanumeric, + "jna 1.6.3-RELEASE", + "jna", + ) + self.assertEqual(expected, create_search_names(filename_version_alphanumeric)) + + def test_package_api_filterset_purl_filter_invalid_purl(self): + data = {"purl": "jna.jar"} + expected = 0 + self.assertEqual(expected, PackageAPIFilterSet(data).qs.count()) + + def test_package_api_filterset_purl_filter_no_value(self): + data = {"purl": ""} + expected = Package.objects.scope(self.dataspace).count() + self.assertEqual(expected, PackageAPIFilterSet(data).qs.count()) + + def test_package_api_filterset_purl_filter_non_existant_purl(self): + data = {"purl": "pkg:PYPI/Django_package@1.11.1.dev1"} + expected = 0 + self.assertEqual(expected, PackageAPIFilterSet(data).qs.count()) + + def test_package_api_filterset_purl_filter_no_version(self): + data = {"purl": "pkg:maven/org.apache.commons/io"} + expected = 2 + self.assertEqual(expected, PackageAPIFilterSet(data).qs.count()) + + def test_package_api_filterset_purl_filter_single_match(self): + data = {"purl": self.package1.package_url} + expected = 1 + filterset_qs = PackageAPIFilterSet(data).qs + self.assertEqual(expected, filterset_qs.count()) + self.assertEqual(filterset_qs.get(), self.package1) + + def test_package_api_filterset_purl_empty(self): + data = {"purl": "EMPTY"} + qs = PackageAPIFilterSet(data).qs + self.assertEqual(2, PackageAPIFilterSet(data).qs.count()) + self.assertTrue(self.package1.package_url) + self.assertNotIn(self.package1, qs) + self.assertTrue(self.package2.package_url) + self.assertNotIn(self.package2, qs) + self.assertFalse(self.package3.package_url) + self.assertIn(self.package3, qs) + self.assertFalse(self.package4.package_url) + self.assertIn(self.package4, qs) + + def test_package_api_filterset_type_namespace_filters(self): + data = {"type": self.package3.type.upper()} + filterset_qs = PackageAPIFilterSet(data).qs + self.assertEqual(1, filterset_qs.count()) + self.assertEqual(filterset_qs.get(), self.package3) + + data = {"namespace": self.package4.namespace.upper()} + filterset_qs = PackageAPIFilterSet(data).qs + self.assertEqual(1, filterset_qs.count()) + self.assertEqual(filterset_qs.get(), self.package4) + + +class SubcomponentAPITestCase(TestCase): + def setUp(self): + self.dataspace = Dataspace.objects.create(name="nexB") + + self.base_user = create_user("base_user", self.dataspace) + self.super_user = create_superuser( + "super_user", self.dataspace, data_email_notification=True + ) + + self.component1 = Component.objects.create( + name="c1", dataspace=self.dataspace, legal_reviewed=True + ) + self.component1_detail_url = reverse("api_v2:component-detail", args=[self.component1.uuid]) + + self.component2 = Component.objects.create(name="c2", dataspace=self.dataspace) + self.component2_detail_url = reverse("api_v2:component-detail", args=[self.component2.uuid]) + + self.subcomponent_list_url = reverse("api_v2:subcomponent-list") + self.subcomponent1 = Subcomponent.objects.create( + parent=self.component1, child=self.component2, dataspace=self.dataspace + ) + self.subcomponent1_detail_url = reverse( + "api_v2:subcomponent-detail", args=[self.subcomponent1.uuid] + ) + + def test_api_subcomponent_list_endpoint_results(self): + self.client.login(username="super_user", password="secret") + response = self.client.get(self.subcomponent_list_url) + + self.assertContains(response, '"count":1,') + self.assertContains(response, self.subcomponent1_detail_url) + self.assertEqual(1, response.data["count"]) + + def test_api_subcomponent_detail_endpoint(self): + self.client.login(username="super_user", password="secret") + response = self.client.get(self.subcomponent1_detail_url) + self.assertContains(response, self.subcomponent1_detail_url) + self.assertIn(self.subcomponent1_detail_url, response.data["api_url"]) + self.assertEqual(str(self.subcomponent1.uuid), response.data["uuid"]) + self.assertIn(str(self.subcomponent1.parent.uuid), response.data["parent"]) + self.assertIn(str(self.subcomponent1.child.uuid), response.data["child"]) + self.assertEqual(self.subcomponent1.is_deployed, response.data["is_deployed"]) + self.assertEqual(self.subcomponent1.is_modified, response.data["is_modified"]) + + def test_api_subcomponent_endpoint_create_minimal(self): + self.client.login(username="super_user", password="secret") + self.subcomponent1.delete() + data = { + "parent": self.component1_detail_url, + "child": f"{self.component2.name}:{self.component2.version}", + } + response = self.client.post(self.subcomponent_list_url, data) + self.assertEqual(status.HTTP_201_CREATED, response.status_code) + + subcomponent = Subcomponent.objects.latest("created_date") + self.assertTrue(subcomponent.is_deployed) + self.assertTrue(response.data["is_deployed"]) + self.assertFalse(subcomponent.is_modified) + self.assertFalse(response.data["is_modified"]) + + def test_api_subcomponent_endpoint_create_required_fields(self): + self.client.login(username="super_user", password="secret") + data = {} + response = self.client.post(self.subcomponent_list_url, data) + expected = { + "parent": [ErrorDetail(string="This field is required.", code="required")], + "child": [ErrorDetail(string="This field is required.", code="required")], + } + self.assertEqual(status.HTTP_400_BAD_REQUEST, response.status_code) + self.assertEqual(expected, response.data) + + def test_api_subcomponent_endpoint_create_with_license_expression(self): + self.client.login(username="super_user", password="secret") + self.subcomponent1.delete() + data = { + "parent": self.component1_detail_url, + "child": self.component2_detail_url, + "license_expression": "non-existing-license", + } + response = self.client.post(self.subcomponent_list_url, data) + expected = {"license_expression": ["Unknown license key(s): non-existing-license"]} + self.assertEqual(status.HTTP_400_BAD_REQUEST, response.status_code) + self.assertEqual(expected, response.data) + + owner = Owner.objects.create(name="Owner1", dataspace=self.dataspace) + license1 = License.objects.create( + key="l1", name="L1", short_name="L1", owner=owner, dataspace=self.dataspace + ) + data["license_expression"] = license1.key + response = self.client.post(self.subcomponent_list_url, data) + self.assertEqual(status.HTTP_201_CREATED, response.status_code) + + def test_api_subcomponent_endpoint_create_unique_parent_child_validation(self): + self.client.login(username="super_user", password="secret") + data = { + "parent": self.component1_detail_url, + "child": self.component2_detail_url, + } + response = self.client.post(self.subcomponent_list_url, data) + self.assertEqual(status.HTTP_400_BAD_REQUEST, response.status_code) + expected = { + "non_field_errors": [ + ErrorDetail( + string="The fields parent, child must make a unique set.", code="unique" + ) + ] + } + self.assertEqual(expected, response.data) diff --git a/component_catalog/tests/test_command.py b/component_catalog/tests/test_command.py new file mode 100644 index 00000000..f2c9d94d --- /dev/null +++ b/component_catalog/tests/test_command.py @@ -0,0 +1,127 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# DejaCode is a trademark of nexB Inc. +# SPDX-License-Identifier: AGPL-3.0-only +# See https://github.com/nexB/dejacode for support or download. +# See https://aboutcode.org for more information about AboutCode FOSS projects. +# + +from io import StringIO + +from django.core import management +from django.core.management.base import CommandError +from django.test import TestCase + +from component_catalog.models import Component +from component_catalog.models import Package +from dje.models import Dataspace +from dje.models import History +from dje.tests import create_superuser + + +class ComponentCatalogManagementCommandsTestCase(TestCase): + def setUp(self): + self.dataspace = Dataspace.objects.create(name="nexB") + self.super_user = create_superuser("super_user", self.dataspace) + + self.component1 = Component.objects.create( + name="component", version="1.0", dataspace=self.dataspace + ) + + def test_component_update_completion_levels_management_command(self): + self.assertFalse(self.component1.completion_level) + output = StringIO() + + with self.assertRaises(CommandError) as error: + management.call_command("updatecompletionlevels", stdout=output) + self.assertEqual( + "Error: the following arguments are required: dataspace", str(error.exception) + ) + + management.call_command( + "updatecompletionlevels", self.dataspace.name, stdout=output, no_color=True + ) + self.component1.refresh_from_db() + self.assertTrue(self.component1.completion_level) + self.assertEqual("1 Component(s) updated.\n", output.getvalue()) + + def test_package_setpurls_management_command(self): + output = StringIO() + package = Package.objects.create( + filename="package.zip", + download_url="http://repo1.maven.org/maven2/jdbm/jdbm/0.20-dev/", + dataspace=self.dataspace, + ) + + self.assertTrue(package.last_modified_date) + initial_modified_date = package.last_modified_date + + with self.assertRaises(CommandError) as error: + management.call_command("setpurls", stdout=output) + self.assertEqual( + "Error: the following arguments are required: dataspace, username", + str(error.exception), + ) + + options = [ + self.dataspace.name, + self.super_user.username, + ] + management.call_command("setpurls", *options, stdout=output, no_color=True) + package.refresh_from_db() + + expected_output = [ + "1 Package(s) updated with a Package URL in the nexB Dataspace.", + "Pre-update: 1 Packages, 0 (0%) with a Package URL, 1 without.", + "Post-update: 1 Packages, 1 (100%) with a Package URL, 0 without.", + "Number of errors encountered when updating Packages: 0", + ] + self.assertIn("\n".join(expected_output), output.getvalue().strip()) + self.assertEqual("pkg:maven/jdbm/jdbm@0.20-dev", package.package_url) + self.assertEqual(initial_modified_date, package.last_modified_date) + + output = StringIO() + Package.objects.filter(pk=package.pk).update( + download_url="http://repo1.maven.org/maven2/abc/abc/1.0/" + ) + initial_modified_date = package.last_modified_date + + options = [ + self.dataspace.name, + self.super_user.username, + "--history", + "--overwrite", + "--save", + ] + management.call_command("setpurls", *options, stdout=output, no_color=True) + package.refresh_from_db() + expected_output[1] = "Pre-update: 1 Packages, 1 (100%) with a Package URL, 0 without." + self.assertEqual("\n".join(expected_output), output.getvalue().strip()) + self.assertEqual("pkg:maven/abc/abc@1.0", package.package_url) + self.assertNotEqual(initial_modified_date, package.last_modified_date) + + history_entry = History.objects.get_for_object(package).get() + expected_messages = "Set Package URL from Download URL" + self.assertEqual(expected_messages, history_entry.change_message) + self.assertEqual(self.super_user, package.last_modified_by) + + def test_collectcpes_management_command(self): + self.assertFalse(self.component1.completion_level) + output = StringIO() + + with self.assertRaises(CommandError) as error: + management.call_command("collectcpes", stdout=output) + + expected = ( + "Error: the following arguments are required: dataspace, " "cpe_dictionary_location" + ) + self.assertEqual(expected, str(error.exception)) + + def test_componentfrompackage_management_command(self): + output = StringIO() + + with self.assertRaises(CommandError) as error: + management.call_command("componentfrompackage", stdout=output) + + expected = "Error: the following arguments are required: dataspace, username" + self.assertEqual(expected, str(error.exception)) diff --git a/component_catalog/tests/test_filters.py b/component_catalog/tests/test_filters.py new file mode 100644 index 00000000..f4d4bf41 --- /dev/null +++ b/component_catalog/tests/test_filters.py @@ -0,0 +1,360 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# DejaCode is a trademark of nexB Inc. +# SPDX-License-Identifier: AGPL-3.0-only +# See https://github.com/nexB/dejacode for support or download. +# See https://aboutcode.org for more information about AboutCode FOSS projects. +# + +from os.path import dirname +from os.path import join + +from django.contrib.contenttypes.models import ContentType +from django.test import TestCase + +from component_catalog.filters import ComponentFilterSet +from component_catalog.filters import PackageFilterSet +from component_catalog.models import Component +from component_catalog.models import ComponentKeyword +from component_catalog.models import ComponentType +from component_catalog.models import Package +from dje.models import Dataspace +from dje.tests import create_superuser +from dje.tests import create_user +from license_library.models import License +from organization.models import Owner +from policy.models import UsagePolicy + + +class ComponentFilterSetTest(TestCase): + def setUp(self): + self.dataspace = Dataspace.objects.create(name="nexB") + self.other_dataspace = Dataspace.objects.create(name="Other") + self.nexb_user = create_superuser("nexb_user", self.dataspace) + self.basic_user = create_user("basic_user", self.dataspace) + + self.owner = Owner.objects.create(name="Owner", dataspace=self.dataspace) + self.other_owner = Owner.objects.create(name="OtherOwner", dataspace=self.other_dataspace) + + self.license_ct = ContentType.objects.get_for_model(License) + self.component_ct = ContentType.objects.get_for_model(Component) + + self.license_policy = UsagePolicy.objects.create( + label="LicensePolicy", + icon="icon", + content_type=self.license_ct, + dataspace=self.dataspace, + ) + self.component_policy = UsagePolicy.objects.create( + label="ComponentPolicy", + icon="icon", + content_type=self.component_ct, + dataspace=self.dataspace, + ) + self.other_component_policy = UsagePolicy.objects.create( + label="OtherComponentPolicy", + icon="icon", + content_type=self.component_ct, + dataspace=self.other_dataspace, + ) + + self.component_type = ComponentType.objects.create(label="type1", dataspace=self.dataspace) + self.other_type = ComponentType.objects.create( + label="other_type", dataspace=self.other_dataspace + ) + + self.license1 = License.objects.create( + key="license1", + name="License1", + short_name="L1", + dataspace=self.dataspace, + owner=self.owner, + ) + self.license2 = License.objects.create( + key="license2", + name="License2", + short_name="L2", + dataspace=self.dataspace, + owner=self.owner, + ) + self.other_license = License.objects.create( + key="license1", + name="License1", + short_name="L1", + dataspace=self.other_dataspace, + owner=self.other_owner, + ) + + self.keyword1 = ComponentKeyword.objects.create(label="Keyword", dataspace=self.dataspace) + self.other_keyword = ComponentKeyword.objects.create( + label="OtherKeyword", dataspace=self.other_dataspace + ) + + def test_component_filterset_no_dataspace(self): + with self.assertRaises(AttributeError): + ComponentFilterSet() + self.assertTrue(ComponentFilterSet(dataspace=self.dataspace)) + + def test_component_filterset_dataspace_scoping(self): + # Removing the `related_only` as we need to test Dataspace scoping only + class ComponentFilterSetScoping(ComponentFilterSet): + related_only = [] + + component_filterset = ComponentFilterSetScoping(dataspace=self.dataspace) + + qs = component_filterset.filters["usage_policy"].queryset + self.assertTrue(self.component_policy in qs) + self.assertFalse(self.other_component_policy in qs) + self.assertFalse(self.license_policy in qs) + + qs = component_filterset.filters["type"].queryset + self.assertTrue(self.component_type in qs) + self.assertFalse(self.other_type in qs) + + qs = component_filterset.filters["licenses"].queryset + self.assertTrue(self.license1 in qs) + self.assertFalse(self.other_license in qs) + + qs = component_filterset.filters["keywords"].queryset + self.assertTrue(self.keyword1 in qs) + self.assertFalse(self.other_keyword in qs) + + def test_component_filterset_primary_language_filter(self): + filterset = ComponentFilterSet(dataspace=self.dataspace) + self.assertEqual([], list(filterset.filters["primary_language"].field.choices)) + + c1 = Component.objects.create( + name="c1", dataspace=self.dataspace, primary_language="Python" + ) + c2 = Component.objects.create(name="c2", dataspace=self.dataspace, primary_language="Java") + + filterset = ComponentFilterSet(dataspace=self.dataspace) + self.assertEqual([c1, c2], list(filterset.qs)) + self.assertEqual( + [("Java", "Java"), ("Python", "Python")], + list(filterset.filters["primary_language"].field.choices), + ) + + filterset = ComponentFilterSet(dataspace=self.dataspace, data={"q": c1.name}) + self.assertEqual([c1], list(filterset.qs)) + self.assertEqual( + [("Python", "Python")], list(filterset.filters["primary_language"].field.choices) + ) + + def test_component_filterset_related_only_values_filter(self): + self.assertEqual( + ["licenses", "primary_language", "usage_policy"], ComponentFilterSet.related_only + ) + + c1 = Component.objects.create( + name="c1", + dataspace=self.dataspace, + license_expression=self.license1.key, + primary_language="Python", + ) + c2 = Component.objects.create(name="c2", dataspace=self.dataspace, primary_language="Java") + + filterset = ComponentFilterSet(dataspace=self.dataspace) + self.assertEqual([c1, c2], list(filterset.qs)) + self.assertEqual([self.license1], list(filterset.filters["licenses"].queryset)) + self.assertEqual( + [("Java", "Java"), ("Python", "Python")], + list(filterset.filters["primary_language"].field.choices), + ) + + filterset = ComponentFilterSet(dataspace=self.dataspace, data={"q": c1.name}) + self.assertEqual([c1], list(filterset.qs)) + self.assertEqual([self.license1], list(filterset.filters["licenses"].queryset)) + self.assertEqual( + [("Python", "Python")], list(filterset.filters["primary_language"].field.choices) + ) + + filterset = ComponentFilterSet(dataspace=self.dataspace, data={"q": c2.name}) + self.assertEqual([c2], list(filterset.qs)) + self.assertEqual([], list(filterset.filters["licenses"].queryset)) + self.assertEqual( + [("Java", "Java")], list(filterset.filters["primary_language"].field.choices) + ) + + c2.license_expression = self.license2.key + c2.save() + filterset = ComponentFilterSet(dataspace=self.dataspace) + self.assertEqual([c1, c2], list(filterset.qs)) + self.assertEqual( + [self.license1, self.license2], list(filterset.filters["licenses"].queryset) + ) + self.assertEqual( + [("Java", "Java"), ("Python", "Python")], + list(filterset.filters["primary_language"].field.choices), + ) + + # The current filter value does not apply to itself + filterset = ComponentFilterSet( + dataspace=self.dataspace, data={"licenses": [self.license1.key]} + ) + self.assertEqual([c1], list(filterset.qs)) + self.assertEqual( + [self.license1, self.license2], list(filterset.filters["licenses"].queryset) + ) + self.assertEqual( + [("Python", "Python")], list(filterset.filters["primary_language"].field.choices) + ) + + +class ComponentFilterSearchTestCase(TestCase): + testfiles_location = join(dirname(__file__), "testfiles") + + # Extract Component data as json for a given list of PKs: + # dejacode dumpdata component_catalog.component --indent 2 --natural-foreign + # --natural-primary --pks + fixtures = [join(testfiles_location, "search", "component_dataset.json")] + + def test_component_filterset_search_filter(self): + nexb_dataspace = Dataspace.objects.get(name="nexB") + data = {"q": ""} + component_filterset = ComponentFilterSet(dataspace=nexb_dataspace, data=data) + expected = [ + "jblogbackup 1.0", + "jblogbackup 1.1", + "logback 0.9.9", + "logback 1.0.0", + "logback 1.0.1", + "logback classic 1.0.0", + "logback eclipse 1.0.0", + "nagios-logback-appender 1.0", + "zzz", + ] + self.assertEqual(expected, [str(component) for component in component_filterset.qs]) + + data = {"q": "logback"} + component_filterset = ComponentFilterSet(dataspace=nexb_dataspace, data=data) + expected = [ + "logback 0.9.9", + "logback 1.0.0", + "logback 1.0.1", + "logback classic 1.0.0", + "logback eclipse 1.0.0", + "nagios-logback-appender 1.0", + "jblogbackup 1.0", + "jblogbackup 1.1", + ] + self.assertEqual(expected, [str(component) for component in component_filterset.qs]) + + def test_component_filterset_sort_keeps_default_ordering_from_model(self): + nexb_dataspace = Dataspace.objects.get(name="nexB") + data = {} + component_filterset = ComponentFilterSet(dataspace=nexb_dataspace, data=data) + self.assertEqual((), component_filterset.qs.query.order_by) + + data = {"sort": ""} + component_filterset = ComponentFilterSet(dataspace=nexb_dataspace, data=data) + self.assertEqual((), component_filterset.qs.query.order_by) + + data = {"sort": "invalid"} + component_filterset = ComponentFilterSet(dataspace=nexb_dataspace, data=data) + self.assertEqual((), component_filterset.qs.query.order_by) + + data = {"sort": "name"} + component_filterset = ComponentFilterSet(dataspace=nexb_dataspace, data=data) + self.assertEqual(("name", "version"), component_filterset.qs.query.order_by) + + data = {"sort": "version"} + component_filterset = ComponentFilterSet(dataspace=nexb_dataspace, data=data) + self.assertEqual(("version", "name"), component_filterset.qs.query.order_by) + + data = {"sort": "primary_language"} + component_filterset = ComponentFilterSet(dataspace=nexb_dataspace, data=data) + self.assertEqual( + ("primary_language", "name", "version"), component_filterset.qs.query.order_by + ) + + +class PackageFilterSearchTestCase(TestCase): + def setUp(self): + self.nexb_dataspace = Dataspace.objects.create(name="nexB") + + def create_package(**kwargs): + return Package.objects.create(**kwargs, dataspace=self.nexb_dataspace) + + filename = { + "filename": "setup.exe", + } + create_package(**filename) + + simple_purl = { + "type": "deb", + "name": "curl", + } + create_package(**simple_purl) + + simple_purl2 = { + **simple_purl, + "type": "git", + } + create_package(**simple_purl2) + + complete_purl = { + "type": "deb", + "namespace": "debian", + "name": "curl", + "version": "7.50.3-1", + "qualifiers": "arch=i386", + "subpath": "googleapis/api/annotations", + } + create_package(**complete_purl) + + def test_package_filterset_search_filter(self): + def sorted_results(qs): + return sorted([str(package) for package in qs]) + + data = {"q": ""} + filterset = PackageFilterSet(dataspace=self.nexb_dataspace, data=data) + expected = [ + "deb/debian/curl@7.50.3-1", + "git/curl", + "deb/curl", + "setup.exe", + ] + self.assertEqual(sorted(expected), sorted_results(filterset.qs)) + + data = {"q": "deb/curl"} + filterset = PackageFilterSet(dataspace=self.nexb_dataspace, data=data) + expected = [ + "deb/curl", + "deb/debian/curl@7.50.3-1", + ] + self.assertEqual(sorted(expected), sorted_results(filterset.qs)) + data = {"q": "pkg:deb/curl"} + filterset = PackageFilterSet(dataspace=self.nexb_dataspace, data=data) + self.assertEqual(sorted(expected), sorted_results(filterset.qs)) + + data = {"q": "deb/debian/curl@7.50.3-1"} + filterset = PackageFilterSet(dataspace=self.nexb_dataspace, data=data) + expected = [ + "deb/debian/curl@7.50.3-1", + ] + self.assertEqual(sorted(expected), sorted_results(filterset.qs)) + data = {"q": "pkg:deb/debian/curl@7.50.3-1"} + filterset = PackageFilterSet(dataspace=self.nexb_dataspace, data=data) + self.assertEqual(sorted(expected), sorted_results(filterset.qs)) + + data = {"q": "git/curl"} + filterset = PackageFilterSet(dataspace=self.nexb_dataspace, data=data) + expected = [ + "git/curl", + ] + self.assertEqual(sorted(expected), sorted_results(filterset.qs)) + data = {"q": "pkg:git/curl"} + filterset = PackageFilterSet(dataspace=self.nexb_dataspace, data=data) + self.assertEqual(sorted(expected), sorted_results(filterset.qs)) + + data = {"q": "setup.exe"} + filterset = PackageFilterSet(dataspace=self.nexb_dataspace, data=data) + expected = [ + "setup.exe", + ] + self.assertEqual(sorted(expected), sorted_results(filterset.qs)) + + data = {"q": "pkg:setup.exe"} + filterset = PackageFilterSet(dataspace=self.nexb_dataspace, data=data) + self.assertEqual([], sorted_results(filterset.qs)) diff --git a/component_catalog/tests/test_importers.py b/component_catalog/tests/test_importers.py new file mode 100644 index 00000000..e62e5ebf --- /dev/null +++ b/component_catalog/tests/test_importers.py @@ -0,0 +1,1668 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# DejaCode is a trademark of nexB Inc. +# SPDX-License-Identifier: AGPL-3.0-only +# See https://github.com/nexB/dejacode for support or download. +# See https://aboutcode.org for more information about AboutCode FOSS projects. +# + +import datetime +import os +from os.path import dirname + +from django.contrib.contenttypes.models import ContentType +from django.core.exceptions import NON_FIELD_ERRORS +from django.forms import widgets +from django.test import TestCase +from django.urls import reverse + +from guardian.shortcuts import assign_perm + +from component_catalog.importers import BaseImporter +from component_catalog.importers import ComponentImporter +from component_catalog.importers import PackageImporter +from component_catalog.importers import SubcomponentImporter +from component_catalog.models import AcceptableLinkage +from component_catalog.models import Component +from component_catalog.models import ComponentKeyword +from component_catalog.models import ComponentStatus +from component_catalog.models import ComponentType +from component_catalog.models import Subcomponent +from dje.models import Dataspace +from dje.models import History +from dje.tests import MaxQueryMixin +from dje.tests import add_perm +from dje.tests import create_admin +from dje.tests import create_superuser +from license_library.models import License +from organization.models import Owner +from policy.models import UsagePolicy +from product_portfolio.models import Product + +TESTFILES_LOCATION = os.path.join(dirname(__file__), "testfiles", "import") + + +class ComponentImporterTestCase(MaxQueryMixin, TestCase): + def setUp(self): + self.dataspace = Dataspace.objects.create(name="nexB") + self.alternate_dataspace = Dataspace.objects.create(name="Alternate") + + self.super_user = create_superuser("super_user", self.dataspace) + self.admin_user = create_admin("admin_user", self.dataspace) + self.alternate_super_user = create_superuser("alternate_user", self.alternate_dataspace) + + self.owner = Owner.objects.create( + name="Apache Software Foundation", dataspace=self.dataspace + ) + self.component_no_version = Component.objects.create( + name="log4j", owner=self.owner, dataspace=self.dataspace + ) + self.component_with_version = Component.objects.create( + name="log4j", version="1.0", owner=self.owner, dataspace=self.dataspace + ) + + self.configuration_status = ComponentStatus.objects.create( + label="Approved", + text="Approved. Ready for Component Catalog.", + dataspace=self.dataspace, + ) + + self.license = License.objects.create( + key="license", + short_name="License1", + name='BSD 2-clause "FreeBSD" License', + dataspace=self.dataspace, + owner=self.owner, + ) + + self.license_ct = ContentType.objects.get_for_model(License) + self.component_ct = ContentType.objects.get_for_model(Component) + + self.license_policy = UsagePolicy.objects.create( + label="LicensePolicy", + icon="icon", + content_type=self.license_ct, + dataspace=self.dataspace, + ) + + self.component_policy = UsagePolicy.objects.create( + label="ComponentPolicy", + icon="icon", + content_type=self.component_ct, + dataspace=self.dataspace, + ) + + def test_exclude_ManyToManyFields_from_model_form(self): + for field_name in [x.name for x in Component._meta.local_many_to_many]: + self.assertTrue(field_name in ComponentImporter.model_form._meta.exclude) + + def test_component_import_non_model_model(self): + # Raise if the importer.model_form is None or not a ModelForm + class Importer(BaseImporter): + model_form = None + + with self.assertRaises(AttributeError): + Importer(self.super_user) + + def test_component_import_empty_csv(self): + # Input file is empty + file = os.path.join(TESTFILES_LOCATION, "empty.csv") + importer = ComponentImporter(self.super_user, file) + self.assertEqual(["The input file is empty."], importer.fatal_errors) + self.assertFalse(getattr(importer, "formset", None)) + + def test_component_import_with_non_utf8_values(self): + # non utf8 char in the header, the value is proper converted + file = os.path.join(TESTFILES_LOCATION, "non_utf8_in_header.csv") + importer = ComponentImporter(self.super_user, file) + self.assertFalse(importer.fatal_errors) + self.assertEqual(set(["\ufffd"]), importer.ignored_columns) + self.assertEqual(["owner", "name", "version"], importer.headers) + self.assertTrue(importer.formset.is_valid()) + + # non utf8 char in a mandatory row + file = os.path.join(TESTFILES_LOCATION, "non_utf8_decode_in_row.csv") + importer = ComponentImporter(self.super_user, file) + self.assertFalse(importer.fatal_errors) + # The value is properly decoded but an error is raised on the name field + # as the value does not comply with the field validation: + # "periods, letters, numbers, or !#"\':,&()+_-.'" + self.assertTrue("name" in importer.formset.errors[0].keys()) + self.assertTrue(importer.formset.errors) + + # Similar issue on the copyright field + file = os.path.join(TESTFILES_LOCATION, "non_utf8_encode_in_row.csv") + importer = ComponentImporter(self.super_user, file) + self.assertFalse(importer.fatal_errors) + # The copyright was properly decoded, the formset is valid. + self.assertTrue(importer.formset.is_valid()) + importer.save_all() + expected = "\ufffd\ufffd_Copyright 2009, Mahmood Ali" + self.assertEqual(expected, importer.results["added"][0].copyright) + + def test_component_import_unicode_encoding_issue(self): + file = os.path.join(TESTFILES_LOCATION, "unicode_encoding_issue.csv") + importer = ComponentImporter(self.super_user, file) + # Make sure the errors are returned and no results + self.assertTrue(importer.fatal_errors) + self.assertFalse(getattr(importer, "formset", None)) + self.assertEqual( + ["Encoding issue. The string that could not be encoded/decoded was: hlsch\ufffdtter "], + importer.fatal_errors, + ) + + def test_component_import_cp1252_encoded_file_works(self): + file = os.path.join(TESTFILES_LOCATION, "K-Components-INVT-UTF-bug.csv") + importer = ComponentImporter(self.super_user, file) + self.assertEqual([], importer.fatal_errors) + + def test_component_import_byte_order_mark(self): + file = os.path.join(TESTFILES_LOCATION, "component_import_byte_order_mark.csv") + importer = ComponentImporter(self.super_user, file) + self.assertEqual([], importer.fatal_errors) + + def test_component_import_mandatory_columns(self): + # The file is missing the mandatory columns + file = os.path.join(TESTFILES_LOCATION, "missing_mandatory_columns.csv") + importer = ComponentImporter(self.super_user, file) + # Make sure the errors are returned and no results + self.assertTrue(importer.fatal_errors) + self.assertFalse(getattr(importer, "formset", None)) + + # Now using a file containing the mandatory columns + file = os.path.join(TESTFILES_LOCATION, "including_mandatory_columns.csv") + importer = ComponentImporter(self.super_user, file) + # Make sure no errors are returned and we got some results + self.assertFalse(importer.fatal_errors) + self.assertTrue(importer.formset) + self.assertTrue(importer.formset.is_valid()) + + def test_component_import_include_mandatory_columns_but_no_data(self): + # The columns are correct, but no data row + file = os.path.join(TESTFILES_LOCATION, "including_mandatory_columns_but_no_data.csv") + importer = ComponentImporter(self.super_user, file) + # Make sure the errors are returned and no results + self.assertEqual(["No data row in input file."], importer.fatal_errors) + self.assertFalse(getattr(importer, "formset", None)) + + def test_component_import_mandatory_data(self): + # The first line is missing the mandatory data + file = os.path.join(TESTFILES_LOCATION, "missing_mandatory_data.csv") + importer = ComponentImporter(self.super_user, file) + expected = [{"name": ["This field is required."]}, {}] + self.assertEqual(expected, importer.formset.errors) + + def test_component_import_empty_rows(self): + file = os.path.join(TESTFILES_LOCATION, "including_empty_rows.csv") + importer = ComponentImporter(self.super_user, file) + + # Make sure that there's no error .. + self.assertFalse(importer.fatal_errors) + # ...and that the empty rows are ignored + self.assertEqual(2, len(importer.formset)) + + def test_perform_form_validation(self): + file = os.path.join(TESTFILES_LOCATION, "including_model_invalid_data.csv") + importer = ComponentImporter(self.super_user, file) + self.assertEqual( + ["Ensure this value has at most 100 characters (it has 509)."], + importer.formset[0].__getitem__("name").errors, + ) + + def test_component_import_quoted_values(self): + file = os.path.join(TESTFILES_LOCATION, "quoted_values.csv") + importer = ComponentImporter(self.super_user, file) + self.assertFalse(importer.fatal_errors) + self.assertTrue(importer.formset.is_valid()) + + def test_component_import_windows_line_endings(self): + file = os.path.join(TESTFILES_LOCATION, "windows_line_endings.csv") + importer = ComponentImporter(self.super_user, file) + # Some error at the formset level but none at the importer level + self.assertFalse(importer.fatal_errors) + self.assertTrue(importer.formset.errors) + + def test_component_import_line_breaks_support(self): + file = os.path.join(TESTFILES_LOCATION, "copyright_with_linebreaks.csv") + + url = reverse("admin:component_catalog_component_import") + self.client.login(username="super_user", password="secret") + with open(file) as f: + response = self.client.post(url, data={"file": f}) + # Line breaks are display in the HTML import preview as
      + expected = 'Copyright 1

      Copyright 2' + self.assertContains(response, expected, html=True) + + importer = ComponentImporter(self.super_user, file) + importer.save_all() + added_component = importer.results["added"][0] + # Line break are kept and saved in the DB + self.assertEqual("Copyright 1\n\nCopyright 2", added_component.copyright) + + def test_component_import_escaped_quote_characters(self): + file = os.path.join(TESTFILES_LOCATION, "escaped_quote_characters.csv") + importer = ComponentImporter(self.super_user, file) + value = importer.formset[0].__getitem__("description").value() + self.assertEqual('Some "quoted chars" description', value) + + def test_component_import_version_column(self): + # Valid CSV: + # * without 'version' column + # * with 'version' column but empty data + # * with 'version' column and correct version data + input = [ + ("valid_with_empty_version.csv", self.component_no_version), + ("valid_without_version.csv", self.component_no_version), + ("valid_with_version.csv", self.component_with_version), + ] + + for file_name, component in input: + file = os.path.join(TESTFILES_LOCATION, file_name) + importer = ComponentImporter(self.super_user, file) + self.assertFalse(importer.fatal_errors) + # If id is a value, that means the Component has been matched + self.assertEqual(component.id, importer.formset[0].instance.id) + + def test_component_import_duplicated_column_header(self): + # There's 2 columns "name" in this file + file = os.path.join(TESTFILES_LOCATION, "duplicated_column_header.csv") + importer = ComponentImporter(self.super_user, file) + self.assertTrue('Column "name" is listed more than once.' in importer.fatal_errors) + + def test_component_import_extra_cell_in_row_line_nb2(self): + # There's a extra cell in line2 + file = os.path.join(TESTFILES_LOCATION, "extra_cell_in_row_line2.csv") + importer = ComponentImporter(self.super_user, file) + self.assertTrue("Row at line 2 is not valid." in importer.fatal_errors) + + def test_component_import_save_all(self): + # Manually creating a formset with 1 new Component and 1 existing + formset_data = { + "form-TOTAL_FORMS": "2", + "form-INITIAL_FORMS": "0", + "form-0-name": "Django", + "form-0-version": "1.4", + "form-0-owner": str(self.component_with_version.owner.id), + "form-0-curation_level": 0, + "form-1-name": self.component_with_version.name, + "form-1-version": str(self.component_with_version.version), + "form-1-owner": str(self.component_with_version.owner.id), + "form-1-curation_level": 0, + } + importer = ComponentImporter(self.super_user, formset_data=formset_data) + importer.save_all() + self.assertEqual([self.component_with_version], importer.results["unmodified"]) + added_component = importer.results["added"][0] + self.assertTrue(added_component) + + self.assertFalse(History.objects.get_for_object(added_component).exists()) + self.assertEqual(self.super_user, added_component.created_by) + self.assertTrue(added_component.created_date) + + self.assertEqual(self.super_user, added_component.created_by) + self.assertEqual(self.super_user, added_component.last_modified_by) + + def test_component_import_clean_version_warnings(self): + # There's a extra cell in line2 + file = os.path.join(TESTFILES_LOCATION, "cleaned_versions_warnings.csv") + importer = ComponentImporter(self.super_user, file) + self.assertTrue(importer.formset.is_valid()) + # No version cleaning applied on first row + self.assertFalse(importer.formset.forms[0].warnings) + self.assertEqual( + {"version": ["Version will been cleaned to 1.1"]}, importer.formset.forms[1].warnings + ) + self.assertEqual( + {"version": ["Version will been cleaned to 1.6"]}, importer.formset.forms[2].warnings + ) + + def test_component_import_multiple_duplicate_rows(self): + file = os.path.join(TESTFILES_LOCATION, "multiple_duplicate_rows.csv") + importer = ComponentImporter(self.super_user, file) + self.assertFalse(importer.formset.is_valid()) + self.assertTrue("One of the row is a duplicate." in importer.formset.non_form_errors()) + # List of errors, skipping the empty dicts + errors = [error for error in importer.formset.errors if error] + # We should have 3 error as we have 3 duplicates + self.assertEqual(3, len(errors)) + expected = {"version": ["This row is a duplicate."], "name": ["This row is a duplicate."]} + self.assertEqual(expected, errors[0]) + + def test_component_import_url_stripping_extra_spaces(self): + file = os.path.join(TESTFILES_LOCATION, "urls_stripped.csv") + importer = ComponentImporter(self.super_user, file) + self.assertTrue(importer.formset.is_valid()) + importer.save_all() + added_component = importer.results["added"][0] + self.assertFalse(" " in added_component.homepage_url) + self.assertFalse(" " in added_component.code_view_url) + self.assertFalse(" " in added_component.bug_tracking_url) + self.assertFalse(" " in added_component.notice_url) + # vcs_url is not an URLField but CharField + self.assertFalse(" " in added_component.vcs_url) + + def test_component_import_url_without_scheme_validation(self): + # In forms.URLField, if no URL scheme given, Django assumes and prepend http:// + # before saving the value in the DB. + file = os.path.join(TESTFILES_LOCATION, "url_without_scheme_validation.csv") + importer = ComponentImporter(self.super_user, file) + self.assertTrue(importer.formset.is_valid()) + importer.save_all() + added_component = importer.results["added"][0] + self.assertEqual("http://www.a.com", added_component.homepage_url) + + def test_component_import_strip_input_values(self): + file = os.path.join(TESTFILES_LOCATION, "valid_with_non_stripped_spaces.csv") + importer = ComponentImporter(self.super_user, file) + + self.assertFalse(importer.fatal_errors) + # If id is a value, that means the Component has been matched + self.assertEqual(self.component_with_version.id, importer.formset[0].instance.id) + + def test_component_import_non_existing_organization(self): + file = os.path.join(TESTFILES_LOCATION, "non_existing_organization.csv") + importer = ComponentImporter(self.super_user, file) + self.assertFalse(importer.formset.is_valid()) + expected = [{"owner": ["This Owner does not exists. No suggestion."]}] + self.assertEqual(expected, importer.formset.errors) + + def test_component_import_organization_suggestions(self): + Owner.objects.create(name="Apache 1", dataspace=self.dataspace) + Owner.objects.create(name="Apache 2", alias="Alias1", dataspace=self.dataspace) + Owner.objects.create(name="Apache 3", dataspace=self.dataspace) + + formset_data = { + "form-TOTAL_FORMS": "1", + "form-INITIAL_FORMS": "0", + "form-0-name": "c1", + "form-0-curation_level": 0, + "form-0-owner": "apache", + } + importer = ComponentImporter(self.super_user, formset_data=formset_data) + self.assertFalse(importer.formset.is_valid()) + expected = [ + { + "owner": [ + "This Owner does not exists. Suggestion(s):" + " Apache 1, Apache 2, Apache 3, Apache Software Foundation." + ] + } + ] + self.assertEqual(expected, importer.formset.errors) + + formset_data["form-0-owner"] = "alias1" + importer = ComponentImporter(self.super_user, formset_data=formset_data) + self.assertFalse(importer.formset.is_valid()) + expected = [{"owner": ["This Owner does not exists. Suggestion(s): Apache 2."]}] + self.assertEqual(expected, importer.formset.errors) + + def test_component_import_validate_case_insensitive_uniqueness(self): + formset_data = { + "form-TOTAL_FORMS": "1", + "form-INITIAL_FORMS": "0", + "form-0-name": self.component_no_version.name.upper(), + "form-0-curation_level": 0, + } + importer = ComponentImporter(self.super_user, formset_data=formset_data) + self.assertFalse(importer.formset.is_valid()) + expected = [ + { + "name": [ + 'The application object that you are creating already exists as "log4j". ' + "Note that a different case in the object name is not sufficient to " + "make it unique." + ] + } + ] + self.assertEqual(expected, importer.formset.errors) + + formset_data["form-0-name"] = "other name" + importer = ComponentImporter(self.super_user, formset_data=formset_data) + self.assertTrue(importer.formset.is_valid()) + + file = os.path.join(TESTFILES_LOCATION, "component_names_suggestion.csv") + importer = ComponentImporter(self.super_user, file) + self.assertFalse(importer.formset.is_valid()) + self.assertEqual(expected, importer.formset.errors) + + def test_component_import_valid_configuration_status(self): + file = os.path.join(TESTFILES_LOCATION, "valid_configuration_status.csv") + importer = ComponentImporter(self.super_user, file) + self.assertTrue(importer.formset.is_valid()) + status_field = importer.formset.forms[0].fields["configuration_status"] + expected = self.configuration_status.get_admin_link(target="_blank") + self.assertEqual(expected, status_field.value_for_display) + + def test_component_import_invalid_configuration_status(self): + file = os.path.join(TESTFILES_LOCATION, "invalid_configuration_status.csv") + importer = ComponentImporter(self.super_user, file) + self.assertFalse(importer.formset.is_valid()) + expected = [ + { + "configuration_status": [ + 'That choice is not one of the available choices: "Approved"' + ] + } + ] + self.assertEqual(expected, importer.formset.errors) + + def test_component_import_curation_level_default_value(self): + file = os.path.join(TESTFILES_LOCATION, "curation_level_default_value.csv") + importer = ComponentImporter(self.super_user, file) + self.assertTrue(importer.formset.is_valid()) + self.assertEqual(40, importer.formset.forms[0].instance.curation_level) + self.assertEqual(0, importer.formset.forms[1].instance.curation_level) + + def test_component_import_fields_help_supported_values(self): + url = reverse("admin:component_catalog_component_import") + self.client.login(username="super_user", password="secret") + + # Let's add 2 ComponentType and 2 ComponentStatus and make sure they are + # displayed as supported values in the help + ComponentType.objects.create(label="TypeA", dataspace=self.dataspace) + ComponentType.objects.create(label="TypeB", dataspace=self.dataspace) + ComponentStatus.objects.create(label="S1", dataspace=self.dataspace) + ComponentStatus.objects.create(label="S2", dataspace=self.dataspace) + + response = self.client.get(url) + self.assertContains(response, "
    • TypeA
    • ", html=True) + self.assertContains(response, "
    • TypeB
    • ", html=True) + self.assertContains(response, "
    • S1
    • ", html=True) + self.assertContains(response, "
    • S2
    • ", html=True) + + def test_component_import_usage_policy_help_supported_values(self): + url = reverse("admin:component_catalog_component_import") + self.client.login(username="super_user", password="secret") + + response = self.client.get(url) + base_html = "{}" + self.assertContains(response, base_html.format("usage_policy")) + self.assertContains(response, base_html.format(self.component_policy)) + self.assertNotContains(response, base_html.format(self.license_policy)) + + self.client.login(username="admin_user", password="secret") + add_perm(self.admin_user, "add_component") + response = self.client.get(url) + self.assertContains(response, base_html.format("name")) + self.assertNotContains(response, base_html.format("usage_policy")) + + def test_component_import_valid_usage_policy(self): + file = os.path.join(TESTFILES_LOCATION, "valid_usage_policy.csv") + # Using the same label to make sure the Policy is scoped by ContentType + self.license_policy.label = self.component_policy.label + self.license_policy.save() + importer = ComponentImporter(self.super_user, file) + self.assertTrue(importer.formset.is_valid()) + policy_field = importer.formset.forms[0].fields["usage_policy"] + expected = self.component_policy.get_admin_link(target="_blank") + self.assertEqual(expected, policy_field.value_for_display) + importer.save_all() + self.assertEqual(1, len(importer.results["added"])) + + def test_component_import_invalid_usage_policy(self): + file = os.path.join(TESTFILES_LOCATION, "invalid_usage_policy.csv") + importer = ComponentImporter(self.super_user, file) + self.assertFalse(importer.formset.is_valid()) + expected = [ + {"usage_policy": ['That choice is not one of the available choices: "ComponentPolicy"']} + ] + self.assertEqual(expected, importer.formset.errors) + + def test_component_import_nullboolean_field_values(self): + file = os.path.join(TESTFILES_LOCATION, "nullboolean_field_values.csv") + importer = ComponentImporter(self.super_user, file) + self.assertTrue(importer.formset.is_valid()) + importer.save_all() + added_component0 = importer.results["added"][0] + added_component1 = importer.results["added"][1] + added_component2 = importer.results["added"][2] + + self.assertEqual(False, importer.formset.forms[0].instance.is_license_notice) + self.assertEqual(None, importer.formset.forms[0].instance.is_copyright_notice) + self.assertEqual(True, importer.formset.forms[0].instance.is_notice_in_codebase) + self.assertEqual(True, importer.formset.forms[0].instance.is_active) + self.assertEqual(False, added_component0.is_license_notice) + self.assertEqual(None, added_component0.is_copyright_notice) + self.assertEqual(True, added_component0.is_notice_in_codebase) + self.assertEqual(True, added_component0.is_active) + + self.assertEqual(False, importer.formset.forms[1].instance.is_license_notice) + self.assertEqual(None, importer.formset.forms[1].instance.is_copyright_notice) + self.assertEqual(True, importer.formset.forms[1].instance.is_notice_in_codebase) + self.assertEqual(True, importer.formset.forms[1].instance.is_active) + self.assertEqual(False, added_component1.is_license_notice) + self.assertEqual(None, added_component1.is_copyright_notice) + self.assertEqual(True, added_component1.is_notice_in_codebase) + self.assertEqual(True, added_component1.is_active) + + self.assertEqual(True, importer.formset.forms[2].instance.is_license_notice) + self.assertEqual(False, importer.formset.forms[2].instance.is_copyright_notice) + self.assertEqual(None, importer.formset.forms[2].instance.is_notice_in_codebase) + self.assertEqual(False, importer.formset.forms[2].instance.is_active) + self.assertEqual(True, added_component2.is_license_notice) + self.assertEqual(False, added_component2.is_copyright_notice) + self.assertEqual(None, added_component2.is_notice_in_codebase) + self.assertEqual(False, added_component2.is_active) + + def test_component_import_get_added_instance_ids(self): + file = os.path.join(TESTFILES_LOCATION, "valid_multiple_rows.csv") + importer = ComponentImporter(self.super_user, file) + self.assertIsNone(importer.get_added_instance_ids()) + importer.save_all() + self.assertEqual(2, len(importer.get_added_instance_ids())) + + def test_component_import_get_admin_changelist_url(self): + file = os.path.join(TESTFILES_LOCATION, "valid_with_version.csv") + importer = ComponentImporter(self.super_user, file) + self.assertEqual( + reverse("admin:component_catalog_component_changelist"), + importer.get_admin_changelist_url(), + ) + + def test_component_import_download_template_from_view(self): + url = reverse("admin:component_catalog_component_import") + self.client.login(username="super_user", password="secret") + response = self.client.get(url) + expected = ( + '' + "Download import template" + ) + self.assertContains(response, expected, html=True) + + response = self.client.get(url + "?get_template=1") + self.assertEqual( + 'attachment; filename="component_import_template.csv"', response["Content-Disposition"] + ) + self.assertEqual("application/csv", response["Content-Type"]) + + importer = ComponentImporter(self.super_user) + expected = ",".join(importer.required_fields + importer.supported_fields) + self.assertContains(response, expected) + + protected_field = "usage_policy" + self.assertIn(protected_field, str(response.content)) + + self.client.login(username="admin_user", password="secret") + add_perm(self.admin_user, "add_component") + response = self.client.get(url + "?get_template=1") + self.assertIn("name", str(response.content)) + self.assertNotIn(protected_field, str(response.content)) + + def test_importers_view_num_queries_view(self): + # Regrouping all importers here for simplicity + self.client.login(username="super_user", password="secret") + + # WARNING: 3 tasks are inserted for some reason, using this + # to avoid the impact of those insertions in following assertions + self.client.get("/") + + with self.assertMaxQueries(6): + self.client.get(reverse("admin:component_catalog_subcomponent_import")) + + with self.assertMaxQueries(9): + self.client.get(reverse("admin:component_catalog_package_import")) + + with self.assertNumQueries(4): + self.client.get(reverse("admin:organization_owner_import")) + + with self.assertMaxQueries(10): + self.client.get(reverse("admin:component_catalog_component_import")) + + def test_component_import_keywords(self): + formset_data = { + "form-TOTAL_FORMS": "1", + "form-INITIAL_FORMS": "0", + "form-0-name": "Name", + "form-0-curation_level": 10, + } + + formset_data["form-0-keywords"] = None + importer = ComponentImporter(self.super_user, formset_data=formset_data) + self.assertTrue(importer.formset.is_valid()) + + formset_data["form-0-keywords"] = "" + importer = ComponentImporter(self.super_user, formset_data=formset_data) + self.assertTrue(importer.formset.is_valid()) + + formset_data["form-0-keywords"] = [] + importer = ComponentImporter(self.super_user, formset_data=formset_data) + self.assertTrue(importer.formset.is_valid()) + + formset_data["form-0-keywords"] = {} + importer = ComponentImporter(self.super_user, formset_data=formset_data) + self.assertTrue(importer.formset.is_valid()) + + formset_data["form-0-keywords"] = () + importer = ComponentImporter(self.super_user, formset_data=formset_data) + self.assertTrue(importer.formset.is_valid()) + + formset_data["form-0-keywords"] = {"key": "value"} + importer = ComponentImporter(self.super_user, formset_data=formset_data) + self.assertFalse(importer.formset.is_valid()) + expected = [{"keywords": ["Enter a list of values."]}] + self.assertEqual(expected, importer.formset.errors) + + formset_data["form-0-keywords"] = "keyword1" + importer = ComponentImporter(self.super_user, formset_data=formset_data) + self.assertFalse(importer.formset.is_valid()) + expected = [ + {"keywords": ["Select a valid choice. keyword1 is not one of the available choices."]} + ] + self.assertEqual(expected, importer.formset.errors) + + formset_data["form-0-keywords"] = "keyword1,keyword2" + importer = ComponentImporter(self.super_user, formset_data=formset_data) + self.assertFalse(importer.formset.is_valid()) + expected = [ + {"keywords": ["Select a valid choice. keyword1 is not one of the available choices."]} + ] + self.assertEqual(expected, importer.formset.errors) + + k1 = ComponentKeyword.objects.create(label="Keyword1", dataspace=self.dataspace) + k2 = ComponentKeyword.objects.create(label="Keyword2", dataspace=self.dataspace) + + formset_data["form-0-keywords"] = f"{k1.label}" + importer = ComponentImporter(self.super_user, formset_data=formset_data) + self.assertTrue(importer.formset.is_valid()) + + formset_data["form-0-keywords"] = f" , {k1.label}, ," + importer = ComponentImporter(self.super_user, formset_data=formset_data) + self.assertTrue(importer.formset.is_valid()) + + formset_data["form-0-keywords"] = f"{k1.label}, {k2.label}" + importer = ComponentImporter(self.super_user, formset_data=formset_data) + self.assertTrue(importer.formset.is_valid()) + + importer.save_all() + self.assertEqual(1, len(importer.results["added"])) + added_component = importer.results["added"][0] + self.assertEqual(["Keyword1", "Keyword2"], added_component.keywords) + + # Making sure the TextInput widget is used in place of the default Select + # as loading multiple initial values is problematic on the Select + keywords_field = importer.formset.forms[0].fields["keywords"] + self.assertIsInstance(keywords_field.widget, widgets.TextInput) + + def test_component_import_license_expression(self): + formset_data = { + "form-TOTAL_FORMS": "1", + "form-INITIAL_FORMS": "0", + "form-0-name": "Django", + "form-0-version": "2.0", + "form-0-curation_level": 0, + "form-0-license_expression": "invalid", + } + importer = ComponentImporter(self.super_user, formset_data=formset_data) + self.assertFalse(importer.formset.is_valid()) + expected = [{"license_expression": ["Unknown license key(s): invalid"]}] + self.assertEqual(expected, importer.formset.errors) + + formset_data["form-0-license_expression"] = self.license.key + importer = ComponentImporter(self.super_user, formset_data=formset_data) + self.assertTrue(importer.formset.is_valid()) + importer.save_all() + self.assertEqual(1, len(importer.results["added"])) + added_component = importer.results["added"][0] + self.assertEqual(1, added_component.licenses.count()) + self.assertEqual(self.license, added_component.licenses.all()[0]) + + def test_component_import_license_expression_short_name(self): + self.license.short_name = "Short Name with Space" + self.license.save() + + license2 = License.objects.create( + key="apache-1.1", + short_name="Apache 1.1", + name="Apache License 1.1", + dataspace=self.dataspace, + owner=self.owner, + ) + + mixed_expression = "{} AND {}".format(self.license.short_name, license2.key) + formset_data = { + "form-TOTAL_FORMS": "1", + "form-INITIAL_FORMS": "0", + "form-0-name": "Django", + "form-0-version": "2.0", + "form-0-curation_level": 0, + "form-0-license_expression": mixed_expression, + } + + importer = ComponentImporter(self.super_user, formset_data=formset_data) + self.assertTrue(importer.formset.is_valid()) + importer.save_all() + self.assertEqual(1, len(importer.results["added"])) + added_component = importer.results["added"][0] + self.assertEqual(2, added_component.licenses.count()) + self.assertIn(self.license, added_component.licenses.all()) + self.assertIn(license2, added_component.licenses.all()) + self.assertEqual("license AND apache-1.1", added_component.license_expression) + + def test_component_import_leading_space_in_formset_data(self): + Component.objects.create(name="cURL", dataspace=self.dataspace) + + formset_data = { + "form-TOTAL_FORMS": "2", + "form-INITIAL_FORMS": "0", + "form-0-name": "ok", + "form-0-curation_level": 0, + # Leading space + "form-1-name": " cURL", + "form-1-curation_level": 0, + } + + importer = ComponentImporter(self.super_user, formset_data=formset_data) + importer.save_all() + self.assertEqual(1, len(importer.results["added"])) + self.assertEqual(1, len(importer.results["unmodified"])) + + def test_component_import_protected_fields_ignored(self): + file = os.path.join(TESTFILES_LOCATION, "valid_usage_policy.csv") + perm = "component_catalog.change_usage_policy_on_component" + self.assertFalse(self.admin_user.has_perm(perm)) + importer = ComponentImporter(self.admin_user, file_location=file) + self.assertFalse(importer.fatal_errors) + self.assertTrue(importer.formset.is_valid()) + self.assertEqual({"usage_policy"}, importer.ignored_columns) + importer.save_all() + self.assertEqual(1, len(importer.results["added"])) + self.assertIsNone(importer.results["added"][0].usage_policy) + importer.results["added"][0].delete() + + self.admin_user = add_perm(self.admin_user, "change_usage_policy_on_component") + self.assertTrue(self.admin_user.has_perm(perm)) + importer = ComponentImporter(self.admin_user, file_location=file) + self.assertFalse(importer.fatal_errors) + self.assertTrue(importer.formset.is_valid()) + importer.save_all() + self.assertEqual(1, len(importer.results["added"])) + self.assertEqual(self.component_policy, importer.results["added"][0].usage_policy) + + def test_component_import_clean_validate_against_reference_data(self): + formset_data = { + "form-TOTAL_FORMS": "1", + "form-INITIAL_FORMS": "0", + "form-0-name": self.component_no_version.name, + "form-0-curation_level": 0, + } + + importer = ComponentImporter(self.alternate_super_user, formset_data=formset_data) + self.assertFalse(importer.formset.is_valid()) + + absolute_link = self.component_no_version.get_absolute_link(target="_blank") + copy_link = self.component_no_version.get_html_link( + self.component_no_version.get_copy_url(), value="Copy to my Dataspace", target="_blank" + ) + error = ( + "The application object that you are creating already exists as " + "{} in the reference dataspace. {}".format(absolute_link, copy_link) + ) + + expected = [ + { + "version": [error], + "name": [error], + } + ] + self.assertEqual(expected, importer.formset.errors) + + def test_component_import_clean_primary_language(self): + formset_data = { + "form-TOTAL_FORMS": "1", + "form-INITIAL_FORMS": "0", + "form-0-name": "Name", + "form-0-curation_level": 0, + "form-0-primary_language": "Python", + } + + importer = ComponentImporter(self.alternate_super_user, formset_data=formset_data) + self.assertTrue(importer.formset.is_valid()) + self.assertEqual({}, importer.formset.forms[0].warnings) + + formset_data["form-0-primary_language"] = "What" + importer = ComponentImporter(self.alternate_super_user, formset_data=formset_data) + self.assertTrue(importer.formset.is_valid()) + expected = { + "primary_language": [ + '"What" is not in standard languages list.' "\nSuggestion(s): WebDNA, Whitespace." + ] + } + self.assertEqual(expected, importer.formset.forms[0].warnings) + + formset_data["form-0-primary_language"] = "python" + importer = ComponentImporter(self.alternate_super_user, formset_data=formset_data) + self.assertTrue(importer.formset.is_valid()) + expected = {"primary_language": ['Language will be imported with proper case: "Python"']} + self.assertEqual(expected, importer.formset.forms[0].warnings) + importer.save_all() + added_component = importer.results["added"][0] + self.assertEqual("Python", added_component.primary_language) + + def test_component_import_add_to_product(self): + self.client.login(username=self.admin_user.username, password="secret") + url = reverse("admin:component_catalog_component_import") + + formset_data = { + "form-TOTAL_FORMS": "1", + "form-INITIAL_FORMS": "0", + "form-0-name": "a", + "form-0-curation_level": 0, + } + + expected1 = "1 Added Components:" + expected2 = '' + ) + + add_perm(self.admin_user, "add_component") + response = self.client.post(url, formset_data) + self.assertContains(response, expected1) + self.assertNotContains(response, expected2) + self.assertNotContains(response, expected3) + self.assertNotContains(response, expected4) + + add_perm(self.admin_user, "add_productcomponent") + formset_data["form-0-name"] = "b" + response = self.client.post(url, formset_data) + self.assertContains(response, expected1) + self.assertNotContains(response, expected2) + self.assertNotContains(response, expected3) + self.assertNotContains(response, expected4) + + product1 = Product.objects.create(name="Product1", dataspace=self.dataspace) + assign_perm("change_product", self.admin_user, product1) + assign_perm("view_product", self.admin_user, product1) + formset_data["form-0-name"] = "c" + response = self.client.post(url, formset_data) + self.assertContains(response, expected1) + self.assertContains(response, expected2) + self.assertContains(response, expected3) + self.assertContains(response, expected4) + + def test_component_import_null_boolean_field(self): + self.client.login(username=self.super_user.username, password="secret") + url = reverse("admin:component_catalog_component_import") + + # Preview + file = os.path.join(TESTFILES_LOCATION, "component_import_null_boolean_values.csv") + with open(file) as f: + response = self.client.post(url, {"file": f}) + + expected1 = 'False' + self.assertContains(response, expected1, html=True, count=4) + + expected_template = """ + + """ + + fields = [ + "sublicense_allowed", + "express_patent_grant", + "covenant_not_to_assert", + "indemnification", + ] + for field_name in fields: + self.assertContains(response, expected_template.format(field_name), html=True) + + # Results + formset_data = { + "form-TOTAL_FORMS": "1", + "form-INITIAL_FORMS": "0", + "form-0-name": "a", + "form-0-curation_level": 0, + "form-0-sublicense_allowed": "False", + "form-0-express_patent_grant": "No", + "form-0-covenant_not_to_assert": "F", + "form-0-indemnification": "N", + } + + self.client.post(url, formset_data) + component = Component.objects.latest("id") + self.assertEqual(formset_data["form-0-name"], component.name) + self.assertTrue(component.sublicense_allowed is False) + self.assertTrue(component.express_patent_grant is False) + self.assertTrue(component.covenant_not_to_assert is False) + self.assertTrue(component.indemnification is False) + + def test_component_import_acceptable_linkages(self): + formset_data = { + "form-TOTAL_FORMS": "1", + "form-INITIAL_FORMS": "0", + "form-0-name": "C1", + "form-0-curation_level": 0, + "form-0-acceptable_linkages": "linkage1", + } + + importer = ComponentImporter(self.super_user, formset_data=formset_data) + self.assertFalse(importer.formset.is_valid()) + expected = [ + { + "acceptable_linkages": [ + "Select a valid choice. linkage1 is not one of the available choices." + ] + } + ] + self.assertEqual(expected, importer.formset.errors) + + AcceptableLinkage.objects.create(label="linkage1", dataspace=self.dataspace) + AcceptableLinkage.objects.create(label="linkage2", dataspace=self.dataspace) + + importer = ComponentImporter(self.super_user, formset_data=formset_data) + self.assertTrue(importer.formset.is_valid()) + importer.save_all() + self.assertEqual(1, len(importer.results["added"])) + added_component = importer.results["added"][0] + self.assertEqual(["linkage1"], added_component.acceptable_linkages) + + file = os.path.join(TESTFILES_LOCATION, "component_acceptable_linkages.csv") + importer = ComponentImporter(self.super_user, file) + self.assertTrue(importer.formset.is_valid()) + importer.save_all() + self.assertEqual(2, len(importer.results["added"])) + added_component1 = importer.results["added"][0] + added_component2 = importer.results["added"][1] + self.assertEqual(["linkage1"], added_component1.acceptable_linkages) + self.assertEqual(["linkage2", "linkage1"], added_component2.acceptable_linkages) + + +class PackageImporterTestCase(TestCase): + def setUp(self): + self.dataspace = Dataspace.objects.create(name="nexB") + self.super_user = create_superuser("super_user", self.dataspace) + + self.owner = Owner.objects.create( + name="Apache Software Foundation", dataspace=self.dataspace + ) + self.component_no_version = Component.objects.create( + name="log4j", owner=self.owner, dataspace=self.dataspace + ) + self.component_with_version = Component.objects.create( + name="log4j", version="1.0", owner=self.owner, dataspace=self.dataspace + ) + + self.configuration_status = ComponentStatus.objects.create( + label="Approved", + text="Approved. Ready for Component Catalog.", + dataspace=self.dataspace, + ) + + self.license = License.objects.create( + key="license", + short_name="License1", + name='BSD 2-clause "FreeBSD" License', + dataspace=self.dataspace, + owner=self.owner, + ) + + def test_package_import_invalid_values(self): + file = os.path.join(TESTFILES_LOCATION, "file_import_invalid_values.csv") + importer = PackageImporter(self.super_user, file) + self.assertFalse(importer.formset.is_valid()) + expected = [ + { + "release_date": ["Enter a valid date."], + "size": ["Enter a whole number."], + "download_url": ["Enter a valid URI."], + } + ] + self.assertEqual(expected, importer.formset.errors) + + def test_package_import_with_invalid_component_value(self): + file = os.path.join(TESTFILES_LOCATION, "package_import_with_invalid_component_value.csv") + importer = PackageImporter(self.super_user, file) + self.assertFalse(importer.formset.is_valid()) + expected = [{"component": ['Invalid format. Expected format: ":".']}] + self.assertEqual(expected, importer.formset.errors) + + def test_package_import_with_non_existing_component(self): + file = os.path.join(TESTFILES_LOCATION, "package_import_with_non_existing_component.csv") + importer = PackageImporter(self.super_user, file) + self.assertFalse(importer.formset.is_valid()) + expected = [{"component": ["Could not find the component."]}] + self.assertEqual(expected, importer.formset.errors) + + def test_package_import_proper_with_component(self): + file = os.path.join(TESTFILES_LOCATION, "file_import_proper_with_component.csv") + importer = PackageImporter(self.super_user, file) + self.assertTrue(importer.formset.is_valid()) + importer.save_all() + added_package = importer.results["added"][0] + self.assertEqual("Django-1.4.tar.gz", added_package.filename) + self.assertEqual( + "https://www.djangoproject.com/m/releases/1.4/Django-1.4.tar.gz", + added_package.download_url, + ) + self.assertEqual(datetime.date(2013, 1, 1), added_package.release_date) + self.assertEqual("851d00905eb70e4aa6384b3b8b111fb7", added_package.md5) + self.assertEqual("1bfaa4643c6775fbf394137f1533659be45441e7", added_package.sha1) + self.assertEqual(411863, added_package.size) + # A ComponentAssignedPackage relation was created + self.assertEqual(self.component_with_version, added_package.component_set.all()[0]) + + def test_package_import_clean_license_expression(self): + formset_data = { + "form-TOTAL_FORMS": "1", + "form-INITIAL_FORMS": "0", + "form-0-filename": "name.zip", + "form-0-license_expression": "bsd-new, bsd-new", + } + + importer = PackageImporter(self.super_user, formset_data=formset_data) + self.assertFalse(importer.formset.is_valid()) + expected = [ + { + "license_expression": [ + "Invalid license key: the valid characters are: letters and numbers, " + "underscore, dot, colon or hyphen signs and spaces: 'bsd-new, bsd-new'" + ] + } + ] + self.assertEqual(expected, importer.formset.errors) + + def test_package_import_unique_url_name_validation(self): + formset_data = { + "form-TOTAL_FORMS": "2", + "form-INITIAL_FORMS": "0", + "form-0-filename": "a.zip", + "form-1-filename": "a.zip", + } + + importer = PackageImporter(self.super_user, formset_data=formset_data) + self.assertFalse(importer.formset.is_valid()) + expected = [ + {}, + { + "download_url": ["This row is a duplicate."], + "filename": ["This row is a duplicate."], + "name": ["This row is a duplicate."], + "namespace": ["This row is a duplicate."], + "qualifiers": ["This row is a duplicate."], + "subpath": ["This row is a duplicate."], + "type": ["This row is a duplicate."], + "version": ["This row is a duplicate."], + }, + ] + self.assertEqual(expected, importer.formset.errors) + + formset_data["form-0-download_url"] = "http://url.com/a.zip" + formset_data["form-1-download_url"] = formset_data["form-0-download_url"] + importer = PackageImporter(self.super_user, formset_data=formset_data) + self.assertFalse(importer.formset.is_valid()) + self.assertEqual(expected, importer.formset.errors) + + formset_data["form-1-download_url"] = "http://other_url.com/a.zip" + importer = PackageImporter(self.super_user, formset_data=formset_data) + self.assertTrue(importer.formset.is_valid()) + + formset_data["form-1-download_url"] = formset_data["form-0-download_url"] + formset_data["form-1-name"] = "Name" + importer = PackageImporter(self.super_user, formset_data=formset_data) + self.assertTrue(importer.formset.is_valid()) + + def test_package_import_filename_validation(self): + formset_data = { + "form-TOTAL_FORMS": "1", + "form-INITIAL_FORMS": "0", + "form-0-filename": "a/b.zip", + } + + importer = PackageImporter(self.super_user, formset_data=formset_data) + self.assertFalse(importer.formset.is_valid()) + expected = [ + {"filename": ["Enter a valid filename: slash, backslash, or colon are not allowed."]} + ] + self.assertEqual(expected, importer.formset.errors) + + formset_data["form-0-filename"] = "a\\b.zip" + importer = PackageImporter(self.super_user, formset_data=formset_data) + self.assertFalse(importer.formset.is_valid()) + self.assertEqual(expected, importer.formset.errors) + + formset_data["form-0-filename"] = "a:b.zip" + importer = PackageImporter(self.super_user, formset_data=formset_data) + self.assertFalse(importer.formset.is_valid()) + self.assertEqual(expected, importer.formset.errors) + + formset_data["form-0-filename"] = "filename1.zip" + importer = PackageImporter(self.super_user, formset_data=formset_data) + self.assertTrue(importer.formset.is_valid()) + importer.save_all() + added_package = importer.results["added"][0] + self.assertEqual(formset_data["form-0-filename"], added_package.filename) + + def test_package_import_keywords(self): + formset_data = { + "form-TOTAL_FORMS": "1", + "form-INITIAL_FORMS": "0", + "form-0-filename": "name.zip", + } + + formset_data["form-0-keywords"] = None + importer = PackageImporter(self.super_user, formset_data=formset_data) + self.assertTrue(importer.formset.is_valid()) + + formset_data["form-0-keywords"] = "" + importer = PackageImporter(self.super_user, formset_data=formset_data) + self.assertTrue(importer.formset.is_valid()) + + formset_data["form-0-keywords"] = [] + importer = PackageImporter(self.super_user, formset_data=formset_data) + self.assertTrue(importer.formset.is_valid()) + + formset_data["form-0-keywords"] = {} + importer = PackageImporter(self.super_user, formset_data=formset_data) + self.assertTrue(importer.formset.is_valid()) + + formset_data["form-0-keywords"] = () + importer = PackageImporter(self.super_user, formset_data=formset_data) + self.assertTrue(importer.formset.is_valid()) + + formset_data["form-0-keywords"] = {"key": "value"} + importer = PackageImporter(self.super_user, formset_data=formset_data) + self.assertFalse(importer.formset.is_valid()) + expected = [{"keywords": ["Enter a list of values."]}] + self.assertEqual(expected, importer.formset.errors) + + formset_data["form-0-keywords"] = "keyword1" + importer = PackageImporter(self.super_user, formset_data=formset_data) + self.assertFalse(importer.formset.is_valid()) + expected = [ + {"keywords": ["Select a valid choice. keyword1 is not one of the available choices."]} + ] + self.assertEqual(expected, importer.formset.errors) + + formset_data["form-0-keywords"] = "keyword1,keyword2" + importer = PackageImporter(self.super_user, formset_data=formset_data) + self.assertFalse(importer.formset.is_valid()) + expected = [ + {"keywords": ["Select a valid choice. keyword1 is not one of the available choices."]} + ] + self.assertEqual(expected, importer.formset.errors) + + k1 = ComponentKeyword.objects.create(label="Keyword1", dataspace=self.dataspace) + k2 = ComponentKeyword.objects.create(label="Keyword2", dataspace=self.dataspace) + + formset_data["form-0-keywords"] = f"{k1.label}" + importer = PackageImporter(self.super_user, formset_data=formset_data) + self.assertTrue(importer.formset.is_valid()) + + formset_data["form-0-keywords"] = f" , {k1.label}, ," + importer = PackageImporter(self.super_user, formset_data=formset_data) + self.assertTrue(importer.formset.is_valid()) + + formset_data["form-0-keywords"] = f"{k1.label}, {k2.label}" + importer = PackageImporter(self.super_user, formset_data=formset_data) + self.assertTrue(importer.formset.is_valid()) + + importer.save_all() + self.assertEqual(1, len(importer.results["added"])) + added_package = importer.results["added"][0] + self.assertEqual(["Keyword1", "Keyword2"], added_package.keywords) + + # Making sure the TextInput widget is used in place of the default Select + # as loading multiple initial values is problematic on the Select + keywords_field = importer.formset.forms[0].fields["keywords"] + self.assertIsInstance(keywords_field.widget, widgets.TextInput) + + def test_package_import_scancode_json_input_issues(self): + file = os.path.join(TESTFILES_LOCATION, "package_empty.json") + importer = PackageImporter(self.super_user, file) + expected_errors = ["The content is not proper JSON."] + self.assertEqual(expected_errors, importer.fatal_errors) + + file = os.path.join(TESTFILES_LOCATION, "package_empty_json.json") + importer = PackageImporter(self.super_user, file) + expected_errors = ["No package data to import in input file."] + self.assertEqual(expected_errors, importer.fatal_errors) + + file = os.path.join(TESTFILES_LOCATION, "package_bad_json.json") + importer = PackageImporter(self.super_user, file) + expected_errors = ["The content is not proper JSON."] + self.assertEqual(expected_errors, importer.fatal_errors) + + file = os.path.join(TESTFILES_LOCATION, "package_not_json.json") + importer = PackageImporter(self.super_user, file) + expected_errors = ["The content is not proper JSON."] + self.assertEqual(expected_errors, importer.fatal_errors) + + def test_package_import_scancode_json_proper_input(self): + file = os.path.join(TESTFILES_LOCATION, "package_from_scancode.json") + importer = PackageImporter(self.super_user, file) + self.assertEqual([], importer.fatal_errors) + self.assertTrue(importer.is_from_scancode) + importer.save_all() + self.assertEqual([], importer.results["unmodified"]) + self.assertEqual(2, len(importer.results["added"])) + added_package = importer.results["added"][0] + self.assertEqual("gem", added_package.type) + self.assertEqual("", added_package.namespace) + self.assertEqual("i18n-js", added_package.name) + self.assertEqual("3.0.11", added_package.version) + self.assertEqual("Ruby", added_package.primary_language) + self.assertEqual("", added_package.license_expression) + self.assertEqual("pkg:gem/i18n-js@3.0.11", added_package.package_url) + self.assertEqual("i18n-js-3.0.11.gem", added_package.filename) + + def test_package_import_scancode_json_proper_no_summary(self): + file = os.path.join(TESTFILES_LOCATION, "package_from_scancode_no_summary.json") + importer = PackageImporter(self.super_user, file) + self.assertEqual([], importer.fatal_errors) + self.assertTrue(importer.is_from_scancode) + importer.save_all() + self.assertEqual([], importer.results["unmodified"]) + self.assertEqual(3, len(importer.results["added"])) + added_package = importer.results["added"][0] + self.assertEqual("gem", added_package.type) + self.assertEqual("", added_package.namespace) + self.assertEqual("i18n-js", added_package.name) + self.assertEqual("3.0.11", added_package.version) + self.assertEqual("Ruby", added_package.primary_language) + self.assertEqual("", added_package.license_expression) + self.assertEqual("pkg:gem/i18n-js@3.0.11", added_package.package_url) + self.assertEqual("i18n-js-3.0.11.gem", added_package.filename) + + def test_package_import_prepare_package(self): + package_data = { + "type": "maven", + "namespace": "org.apache.activemq", + "name": "activemq-camel", + "version": "5.11.0", + "qualifiers": None, + "subpath": None, + "primary_language": "Java", + "description": "ActiveMQ :: Camel\nActiveMQ component for Camel", + "release_date": None, + "parties": [], + "keywords": [], + "homepage_url": None, + "download_url": None, + "size": None, + "sha1": None, + "md5": None, + "sha256": None, + "sha512": None, + "bug_tracking_url": None, + "code_view_url": None, + "vcs_url": None, + "copyright": None, + "license_expression": None, + "declared_license": None, + "notice_text": None, + "manifest_path": "META-INF/maven/org.apache.activemq/activemq-camel/pom.xml", + "dependencies": [ + { + "purl": "pkg:maven/org.slf4j/slf4j-api", + "requirement": None, + "scope": "compile", + "is_runtime": True, + "is_optional": False, + "is_resolved": False, + } + ], + "contains_source_code": None, + "source_packages": [ + "pkg:maven/org.apache.activemq/activemq-camel@5.11.0?classifier=sources" + ], + "purl": "pkg:maven/org.apache.activemq/activemq-camel@5.11.0", + "repository_homepage_url": "https://repo1.maven.org/maven2/org/apache/" + "activemq/activemq-camel/5.11.0/", + "repository_download_url": "https://repo1.maven.org/maven2/org/apache/" + "activemq/activemq-camel/5.11.0/activemq-camel-5.11.0.jar", + "api_data_url": "https://repo1.maven.org/maven2/org/apache/activemq/" + "activemq-camel/5.11.0/activemq-camel-5.11.0.pom", + "files": [ + { + "path": "activemq-camel-5.11.0.jar-extract", + "type": "directory", + } + ], + } + expected = { + "type": "maven", + "namespace": "org.apache.activemq", + "name": "activemq-camel", + "version": "5.11.0", + "primary_language": "Java", + "description": "ActiveMQ :: Camel\nActiveMQ component for Camel", + "homepage_url": "https://repo1.maven.org/maven2/org/apache/activemq/" + "activemq-camel/5.11.0/", + "download_url": "https://repo1.maven.org/maven2/org/apache/activemq/" + "activemq-camel/5.11.0/activemq-camel-5.11.0.jar", + "dependencies": '[\n {\n "purl": "pkg:maven/org.slf4j/slf4j-api",' + '\n "requirement": null,\n "scope": "compile",' + '\n "is_runtime": true,\n "is_optional": false,' + '\n "is_resolved": false\n }\n]', + "repository_homepage_url": ( + "https://repo1.maven.org/maven2/org/apache/activemq/activemq-camel/5.11.0/" + ), + "repository_download_url": ( + "https://repo1.maven.org/maven2/org/apache/activemq/activemq-camel/" + "5.11.0/activemq-camel-5.11.0.jar" + ), + "api_data_url": ( + "https://repo1.maven.org/maven2/org/apache/activemq/activemq-camel/" + "5.11.0/activemq-camel-5.11.0.pom" + ), + "filename": "activemq-camel-5.11.0.jar", + } + prepared_package = PackageImporter.prepare_package(package_data) + self.assertEqual(expected, prepared_package) + + package_data["files"] = [] + prepared_package = PackageImporter.prepare_package(package_data) + self.assertEqual(expected, prepared_package) + + del package_data["files"] + prepared_package = PackageImporter.prepare_package(package_data) + self.assertEqual(expected, prepared_package) + + def test_package_import_add_to_product(self): + admin_user = create_admin("admin_user", self.dataspace) + self.client.login(username=admin_user.username, password="secret") + url = reverse("admin:component_catalog_package_import") + + formset_data = { + "form-TOTAL_FORMS": "1", + "form-INITIAL_FORMS": "0", + "form-0-filename": "a.zip", + } + + expected1 = "1 Added Packages:" + expected2 = '' + ) + + add_perm(admin_user, "add_package") + response = self.client.post(url, formset_data) + self.assertContains(response, expected1) + self.assertNotContains(response, expected2) + self.assertNotContains(response, expected3) + self.assertNotContains(response, expected4) + + add_perm(admin_user, "add_productpackage") + formset_data["form-0-filename"] = "b.zip" + response = self.client.post(url, formset_data) + self.assertContains(response, expected1) + self.assertNotContains(response, expected2) + self.assertNotContains(response, expected3) + self.assertNotContains(response, expected4) + + product1 = Product.objects.create(name="Product1", dataspace=self.dataspace) + assign_perm("change_product", admin_user, product1) + assign_perm("view_product", admin_user, product1) + formset_data["form-0-filename"] = "c.zip" + response = self.client.post(url, formset_data) + self.assertContains(response, expected1) + self.assertContains(response, expected2) + self.assertContains(response, expected3) + self.assertContains(response, expected4) + + +class SubcomponentImporterTestCase(TestCase): + def setUp(self): + self.dataspace = Dataspace.objects.create(name="nexB") + self.super_user = create_superuser("super_user", self.dataspace) + + self.owner = Owner.objects.create( + name="Apache Software Foundation", dataspace=self.dataspace + ) + + self.configuration_status = ComponentStatus.objects.create( + label="Approved", + text="Approved. Ready for Component Catalog.", + dataspace=self.dataspace, + ) + + self.license1 = License.objects.create( + key="apache-2.0", + short_name="Apache 2.0", + name="Apache License 2.0", + dataspace=self.dataspace, + owner=self.owner, + ) + + self.license2 = License.objects.create( + key="apache-1.1", + short_name="Apache 1.1", + name="Apache License 1.1", + dataspace=self.dataspace, + owner=self.owner, + ) + + self.component_no_version = Component.objects.create( + name="log4j", + license_expression=self.license1.key, + owner=self.owner, + dataspace=self.dataspace, + ) + self.component_with_version = Component.objects.create( + name="log4j", version="1.0", owner=self.owner, dataspace=self.dataspace + ) + + self.license_ct = ContentType.objects.get_for_model(License) + self.subcomponent_ct = ContentType.objects.get_for_model(Subcomponent) + + self.license_policy = UsagePolicy.objects.create( + label="LicensePolicy", + icon="icon", + content_type=self.license_ct, + dataspace=self.dataspace, + ) + + self.subcomponent_policy = UsagePolicy.objects.create( + label="SubcomponentPolicy", + icon="icon", + content_type=self.subcomponent_ct, + dataspace=self.dataspace, + ) + + def test_subcomponent_import_mandatory_columns(self): + # The first file is missing the mandatory columns + file = os.path.join(TESTFILES_LOCATION, "missing_mandatory_columns.csv") + importer = SubcomponentImporter(self.super_user, file) + # Make sure the errors are returned and no results + expected_errors = [ + 'Required column missing: "parent".', + 'Required column missing: "child".', + ] + self.assertEqual(expected_errors, importer.fatal_errors) + self.assertFalse(getattr(importer, "formset", None)) + + # Now using a file containing the mandatory columns + file = os.path.join(TESTFILES_LOCATION, "subcomponent_including_mandatory_columns.csv") + importer = SubcomponentImporter(self.super_user, file) + # Make sure no errors are returned and we got some results + self.assertFalse(importer.fatal_errors) + self.assertTrue(importer.formset) + self.assertTrue(importer.formset.is_valid()) + + def test_subcomponent_import_mandatory_data(self): + # The first line is missing the mandatory columns + file = os.path.join(TESTFILES_LOCATION, "subcomponent_missing_mandatory_data.csv") + importer = SubcomponentImporter(self.super_user, file) + expected = [{"child": ["This field is required."]}] + self.assertEqual(expected, importer.formset.errors) + + def test_subcomponent_import_component_syntax_issues(self): + file = os.path.join(TESTFILES_LOCATION, "subcomponent_component_syntax_issues.csv") + importer = SubcomponentImporter(self.super_user, file) + expected = [ + { + "child": ["Could not find the component."], + "parent": ['Invalid format. Expected format: ":".'], + } + ] + self.assertEqual(expected, importer.formset.errors) + + def test_subcomponent_import_parent_equal_child(self): + file = os.path.join(TESTFILES_LOCATION, "subcomponent_parent_equal_child.csv") + importer = SubcomponentImporter(self.super_user, file) + expected = [{NON_FIELD_ERRORS: ["This Object cannot be his own child or parent."]}] + self.assertEqual(expected, importer.formset.errors) + + def test_subcomponent_import_parent_is_descendant_of_child(self): + Subcomponent.objects.create( + parent=self.component_with_version, + child=self.component_no_version, + dataspace=self.dataspace, + ) + + file = os.path.join(TESTFILES_LOCATION, "subcomponent_parent_is_descendant_of_child.csv") + importer = SubcomponentImporter(self.super_user, file) + expected = [ + { + NON_FIELD_ERRORS: [ + "The current Object is a descendant of the selected child," + " it cannot also be a parent for it." + ] + } + ] + self.assertEqual(expected, importer.formset.errors) + + def test_subcomponent_import_save_all(self): + component3 = Component.objects.create(name="log4j", version="3.0", dataspace=self.dataspace) + + subcomponent = Subcomponent.objects.create( + parent=self.component_with_version, + child=self.component_no_version, + dataspace=self.dataspace, + ) + + # Manually creating a formset with 1 new Subcomponent and 1 existing + formset_data = { + "form-TOTAL_FORMS": "2", + "form-INITIAL_FORMS": "0", + "form-0-parent": "{}:{}".format( + self.component_with_version.name, self.component_with_version.version + ), + "form-0-child": "{}:{}".format(component3.name, component3.version), + "form-0-notes": "Notes", + "form-0-purpose": "Core", + "form-0-package_paths": "/path/", + "form-0-extra_attribution_text": "Extra Attrib", + "form-1-parent": "{}:{}".format( + self.component_with_version.name, self.component_with_version.version + ), + "form-1-child": "{}:{}".format( + self.component_no_version.name, self.component_no_version.version + ), + "form-1-notes": "", + "form-1-purpose": "", + "form-1-package_paths": "", + "form-1-extra_attribution": "", + } + importer = SubcomponentImporter(self.super_user, formset_data=formset_data) + self.assertTrue(importer.formset.is_valid()) + importer.save_all() + self.assertEqual(importer.results["unmodified"], [subcomponent]) + self.assertEqual(1, len(importer.results["added"])) + added_subcomponent = importer.results["added"][0] + self.assertTrue(added_subcomponent) + self.assertTrue(added_subcomponent.id) + self.assertEqual("Notes", added_subcomponent.notes) + self.assertEqual("Core", added_subcomponent.purpose) + self.assertEqual("/path/", added_subcomponent.package_paths) + self.assertEqual("Extra Attrib", added_subcomponent.extra_attribution_text) + self.assertEqual(component3.pk, added_subcomponent.child.pk) + self.assertEqual(self.component_with_version.pk, added_subcomponent.parent.pk) + + def test_subcomponent_import_license_expression(self): + formset_data = { + "form-TOTAL_FORMS": "1", + "form-INITIAL_FORMS": "0", + "form-0-parent": "{}:{}".format( + self.component_with_version.name, self.component_with_version.version + ), + "form-0-child": "{}:{}".format( + self.component_no_version.name, self.component_no_version.version + ), + "form-0-license_expression": "", # license_expression is not mandatory + } + + # license_expression is not provided, taken form the Component by default + importer = SubcomponentImporter(self.super_user, formset_data=formset_data) + self.assertFalse(importer.fatal_errors) + self.assertTrue(importer.formset) + self.assertTrue(importer.formset.is_valid()) + importer.save_all() + self.assertEqual(1, len(importer.results["added"])) + added_subcomponent = importer.results["added"][0] + self.assertEqual(1, added_subcomponent.licenses.count()) + self.assertEqual(self.license1, added_subcomponent.licenses.all()[0]) + self.assertEqual( + added_subcomponent.child.license_expression, added_subcomponent.license_expression + ) + added_subcomponent.delete() + + formset_data["form-0-license_expression"] = "{} AND {}".format( + self.license1.key, self.license2.key + ) + + importer = SubcomponentImporter(self.super_user, formset_data=formset_data) + expected = [ + { + "license_expression": [ + "Unknown license key(s): apache-1.1
      Available licenses: apache-2.0" + ] + } + ] + self.assertEqual(expected, importer.formset.errors) + + formset_data["form-0-license_expression"] = self.license1.key + importer = SubcomponentImporter(self.super_user, formset_data=formset_data) + self.assertTrue(importer.formset.is_valid()) + importer.save_all() + self.assertEqual(1, len(importer.results["added"])) + added_subcomponent = importer.results["added"][0] + self.assertEqual(1, added_subcomponent.licenses.count()) + self.assertEqual(self.license1, added_subcomponent.licenses.all()[0]) + + def test_subcomponent_import_usage_policy_help_supported_values(self): + url = reverse("admin:component_catalog_subcomponent_import") + self.client.login(username="super_user", password="secret") + + response = self.client.get(url) + self.assertContains( + response, "
    • {}
    • ".format(self.subcomponent_policy) + ) + self.assertNotContains(response, "
    • {}
    • ".format(self.license_policy)) + + def test_subcomponent_import_valid_usage_policy(self): + file = os.path.join(TESTFILES_LOCATION, "subcomponent_valid_usage_policy.csv") + # Using the same label to make sure the Policy is scoped by ContentType + self.license_policy.label = self.subcomponent_policy.label + self.license_policy.save() + importer = SubcomponentImporter(self.super_user, file) + self.assertTrue(importer.formset.is_valid()) + policy_field = importer.formset.forms[0].fields["usage_policy"] + expected = self.subcomponent_policy.get_admin_link(target="_blank") + self.assertEqual(expected, policy_field.value_for_display) + importer.save_all() + self.assertEqual(1, len(importer.results["added"])) + subcomponent = importer.results["added"][0] + self.assertEqual(self.subcomponent_policy, subcomponent.usage_policy) + self.assertTrue(subcomponent.is_deployed) + self.assertTrue(subcomponent.is_modified) + + def test_subcomponent_import_invalid_usage_policy(self): + file = os.path.join(TESTFILES_LOCATION, "subcomponent_invalid_usage_policy.csv") + importer = SubcomponentImporter(self.super_user, file) + self.assertFalse(importer.formset.is_valid()) + expected = [ + { + "usage_policy": [ + 'That choice is not one of the available choices: "SubcomponentPolicy"' + ] + } + ] + self.assertEqual(expected, importer.formset.errors) diff --git a/component_catalog/tests/test_license_expression_dje.py b/component_catalog/tests/test_license_expression_dje.py new file mode 100644 index 00000000..1add8e18 --- /dev/null +++ b/component_catalog/tests/test_license_expression_dje.py @@ -0,0 +1,384 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# DejaCode is a trademark of nexB Inc. +# SPDX-License-Identifier: AGPL-3.0-only +# See https://github.com/nexB/dejacode for support or download. +# See https://aboutcode.org for more information about AboutCode FOSS projects. +# + +import json +import os +from collections import namedtuple +from itertools import zip_longest +from unittest import TestCase + +from django.core.exceptions import ValidationError + +from license_expression import LicenseSymbolLike +from license_expression import ParseError +from license_expression import as_symbols + +from component_catalog.license_expression_dje import build_licensing +from component_catalog.license_expression_dje import get_license_objects +from component_catalog.license_expression_dje import get_unique_license_keys +from component_catalog.license_expression_dje import normalize_and_validate_expression +from component_catalog.license_expression_dje import parse_expression + +MockLicense = namedtuple("MockLicense", "key aliases is_exception") + + +class LicenseExpressionDjeTestCase(TestCase): + def test_as_symbols(self): + lic1 = MockLicense("x11", ["X11 License"], True) + lic2 = MockLicense("x11-xconsortium", ["X11 XConsortium"], False) + licenses = [lic1, lic2] + results = list(as_symbols(licenses)) + expected = [LicenseSymbolLike(lic1), LicenseSymbolLike(lic2)] + self.assertEqual(expected, results) + + def test_get_license_objects_with_spaces_in_keys_raise_exception(self): + lic1 = MockLicense("x11", ["X11 License"], True) + lic2 = MockLicense("x11-xconsortium", ["X11 XConsortium"], False) + lic3 = MockLicense("gps-2.0-plus-ekiga", ["GPL 2.0 or later with Ekiga exception"], False) + licenses = [lic1, lic2, lic3] + expression = "x11 or x11 Xconsortium OR gps-2.0-plus-ekiga" + try: + get_license_objects(expression, licenses) + except ParseError as pe: + self.assertEqual( + 'Invalid symbols sequence such as (A B) for token: "Xconsortium" at position: 11', + str(pe), + ) + + def test_get_license_objects_without_spaces_in_keys(self): + lic1 = MockLicense("x11", ["X11 License"], True) + lic2 = MockLicense("x11-xconsortium", ["X11 XConsortium"], False) + lic3 = MockLicense("gps-2.0-plus-ekiga", ["GPL 2.0 or later with Ekiga exception"], False) + licenses = [lic1, lic2, lic3] + expression = "x11 or x11-Xconsortium OR gps-2.0-plus-ekiga" + results = get_license_objects(expression, licenses) + self.assertEqual(licenses, results) + + def test_normalize_and_validate_expression(self): + expression = "gpl and BSD" + licenses = [MockLicense("GPL", [], False), MockLicense("bsd", [], False)] + normalize_and_validate_expression(expression, licenses) + + def test_normalize_and_validate_expression_with_exception(self): + licenses = [ + MockLicense("gps-2.0", ["GPL 2.0"], False), + MockLicense("classpath-2.0", ["Classpath Exception 2.0"], True), + MockLicense("gps-2.0-plus", ["GPL 2.0 or later"], False), + MockLicense("lgps-2.1-plus", ["LGPL 2.1 or later"], False), + ] + expression = ( + "GPL 2.0 or later with Classpath Exception 2.0 or GPL 2.0" + " or later and LGPL 2.1 or later" + ) + results = normalize_and_validate_expression(expression, licenses) + expected = "gps-2.0-plus WITH classpath-2.0 OR (gps-2.0-plus AND lgps-2.1-plus)" + self.assertEqual(expected, results) + + def test_normalize_expression_without_symbols_with_keys_containing_keywords(self): + expression = " withorand with orribleand or orwithand and andwithor or orandwith" + results = normalize_and_validate_expression(expression, validate_known=False) + expected = "withorand WITH orribleand OR (orwithand AND andwithor) OR orandwith" + self.assertEqual(expected, results) + + def test_normalize_expression_without_symbols_and_with_validate(self): + expression = "gps-2.0 with Classpath or gps-2.0-plus and lgps-2.1-plus or oracle-bcl" + results = normalize_and_validate_expression(expression, validate_known=False) + expected = "gps-2.0 WITH Classpath OR (gps-2.0-plus AND lgps-2.1-plus) OR oracle-bcl" + self.assertEqual(expected, results) + + def test_normalize_expression_without_symbols_and_with_validate_known_True(self): + expression = "gps-2.0 with Classpath or gps-2.0-plus and lgps-2.1-plus or oracle-bcl" + try: + normalize_and_validate_expression(expression, validate_known=True) + self.fail("ValidationError not raised") + except ValidationError as e: + self.assertEqual( + "Unknown license key(s): gps-2.0, Classpath, gps-2.0-plus," + " lgps-2.1-plus, oracle-bcl", + e.message, + ) + + def test_normalize_expression_raise_exception_if_symbol_has_spaces(self): + expression = "gps-2.0 with classpath or gps-2.0-plus and lgpl 2.1-plus or oracle-bcl or" + try: + normalize_and_validate_expression(expression, validate_known=False, simple=True) + self.fail("ValidationError not raised") + except Exception as e: + self.assertEqual( + 'Invalid symbols sequence such as (A B) for token: "2.1-plus" at position: 48', + e.message, + ) + + def test_normalize_expression_raise_exception_if_symbol_has_spaces_with_advanced(self): + expression = "gps-2.0 with classpath or gps-2.0-plus and lgpl 2.1-plus or oracle-bcl or" + results = normalize_and_validate_expression(expression, validate_known=False, simple=False) + expected = "gps-2.0 WITH classpath OR (gps-2.0-plus AND lgpl 2.1-plus) OR oracle-bcl" + self.assertEqual(expected, results) + + def test_normalize_xpression_without_symbols_exception_for_unknown_symbols(self): + expression = "gps-2.0 with class path or gpl 2.0-plus and lgpl 2.1-plus or oracle bcl" + try: + normalize_and_validate_expression(expression, validate_known=True) + self.fail("ValidationError not raised") + except ValidationError as e: + self.assertEqual( + "Unknown license key(s): gps-2.0, class path, gpl 2.0-plus," + " lgpl 2.1-plus, oracle bcl", + e.message, + ) + + def test_normalize_expression_without_symbols_does_not_raise_for_unknown_symbols(self): + expression = "gpl 2.0 with class path or gpl 2.0-plus and lgpl 2.1-plus or oracle bcl" + result = normalize_and_validate_expression(expression, validate_known=False) + expected = "gpl 2.0 WITH class path OR (gpl 2.0-plus AND lgpl 2.1-plus) OR oracle bcl" + self.assertEqual(expected, result) + + def test_normalize_expression_without_symbols_raise_exception_if_syntax_incorrect(self): + expression = "gps-2.0 with classpath or with gps-2.0-plus" + try: + normalize_and_validate_expression(expression, validate_known=False) + self.fail("ValidationError not raised") + except ValidationError as e: + self.assertEqual('Invalid expression for token: "with" at position: 26', e.message) + + def test_normalize_and_validate_ticket_503(self): + expression = ( + "apache-2.0 AND (bsd-new AND bsd-simplified AND mit AND zlib " + "AND cddl-1.0 AND gps-2.0-classpath AND cddl-1.1 AND sax-pd " + "AND jdom AND w3c AND public-domain AND eps-1.0 AND protobuf AND gps-2.0-gcc)" + ) + results = normalize_and_validate_expression(expression, validate_known=False) + expected = ( + "apache-2.0 AND (bsd-new AND bsd-simplified AND mit AND zlib " + "AND cddl-1.0 AND gps-2.0-classpath AND cddl-1.1 AND sax-pd " + "AND jdom AND w3c AND public-domain AND eps-1.0 AND protobuf AND gps-2.0-gcc)" + ) + self.assertEqual(expected, results) + + def test_normalize_and_validate_ticket_503_with_licensing(self): + licenses = [ + MockLicense("mit", ["MIT"], False), + MockLicense("zlib", ["zlib"], False), + MockLicense("d-zlib", ["D zlib"], False), + MockLicense("mitr", ["mit or"], False), + ] + + expression = "mit AND zlib or mit or mit" + try: + normalize_and_validate_expression(expression, licenses, validate_known=True) + self.fail("Exception not raised") + except ValidationError as e: + expected = 'Invalid symbols sequence such as (A B) for token: "mit" at position: 23' + self.assertEqual(expected, e.message) + + def test_normalize_and_validate_ticket_503_with_licensing_valid(self): + licenses = [ + MockLicense("mit", ["MIT"], False), + MockLicense("zlib", ["zlib"], False), + MockLicense("d-zlib", ["D zlib"], False), + MockLicense("mitr", ["mit or"], False), + ] + + expression = "mit AND zlib or mit or and mit" + results = normalize_and_validate_expression(expression, licenses, validate_known=True) + expected = "(mit AND zlib) OR (mitr AND mit)" + self.assertEqual(expected, results) + + def test_normalize_and_validate_ticket_503_with_licensing_2(self): + licenses = [ + MockLicense("mit", ["MIT"], False), + MockLicense("zlib", ["zlib"], False), + MockLicense("d-zlib", ["D zlib"], False), + MockLicense("mitr", ["mit or NOT"], False), + ] + + expression = "mit AND zlib or mit or mit" + results = normalize_and_validate_expression(expression, licenses, validate_known=True) + expected = "(mit AND zlib) OR mit OR mit" + self.assertEqual(expected, results) + + def test_expression_is_equivalent(self): + licenses = [ + MockLicense("gps-2.0", ["GPL 2.0"], False), + MockLicense("classpath-2.0", ["Classpath Exception 2.0"], True), + MockLicense("gps-2.0-plus", ["GPL 2.0 or later"], False), + MockLicense("lgps-2.1-plus", ["LGPL 2.1 or later"], False), + ] + ex1 = ( + "(GPL 2.0 or later with Classpath Exception 2.0 or GPL 2.0 or later)" + " and LGPL 2.1 or later" + ) + expression1 = parse_expression(ex1, licenses) + ex2 = ( + "LGPL 2.1 or later and (GPL 2.0 or later oR" + " GPL 2.0 or later with Classpath Exception 2.0)" + ) + expression2 = parse_expression(ex2, licenses) + ex3 = "LGPL 2.1 or later and (GPL 2.0 or later oR GPL 2.0 or later)" + expression3 = parse_expression(ex3, licenses) + + licensing = build_licensing(licenses) + self.assertTrue(licensing.is_equivalent(expression1, expression2)) + self.assertTrue(licensing.is_equivalent(expression2, expression1)) + self.assertFalse(licensing.is_equivalent(expression1, expression3)) + self.assertFalse(licensing.is_equivalent(expression2, expression3)) + + def test_expression_is_equivalent_no_spaces(self): + licenses = [ + MockLicense("gps-2.0", ["GPL-2.0"], False), + MockLicense("classpath-2.0", ["Classpath-Exception-2.0"], True), + MockLicense("gps-2.0-plus", ["GPL-2.0-or-later"], False), + MockLicense("lgps-2.1-plus", ["LGPL-2.1-or-later"], False), + ] + ex1 = ( + "(GPL-2.0-or-later with Classpath-Exception-2.0 or GPL-2.0-or-later)" + " and LGPL-2.1-or-later" + ) + expression1 = parse_expression(ex1, licenses) + ex2 = ( + "LGPL-2.1-or-later and (GPL-2.0-or-later oR GPL-2.0-or-later" + " with Classpath-Exception-2.0)" + ) + expression2 = parse_expression(ex2, licenses) + ex3 = "LGPL-2.1-or-later and (GPL-2.0-or-later oR GPL-2.0-or-later)" + expression3 = parse_expression(ex3, licenses) + + licensing = build_licensing(licenses) + self.assertTrue(licensing.is_equivalent(expression1, expression2)) + self.assertTrue(licensing.is_equivalent(expression2, expression1)) + self.assertFalse(licensing.is_equivalent(expression1, expression3)) + self.assertFalse(licensing.is_equivalent(expression2, expression3)) + + def test_get_unique_license_keys(self): + expression = "(bsd-new OR eps-1.0 OR apache-2.0 OR mit) AND unknown AND bsd-new" + expected = {"unknown", "bsd-new", "apache-2.0", "mit", "eps-1.0"} + self.assertEqual(expected, get_unique_license_keys(expression)) + + expression = "gps-2.0 WITH classpath OR (gps-2.0 AND lgpl) OR oracle-bcl" + expected = {"lgpl", "oracle-bcl", "gps-2.0", "classpath"} + self.assertEqual(expected, get_unique_license_keys(expression)) + + +def _print_sequence_diff(left, right): + for lft, rht in zip_longest(left.split(), right.split()): + if lft == rht: + continue + print("left:", lft, "!= right:", rht) + + +class LicenseExpressionDataTestCase(TestCase): + def check_parse(self, keyword, expression_items, keys, licenses): + expression = keyword.join(expression_items) + expected = keyword.upper().join(keys) + result = normalize_and_validate_expression( + expression, + licenses=licenses, + validate_known=True, + validate_strict=False, + include_available=False, + ) + if expected != result: + _print_sequence_diff(expected, result) + self.assertEqual(expected, result) + + def test_normalize_and_validate_expression_on_all_licenses(self): + # See testfiles/all_licenses.py to recreate this test data set + self.maxDiff = None + + test_data = os.path.join(os.path.dirname(__file__), "testfiles", "all_licenses.json") + with open(test_data) as f: + licenses = json.load(f) + + keys = [key for key, _, _ in licenses] + + # using keys only + key_only_symbols = [MockLicense(key, [], False) for key, _, _ in licenses] + expression_items = keys + self.check_parse(" and ", expression_items, keys=keys, licenses=key_only_symbols) + self.check_parse(" or ", expression_items, keys=keys, licenses=key_only_symbols) + + # using key + short name + short_name_alias_symbols = [ + MockLicense(key, [short_name], False) for key, _, short_name in licenses + ] + expression_items = keys + self.check_parse(" and ", expression_items, keys=keys, licenses=short_name_alias_symbols) + self.check_parse(" or ", expression_items, keys=keys, licenses=short_name_alias_symbols) + + short_names = [short_name for _, _, short_name in licenses] + expression_items = short_names + self.check_parse(" and ", expression_items, keys=keys, licenses=short_name_alias_symbols) + self.check_parse(" or ", expression_items, keys=keys, licenses=short_name_alias_symbols) + + expression_items = keys + short_names + self.check_parse( + " and ", expression_items, keys=keys + keys, licenses=short_name_alias_symbols + ) + self.check_parse( + " or ", expression_items, keys=keys + keys, licenses=short_name_alias_symbols + ) + + # using full aliases + full_alias_symbols = [ + MockLicense(key, [short_name, name], False) for key, name, short_name in licenses + ] + expression_items = keys + self.check_parse(" and ", expression_items, keys=keys, licenses=full_alias_symbols) + self.check_parse(" or ", expression_items, keys=keys, licenses=full_alias_symbols) + + expression_items = short_names + self.check_parse(" and ", expression_items, keys=keys, licenses=full_alias_symbols) + self.check_parse(" or ", expression_items, keys=keys, licenses=full_alias_symbols) + + expression_items = keys + short_names + self.check_parse(" and ", expression_items, keys=keys + keys, licenses=full_alias_symbols) + self.check_parse(" or ", expression_items, keys=keys + keys, licenses=full_alias_symbols) + + names = [name for _, name, _ in licenses] + expression_items = names + self.check_parse(" and ", expression_items, keys=keys, licenses=full_alias_symbols) + self.check_parse(" or ", expression_items, keys=keys, licenses=full_alias_symbols) + + expression_items = keys + names + self.check_parse(" and ", expression_items, keys=keys + keys, licenses=full_alias_symbols) + self.check_parse(" or ", expression_items, keys=keys + keys, licenses=full_alias_symbols) + + expression_items = short_names + keys + names + self.check_parse( + " and ", expression_items, keys=keys + keys + keys, licenses=full_alias_symbols + ) + self.check_parse( + " or ", expression_items, keys=keys + keys + keys, licenses=full_alias_symbols + ) + + # using full aliases and with + expression = " and ".join(" with ".join([x, y]) for x, y in zip(keys, names)) + expected = " AND ".join(" WITH ".join([x, y]) for x, y in zip(keys, keys)) + result = normalize_and_validate_expression( + expression, + licenses=full_alias_symbols, + validate_known=True, + validate_strict=False, + include_available=False, + ) + if expected != result: + _print_sequence_diff(expected, result) + self.assertEqual(expected, result) + + expression = " or ".join(" with ".join([x, y]) for x, y in zip(keys, names)) + expected = " OR ".join(" WITH ".join([x, y]) for x, y in zip(keys, keys)) + result = normalize_and_validate_expression( + expression, + licenses=full_alias_symbols, + validate_known=True, + validate_strict=False, + include_available=False, + ) + if expected != result: + _print_sequence_diff(expected, result) + self.assertEqual(expected, result) diff --git a/component_catalog/tests/test_models.py b/component_catalog/tests/test_models.py new file mode 100644 index 00000000..342d9f9c --- /dev/null +++ b/component_catalog/tests/test_models.py @@ -0,0 +1,2403 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# DejaCode is a trademark of nexB Inc. +# SPDX-License-Identifier: AGPL-3.0-only +# See https://github.com/nexB/dejacode for support or download. +# See https://aboutcode.org for more information about AboutCode FOSS projects. +# + +import json +import uuid +from operator import attrgetter +from unittest import mock + +from django.contrib.contenttypes.models import ContentType +from django.core.exceptions import ValidationError +from django.db import IntegrityError +from django.db import transaction +from django.db.models import ProtectedError +from django.db.utils import DataError +from django.test import TestCase +from django.urls import reverse + +import license_expression +import requests +from license_expression import Licensing + +from component_catalog.importers import ComponentImporter +from component_catalog.importers import PackageImporter +from component_catalog.models import Component +from component_catalog.models import ComponentAssignedLicense +from component_catalog.models import ComponentAssignedPackage +from component_catalog.models import ComponentKeyword +from component_catalog.models import ComponentStatus +from component_catalog.models import ComponentType +from component_catalog.models import LicenseExpressionMixin +from component_catalog.models import Package +from component_catalog.models import Subcomponent +from dejacode_toolkit import download +from dejacode_toolkit.download import DataCollectionException +from dejacode_toolkit.download import collect_package_data +from dje.copier import copy_object +from dje.models import Dataspace +from dje.models import History +from dje.tests import add_perm +from dje.tests import create_admin +from dje.tests import create_superuser +from dje.tests import create_user +from license_library.models import License +from license_library.models import LicenseAssignedTag +from license_library.models import LicenseChoice +from license_library.models import LicenseTag +from organization.models import Owner +from product_portfolio.models import Product +from product_portfolio.models import ProductComponent +from product_portfolio.models import ProductPackage + + +class ComponentCatalogModelsTestCase(TestCase): + def setUp(self): + self.dataspace = Dataspace.objects.create(name="nexB") + self.other_dataspace = Dataspace.objects.create(name="other") + self.user = create_superuser("nexb_user", self.dataspace) + self.admin_user = create_admin("admin_user", self.dataspace) + + self.owner = Owner.objects.create(name="Owner", dataspace=self.dataspace) + self.other_owner = Owner.objects.create(name="Other_Org", dataspace=self.other_dataspace) + self.license1 = License.objects.create( + key="license1", + name="License1", + short_name="L1", + is_active=True, + spdx_license_key="SPDX-1", + owner=self.owner, + dataspace=self.dataspace, + ) + self.license2 = License.objects.create( + key="license2", + name="License2", + short_name="L2", + is_active=True, + owner=self.owner, + dataspace=self.dataspace, + ) + self.component_type = ComponentType.objects.create(label="type1", dataspace=self.dataspace) + self.configuration_status = ComponentStatus.objects.create( + label="Status1", dataspace=self.dataspace + ) + self.status1 = ComponentStatus.objects.create( + label="Label1", text="Status1", default_on_addition=True, dataspace=self.dataspace + ) + self.status2 = ComponentStatus.objects.create( + label="Label2", text="Status2", default_on_addition=True, dataspace=self.dataspace + ) + self.component1 = Component.objects.create( + owner=self.owner, + name="a", + version="1.0", + type=self.component_type, + dataspace=self.dataspace, + ) + self.c1 = Component.objects.create( + name="c1", owner=self.owner, type=self.component_type, dataspace=self.dataspace + ) + self.c2 = Component.objects.create( + name="c2", owner=self.owner, type=self.component_type, dataspace=self.dataspace + ) + self.c3 = Component.objects.create( + name="c3", owner=self.owner, type=self.component_type, dataspace=self.dataspace + ) + self.c4 = Component.objects.create( + name="c4", owner=self.owner, type=self.component_type, dataspace=self.dataspace + ) + self.sub_1_2 = Subcomponent.objects.create( + parent=self.c1, child=self.c2, dataspace=self.c1.dataspace + ) + self.sub_1_3 = Subcomponent.objects.create( + parent=self.c1, child=self.c3, dataspace=self.c1.dataspace + ) + self.sub_2_4 = Subcomponent.objects.create( + parent=self.c2, child=self.c4, dataspace=self.c2.dataspace + ) + self.sub_3_4 = Subcomponent.objects.create( + parent=self.c3, child=self.c4, dataspace=self.c3.dataspace + ) + + def test_component_unique_together(self): + # A unique component is defined by: + # Dataspace + Component Name + Component Version + # It's ok to have a blank "" (empty string) value for the Version + + # First, we try to create a duplication entry of component1 + dup_component = Component( + name=self.component1.name, version=self.component1.version, dataspace=self.dataspace + ) + + with self.assertRaises(IntegrityError), transaction.atomic(): + dup_component.save() + + # Changing the version + dup_component.version = "" + dup_component.save() + + dup_component.id = None # Force insert + dup_component.uuid = uuid.uuid4() + + with self.assertRaises(IntegrityError), transaction.atomic(): + dup_component.save() + + dup_component.name = "New Name" + dup_component.save() + + def test_component_model_clean_validate_against_reference_data(self): + c = Component( + name=self.c1.name, + version=self.c1.version, + dataspace=self.other_dataspace, + ) + + with self.assertRaises(ValidationError) as cm: + c.clean() + + self.assertEqual(2, len(cm.exception.error_dict.keys())) + self.assertIn("name", cm.exception.error_dict.keys()) + self.assertIn("version", cm.exception.error_dict.keys()) + + absolute_link = self.c1.get_absolute_link(target="_blank") + copy_link = self.c1.get_html_link( + self.c1.get_copy_url(), value="Copy to my Dataspace", target="_blank" + ) + + error = ( + "The application object that you are creating already exists as " + "{} in the reference dataspace. {}".format(absolute_link, copy_link) + ) + + self.assertEqual(error, cm.exception.error_dict["version"][0].message) + self.assertEqual(error, cm.exception.error_dict["name"][0].message) + + copy_object(self.c1, self.other_dataspace, self.user) + c.clean() # Skipped to not raised along _perform_unique_checks() + + def test_component_ordering(self): + # All the Component will be create with this type to query only on this set + type_ordering = ComponentType.objects.create(label="ordering", dataspace=self.dataspace) + # Creating the component in out of order on purpose + Component.objects.create(name="b", version="", type=type_ordering, dataspace=self.dataspace) + Component.objects.create( + name="b", version="1", type=type_ordering, dataspace=self.dataspace + ) + Component.objects.create( + name="a", version="2.0", type=type_ordering, dataspace=self.dataspace + ) + Component.objects.create( + name="a", version="4.0", type=type_ordering, dataspace=self.dataspace + ) + Component.objects.create( + name="a", version="3.0", type=type_ordering, dataspace=self.dataspace + ) + Component.objects.create( + name="a", version="1", type=type_ordering, dataspace=self.dataspace + ) + Component.objects.create( + name="c", version="1.0", type=type_ordering, dataspace=self.dataspace + ) + + # Expected values in order + expected = [ + ("a", "1"), + ("a", "2.0"), + ("a", "3.0"), + ("a", "4.0"), + ("b", ""), + ("b", "1"), + ("c", "1.0"), + ] + + # Testing the default ordering of the queryset + queryset = Component.objects.filter(type=type_ordering) + self.assertEqual(7, queryset.count()) + qs_values = queryset.values_list("name", "version") + self.assertEqual(list(expected), list(qs_values)) + + def test_component_get_children(self): + self.assertEqual([self.c2, self.c3], list(self.c1.get_children())) + self.assertEqual([self.c4], list(self.c2.get_children())) + self.assertEqual([self.c4], list(self.c3.get_children())) + self.assertEqual([], list(self.c4.get_children())) + + def test_component_get_parents(self): + self.assertEqual([], list(self.c1.get_parents())) + self.assertEqual([self.c1], list(self.c2.get_parents())) + self.assertEqual([self.c1], list(self.c3.get_parents())) + self.assertEqual([self.c2, self.c3], list(self.c4.get_parents())) + + def test_component_is_child_of(self): + self.assertFalse(self.c1.is_child_of(self.c1)) + self.assertFalse(self.c1.is_child_of(self.c2)) + self.assertFalse(self.c1.is_child_of(self.c3)) + self.assertFalse(self.c1.is_child_of(self.c4)) + + self.assertTrue(self.c2.is_child_of(self.c1)) + self.assertFalse(self.c2.is_child_of(self.c2)) + self.assertFalse(self.c2.is_child_of(self.c3)) + self.assertFalse(self.c2.is_child_of(self.c4)) + + self.assertFalse(self.c4.is_child_of(self.c1)) + self.assertTrue(self.c4.is_child_of(self.c2)) + self.assertTrue(self.c4.is_child_of(self.c3)) + self.assertFalse(self.c4.is_child_of(self.c4)) + + def test_component_is_parent_of(self): + self.assertFalse(self.c1.is_parent_of(self.c1)) + self.assertTrue(self.c1.is_parent_of(self.c2)) + self.assertTrue(self.c1.is_parent_of(self.c3)) + self.assertFalse(self.c1.is_parent_of(self.c4)) + + self.assertFalse(self.c2.is_parent_of(self.c1)) + self.assertFalse(self.c2.is_parent_of(self.c2)) + self.assertFalse(self.c2.is_parent_of(self.c3)) + self.assertTrue(self.c2.is_parent_of(self.c4)) + + self.assertFalse(self.c4.is_parent_of(self.c1)) + self.assertFalse(self.c4.is_parent_of(self.c2)) + self.assertFalse(self.c4.is_parent_of(self.c3)) + self.assertFalse(self.c4.is_parent_of(self.c4)) + + def test_component_get_ancestors(self): + self.assertEqual([], list(self.c1.get_ancestors())) + self.assertEqual([self.c1], list(self.c2.get_ancestors())) + self.assertEqual([self.c1], list(self.c3.get_ancestors())) + self.assertEqual( + sorted([self.c1, self.c2, self.c3], key=attrgetter("name")), + sorted(self.c4.get_ancestors(), key=attrgetter("name")), + ) + + def test_component_get_ancestor_ids(self): + self.assertEqual([], self.c1.get_ancestor_ids()) + self.assertEqual([self.c1.id], self.c2.get_ancestor_ids()) + self.assertEqual([self.c1.id], self.c3.get_ancestor_ids()) + self.assertEqual( + sorted([self.c1.id, self.c2.id, self.c3.id]), sorted(self.c4.get_ancestor_ids()) + ) + + def test_component_get_descendants(self): + self.assertEqual( + sorted([self.c2, self.c3, self.c4], key=attrgetter("name")), + sorted(self.c1.get_descendants(), key=attrgetter("name")), + ) + self.assertEqual([self.c4], list(self.c2.get_descendants())) + self.assertEqual([self.c4], list(self.c3.get_descendants())) + self.assertEqual([], list(self.c4.get_descendants())) + + descendants = self.c2.get_descendants(set_direct_parent=True) + self.assertEqual([self.c4], list(descendants)) + self.assertEqual(self.c2, list(descendants)[0].direct_parent) + + def test_component_get_descendant_ids(self): + self.assertEqual( + sorted([self.c2.id, self.c3.id, self.c4.id]), sorted(self.c1.get_descendant_ids()) + ) + self.assertEqual([self.c4.id], self.c2.get_descendant_ids()) + self.assertEqual([self.c4.id], self.c3.get_descendant_ids()) + self.assertEqual([], self.c4.get_descendant_ids()) + + def test_component_get_related_ancestors(self): + self.assertEqual([], list(self.c1.get_related_ancestors())) + self.assertEqual([self.sub_1_2], list(self.c2.get_related_ancestors())) + self.assertEqual([self.sub_1_3], list(self.c3.get_related_ancestors())) + self.assertEqual( + sorted([self.sub_1_3, self.sub_1_2, self.sub_2_4, self.sub_3_4], key=attrgetter("id")), + sorted(self.c4.get_related_ancestors(), key=attrgetter("id")), + ) + + def test_component_get_related_descendants(self): + self.assertEqual( + sorted([self.sub_1_3, self.sub_1_2, self.sub_2_4, self.sub_3_4], key=attrgetter("id")), + sorted(self.c1.get_related_descendants(), key=attrgetter("id")), + ) + self.assertEqual([self.sub_2_4], list(self.c2.get_related_descendants())) + self.assertEqual([self.sub_3_4], list(self.c3.get_related_descendants())) + self.assertEqual([], list(self.c4.get_related_descendants())) + + def test_component_is_ancestor_of(self): + self.assertFalse(self.c1.is_ancestor_of(self.c1)) + self.assertTrue(self.c1.is_ancestor_of(self.c2)) + self.assertTrue(self.c1.is_ancestor_of(self.c3)) + self.assertTrue(self.c1.is_ancestor_of(self.c4)) + + self.assertFalse(self.c2.is_ancestor_of(self.c1)) + self.assertFalse(self.c2.is_ancestor_of(self.c2)) + self.assertFalse(self.c2.is_ancestor_of(self.c3)) + self.assertTrue(self.c2.is_ancestor_of(self.c4)) + + self.assertFalse(self.c4.is_ancestor_of(self.c1)) + self.assertFalse(self.c4.is_ancestor_of(self.c2)) + self.assertFalse(self.c4.is_ancestor_of(self.c3)) + self.assertFalse(self.c4.is_ancestor_of(self.c4)) + + def test_component_is_descendant_of(self): + self.assertFalse(self.c1.is_descendant_of(self.c1)) + self.assertFalse(self.c1.is_descendant_of(self.c2)) + self.assertFalse(self.c1.is_descendant_of(self.c3)) + self.assertFalse(self.c1.is_descendant_of(self.c4)) + + self.assertTrue(self.c2.is_descendant_of(self.c1)) + self.assertFalse(self.c2.is_descendant_of(self.c2)) + self.assertFalse(self.c2.is_descendant_of(self.c3)) + self.assertFalse(self.c2.is_descendant_of(self.c4)) + + self.assertTrue(self.c4.is_descendant_of(self.c1)) + self.assertTrue(self.c4.is_descendant_of(self.c2)) + self.assertTrue(self.c4.is_descendant_of(self.c3)) + self.assertFalse(self.c4.is_descendant_of(self.c4)) + + def test_component_type_unique_filters_for(self): + filters = self.component_type.unique_filters_for(self.other_dataspace) + expected = {"label": self.component_type.label, "dataspace": self.other_dataspace} + self.assertEqual(expected, filters) + + def test_component_catalog_models_get_identifier_fields(self): + inputs = [ + (ComponentType, ["label"]), + (ComponentStatus, ["label"]), + (Component, ["name", "version"]), + (Subcomponent, ["parent", "child"]), + (ComponentAssignedLicense, ["component", "license"]), + (ComponentKeyword, ["label"]), + ] + for model_class, expected in inputs: + self.assertEqual(expected, model_class.get_identifier_fields()) + + def test_component_model_get_absolute_url(self): + c = Component(name="c1", version="1.0", dataspace=self.dataspace) + self.assertEqual("/components/nexB/c1/1.0/", c.get_absolute_url()) + + def test_component_model_get_absolute_url_with_space(self): + c = Component(name="c1 c1", dataspace=self.dataspace, version="1.0") + self.assertEqual("/components/nexB/c1+c1/1.0/", c.get_absolute_url()) + + def test_component_model_get_absolute_url_with_slash_in_name(self): + c = Component(name="c1/c1", dataspace=self.dataspace, version="1.0") + self.assertEqual("/components/nexB/c1%252Fc1/1.0/", c.get_absolute_url()) + + def test_component_model_get_absolute_url_with_multiline_name_ticket(self): + multilines_name = ( + "* zstream.h - C++ interface to the 'zlib' general purpose" + " compression library\r\n * $Id: zstream.h" + ) + c = Component(name=multilines_name, dataspace=self.dataspace, version="1.0") + expected = ( + "/components/nexB/%252A+zstream.h+-+C%252B%252B+" + "interface+to+the+%2527zlib%2527+general+purpose+compression" + "+library%250D%250A+%252A+%2524Id%253A+zstream.h/1.0/" + ) + self.assertEqual(expected, c.get_absolute_url()) + + def test_component_model_get_absolute_url_with_long_component_name_version(self): + c = Component( + name="*" * 600, + owner=self.owner, + dataspace=self.dataspace, + version="1" * 400, + ) + self.assertGreater(len(c.get_absolute_url()), 3000) + + def test_component_license_expression_linked(self): + expression = "{} AND {}".format(self.license1.key, self.license2.key) + + self.component1.license_expression = expression + self.component1.save() + expected = ( + 'license1 AND license2'.format( + self.license1.get_absolute_url(), self.license2.get_absolute_url() + ) + ) + self.assertEqual(expected, self.component1.get_license_expression_linked()) + + def test_component_get_license_expression_spdx_id(self): + self.license1.spdx_license_key = "SPDX-1" + self.license1.save() + + expression = "{} AND {}".format(self.license1.key, self.license2.key) + self.component1.license_expression = expression + self.component1.save() + expected = "SPDX-1 AND LicenseRef-dejacode-license2" + self.assertEqual(expected, self.component1.get_license_expression_spdx_id()) + + expression = "{} WITH {}".format(self.license1.key, self.license2.key) + self.component1.license_expression = expression + self.component1.save() + # WITH is replaced by AND for "LicenseRef-" exceptions + expected = "SPDX-1 AND LicenseRef-dejacode-license2" + self.assertEqual(expected, self.component1.get_license_expression_spdx_id()) + + self.license2.spdx_license_key = "SPDX-2" + self.license2.save() + self.component1 = Component.objects.get(pk=self.component1.pk) + # WITH is kept for exceptions in SPDX list + expected = "SPDX-1 WITH SPDX-2" + self.assertEqual(expected, self.component1.get_license_expression_spdx_id()) + + def test_get_license_expression_key_as_link_conflict(self): + # self.license1.key is contained in self.license2.key + self.license1.key = "w3c" + self.license2.key = "w3c-documentation" + self.license1.save() + self.license2.save() + + self.component1.license_expression = "{} AND {}".format( + self.license1.key, self.license2.key + ) + self.component1.save() + + expected = ( + f'w3c AND' + f' w3c-documentation' + ) + self.assertEqual(expected, self.component1.get_license_expression_linked()) + + def test_component_model_save_license_expression_handle_assigned_licenses(self): + expression = "{} AND {}".format(self.license1.key, self.license2.key) + + c1 = Component.objects.create( + name="c 1", + dataspace=self.dataspace, + license_expression=expression, + ) + + self.assertEqual(2, c1.licenses.count()) + self.assertIn(self.license1, c1.licenses.all()) + self.assertIn(self.license2, c1.licenses.all()) + + c1.license_expression = self.license1.key + c1.save() + + self.assertEqual(1, c1.licenses.count()) + self.assertIn(self.license1, c1.licenses.all()) + self.assertNotIn(self.license2, c1.licenses.all()) + + c1.license_expression = "" + c1.save() + self.assertEqual(0, c1.licenses.count()) + + def test_subcomponent_model_license_expression_handle_assigned_licenses(self): + self.assertFalse(self.sub_1_2.license_expression) + self.assertFalse(self.sub_1_2.licenses.exists()) + + self.sub_1_2.license_expression = self.license1.key + self.sub_1_2.save() + + # license1 is assigned from the expression, and license is deleted as not in expression + self.assertEqual(1, self.sub_1_2.licenses.count()) + self.assertIn(self.license1, self.sub_1_2.licenses.all()) + + self.sub_1_2.license_expression = "" + self.sub_1_2.save() + self.assertFalse(self.sub_1_2.licenses.exists()) + + def test_component_status_model_set_default_on_addition(self): + # The default_on_addition can only be True on 1 ComponentStatus per Dataspace + self.assertFalse(ComponentStatus.objects.get(pk=self.status1.pk).default_on_addition) + self.assertTrue(ComponentStatus.objects.get(pk=self.status2.pk).default_on_addition) + + self.status1.default_on_addition = True + self.status1.save() + self.assertTrue(ComponentStatus.objects.get(pk=self.status1.pk).default_on_addition) + self.assertFalse(ComponentStatus.objects.get(pk=self.status2.pk).default_on_addition) + + def test_component_model_default_status_on_component_addition(self): + self.status1.default_on_addition = True + self.status1.save() + + # No status given at creation time, the default is set + c1 = Component.objects.create(owner=self.owner, name="C1", dataspace=self.dataspace) + self.assertEqual(self.status1, c1.configuration_status) + + # A status is given at creation time, no default is set + c2 = Component.objects.create( + configuration_status=self.status2, name="C2", dataspace=self.dataspace + ) + self.assertEqual(self.status2, c2.configuration_status) + + def test_component_model_attribution_required_property(self): + self.component1.license_expression = self.license1.key + self.component1.save() + + self.assertFalse(self.component1.attribution_required) + + tag1 = LicenseTag.objects.create( + label="Tag1", text="Text for tag1", dataspace=self.dataspace + ) + assigned_tag1 = LicenseAssignedTag.objects.create( + license=self.license1, license_tag=tag1, value=True, dataspace=self.dataspace + ) + + assigned_tag1.license_tag.attribution_required = True + assigned_tag1.license_tag.save() + assigned_tag1.value = False + assigned_tag1.save() + self.assertFalse(self.component1.attribution_required) + + assigned_tag1.value = True + assigned_tag1.save() + self.assertTrue(self.component1.attribution_required) + + def test_component_model_redistribution_required_property(self): + self.component1.license_expression = self.license1.key + self.component1.save() + + self.assertFalse(self.component1.redistribution_required) + + tag1 = LicenseTag.objects.create( + label="Tag1", text="Text for tag1", dataspace=self.dataspace + ) + assigned_tag1 = LicenseAssignedTag.objects.create( + license=self.license1, license_tag=tag1, value=True, dataspace=self.dataspace + ) + + assigned_tag1.license_tag.redistribution_required = True + assigned_tag1.license_tag.save() + assigned_tag1.value = False + assigned_tag1.save() + self.assertFalse(self.component1.redistribution_required) + + assigned_tag1.value = True + assigned_tag1.save() + self.assertTrue(self.component1.redistribution_required) + + def test_component_model_change_tracking_required_property(self): + self.component1.license_expression = self.license1.key + self.component1.save() + + self.assertFalse(self.component1.change_tracking_required) + + tag1 = LicenseTag.objects.create( + label="Tag1", text="Text for tag1", dataspace=self.dataspace + ) + assigned_tag1 = LicenseAssignedTag.objects.create( + license=self.license1, license_tag=tag1, value=True, dataspace=self.dataspace + ) + + assigned_tag1.license_tag.change_tracking_required = True + assigned_tag1.license_tag.save() + assigned_tag1.value = False + assigned_tag1.save() + self.assertFalse(self.component1.change_tracking_required) + + assigned_tag1.value = True + assigned_tag1.save() + self.assertTrue(self.component1.change_tracking_required) + + def test_get_parents(self): + self.assertEqual([self.c2, self.c3], list(self.c4.get_parents())) + + def test_get_children(self): + self.assertEqual([self.c2, self.c3], list(self.c1.get_children())) + + def test_component_deletion(self): + # We have a Subcomponent relation between c1 and c2 + self.assertTrue(Subcomponent.objects.filter(parent=self.c1, child=self.c2)) + # Deleting c1 should delete the c1 object and the Subcomponent, but c2 + # is not impacted + self.c1.delete() + self.assertFalse(Subcomponent.objects.filter(parent=self.c1, child=self.c2)) + self.assertFalse(Component.objects.filter(id=self.c1.id)) + self.assertTrue(Component.objects.filter(id=self.c2.id)) + + # Assigning a License on c2 + self.c2.license_expression = self.license1.key + self.c2.save() + self.assertTrue(self.c2.licenses.exists()) + # Deleting the license is not possible, the field is protected. + with self.assertRaises(ProtectedError): + self.license1.delete() + + # Although deleting the Component is ok, it will delete the relation in + # cascade but the license is not impacted + self.c2.delete() + self.assertFalse(Component.objects.filter(id=self.c2.id)) + self.assertTrue(License.objects.filter(id=self.license1.id)) + + def test_component_compute_completion_level(self): + component = Component.objects.create(name="Component", dataspace=self.dataspace) + + # Minimum possible for a Component + self.assertEqual(0, component.compute_completion_level()) + + component.notice_text = "a" + component.copyright = "a" + component.description = "a" + component.homepage_url = "a" + component.notice_filename = "a" + component.notice_url = "a" + component.bug_tracking_url = "a" + component.code_view_url = "a" + component.primary_language = "a" + component.release_date = "2013-01-01" + component.vcs_url = "a" + component.owner = self.owner + component.type = self.component_type + component.version = "a" + component.keywords = ["Keyword"] + component.save() + + # Maximum with no m2m + self.assertEqual(85, component.compute_completion_level()) + + component.license_expression = self.license1.key + + component.save() + package = Package.objects.create(filename="package", dataspace=self.dataspace) + ComponentAssignedPackage.objects.create( + component=component, package=package, dataspace=self.dataspace + ) + + # Including m2m + self.assertEqual(100, component.compute_completion_level()) + + def test_component_update_completion_level_method(self): + self.assertFalse(self.component1.completion_level) + self.component1.update_completion_level() + self.component1.refresh_from_db() + self.assertTrue(self.component1.completion_level) + + def test_component_update_completion_level_after_copy(self): + # Making sure the weight of m2m is included + self.component1.license_expression = self.license1.key + self.component1.save() + copied_component = copy_object(self.component1, self.other_dataspace, self.user) + self.assertEqual(15, copied_component.completion_level) + + def test_component_update_completion_level_after_update(self): + copied_component = copy_object(self.component1, self.other_dataspace, self.user) + self.assertEqual(8, copied_component.completion_level) + + self.component1.description = "desc" + self.component1.notice_url = "http://url.com" + self.component1.license_expression = self.license1.key + self.component1.save() + # Let's copy again with update + copied_component = copy_object( + self.component1, self.other_dataspace, self.user, update=True + ) + self.assertEqual(29, copied_component.completion_level) + + def test_component_update_completion_level_after_save_in_ui(self): + self.client.login(username="nexb_user", password="secret") + url = reverse("admin:component_catalog_component_add") + data = { + "name": "New Component", + "version": "1.0", + "curation_level": 0, + "copyright": "copyright", + "related_children-INITIAL_FORMS": 0, + "related_children-TOTAL_FORMS": 0, + "componentassignedpackage_set-TOTAL_FORMS": 0, + "componentassignedpackage_set-INITIAL_FORMS": 0, + "dje-externalreference-content_type-object_id-TOTAL_FORMS": 0, + "dje-externalreference-content_type-object_id-INITIAL_FORMS": 0, + } + self.client.post(url, data) + added_component = Component.objects.get( + name="New Component", version="1.0", dataspace=self.user.dataspace + ) + self.assertEqual(9, added_component.completion_level) + + def test_component_update_completion_level_after_import(self): + # A license is given to ensure that the m2m weight is included. + formset_data = { + "form-TOTAL_FORMS": "1", + "form-INITIAL_FORMS": "0", + "form-0-name": "Django", + "form-0-version": "1.4", + "form-0-curation_level": 0, + "form-0-type": self.component_type.id, + "form-0-license_expression": self.license1.key, + } + importer = ComponentImporter(self.user, formset_data=formset_data) + importer.save_all() + component = Component.objects.get( + name="Django", version="1.4", dataspace=self.user.dataspace + ) + self.assertEqual(12, component.completion_level) + + def test_component_update_completion_level_after_package_import(self): + self.assertEqual(0, self.component1.componentassignedpackage_set.count()) + self.assertEqual(0, self.component1.completion_level) + + formset_data = { + "form-TOTAL_FORMS": "1", + "form-INITIAL_FORMS": "0", + "form-0-filename": "Django-2.0.zip", + "form-0-component": "{}:{}".format(self.component1.name, self.component1.version), + } + + importer = PackageImporter(self.user, formset_data=formset_data) + importer.save_all() + + package1 = Package.objects.get(filename="Django-2.0.zip") + self.assertEqual(1, self.component1.componentassignedpackage_set.count()) + self.assertEqual(1, package1.componentassignedpackage_set.count()) + self.component1.refresh_from_db() + self.assertEqual(15, self.component1.completion_level) + + def test_component_update_completion_level_after_mass_update(self): + self.client.login(username="nexb_user", password="secret") + self.assertEqual(0, self.component1.completion_level) + url = reverse("admin:component_catalog_component_changelist") + data = { + "_selected_action": [self.component1.id], + "copyright": "Copyright", + "chk_id_copyright": "on", + "description": "Description", + "chk_id_description": "on", + "select_across": "False", + "action": "mass_update", + "apply": "Update records", + } + self.client.post(url, data) + self.component1.refresh_from_db() + self.assertEqual(22, self.component1.completion_level) + + def test_component_model_has_license_choices_when_no_license_expression(self): + self.component1.license_expression = "" + self.component1.save() + self.assertFalse(self.component1.has_license_choices) + + def test_component_model_license_choices_expression_and_has_license_choices(self): + self.component1.license_expression = self.license1.key + self.component1.save() + self.assertEqual( + self.component1.license_expression, self.component1.license_choices_expression + ) + self.assertFalse(self.component1.has_license_choices) + + LicenseChoice.objects.create( + from_expression=self.license1.key, + to_expression=self.license2.key, + dataspace=self.dataspace, + ) + # Since it's a cached_property + self.assertEqual( + self.component1.license_expression, self.component1.license_choices_expression + ) + # Force a refresh of the instance + self.component1 = Component.objects.get(pk=self.component1.pk) + self.assertEqual(self.license2.key, self.component1.license_choices_expression) + self.assertTrue(self.component1.has_license_choices) + + def test_mass_update_m2m_fields_scope_to_dataspace(self): + # The limitation of the m2ms is done in dje.forms.DejacodeMassUpdateForm + self.client.login(username="nexb_user", password="secret") + + keyword1 = ComponentKeyword.objects.create(label="Keyword1", dataspace=self.dataspace) + + other_keyword = ComponentKeyword.objects.create( + label="OtherKeyword", dataspace=self.other_dataspace + ) + + data = { + "_selected_action": [self.component1.pk], + "action": "mass_update", + "select_across": 0, + } + + url = reverse("admin:component_catalog_component_changelist") + response = self.client.post(url, data) + + self.assertContains(response, keyword1.label) + self.assertNotContains(response, other_keyword.label) + + def test_mass_update_component_keywords(self): + self.client.login(username="nexb_user", password="secret") + + keyword1 = ComponentKeyword.objects.create(label="Keyword1", dataspace=self.dataspace) + keyword2 = ComponentKeyword.objects.create(label="Keyword2", dataspace=self.dataspace) + + data = { + "_selected_action": [self.c1.pk, self.c2.pk], + "action": "mass_update", + "select_across": 0, + "apply": "Update records", + "chk_id_keywords": "on", + "keywords": f"{keyword1.label}, {keyword2.label}", + } + + url = reverse("admin:component_catalog_component_changelist") + response = self.client.post(url, data, follow=True) + self.assertContains(response, "Updated 2 records") + + self.c1.refresh_from_db() + self.c2.refresh_from_db() + self.assertEqual([keyword1.label, keyword2.label], self.c1.keywords) + self.assertEqual([keyword1.label, keyword2.label], self.c2.keywords) + + def test_mass_update_component_owner(self): + self.client.login(username="nexb_user", password="secret") + + new_owner = Owner.objects.create(name="new owner", dataspace=self.dataspace) + + data = { + "_selected_action": [self.component1.pk, self.c2.pk], + "action": "mass_update", + "select_across": 0, + "apply": "Update records", + "chk_id_owner": "on", + "owner": new_owner.id, + } + + url = reverse("admin:component_catalog_component_changelist") + response = self.client.post(url, data, follow=True) + self.assertContains(response, "Updated 2 records") + self.component1.refresh_from_db() + self.c2.refresh_from_db() + self.assertEqual(self.component1.owner, new_owner) + self.assertEqual(self.c2.owner, new_owner) + + # Check the scoping + data["owner"] = self.other_owner.id + response = self.client.post(url, data) + expected = { + "owner": ["Select a valid choice. That choice is not one of the available choices."] + } + self.assertEqual(expected, response.context["adminform"].form.errors) + + def test_mass_update_component_license_expression(self): + self.client.login(username="nexb_user", password="secret") + url = reverse("admin:component_catalog_component_changelist") + self.assertFalse(self.component1.licenses.exists()) + + data = { + "_selected_action": [self.component1.pk, self.c2.pk], + "action": "mass_update", + "select_across": 0, + } + response = self.client.post(url, data) + self.assertContains(response, "awesomplete-1.1.5.css") + self.assertContains(response, "awesomplete-1.1.5.min.js") + self.assertContains(response, "license_expression_builder.js") + expected = [("L1 (license1)", "license1"), ("L2 (license2)", "license2")] + self.assertEqual(expected, response.context["client_data"]["license_data"]) + + data.update( + { + "apply": "Update records", + "chk_id_license_expression": "on", + "license_expression": "wrong", + } + ) + + response = self.client.post(url, data) + expected = {"license_expression": ["Unknown license key(s): wrong"]} + self.assertEqual(expected, response.context["adminform"].form.errors) + expected = '

      Please correct the error below.

      ' + self.assertContains(response, expected) + self.assertContains(response, "
    • Unknown license key(s): wrong
    • ", html=True) + # Make sure the enabler checkbox is checked + expected = ( + '' + ) + self.assertContains(response, expected, html=True) + + data["license_expression"] = "{} OR {}".format(self.license1.key, self.license2.key) + response = self.client.post(url, data, follow=True) + self.assertContains(response, "Updated 2 records") + self.assertEqual(2, self.component1.licenses.count()) + self.assertIn(self.license1, self.component1.licenses.all()) + self.assertIn(self.license2, self.component1.licenses.all()) + self.assertEqual(2, self.c2.licenses.count()) + self.assertIn(self.license1, self.c2.licenses.all()) + self.assertIn(self.license2, self.c2.licenses.all()) + + def test_mass_update_component_protected_fields(self): + self.client.login(username="admin_user", password="secret") + url = reverse("admin:component_catalog_component_changelist") + self.admin_user = add_perm(self.admin_user, "change_component") + + data = { + "_selected_action": [self.component1.pk], + "action": "mass_update", + "select_across": 0, + } + response = self.client.post(url, data) + self.assertNotContains(response, "usage_policy") + + self.admin_user = add_perm(self.admin_user, "change_usage_policy_on_component") + response = self.client.post(url, data) + self.assertContains(response, "usage_policy") + + def test_mass_update_packages(self): + self.client.login(username="nexb_user", password="secret") + + package1 = Package.objects.create(filename="a", dataspace=self.dataspace) + package2 = Package.objects.create(filename="b", dataspace=self.dataspace) + + data = { + "_selected_action": [package1.pk, package2.pk], + "action": "mass_update", + "select_across": 0, + "apply": "Update records", + "chk_id_notes": "on", + "notes": "Notes", + } + + url = reverse("admin:component_catalog_package_changelist") + response = self.client.post(url, data, follow=True) + self.assertContains(response, "Updated 2 records") + + package1.refresh_from_db() + package2.refresh_from_db() + self.assertEqual("Notes", package1.notes) + self.assertEqual("Notes", package2.notes) + + # Break the purl on a Package without a filename + package3 = Package.objects.create(type="type", name="name", dataspace=self.dataspace) + data["_selected_action"] = [package3.pk] + data["chk_id_name"] = "on" + data["name"] = "" + response = self.client.post(url, data, follow=True) + self.assertContains(response, "1 error(s): package_url or filename required") + + def test_mass_update_package_keywords(self): + self.client.login(username="nexb_user", password="secret") + + package1 = Package.objects.create(filename="a", dataspace=self.dataspace) + package2 = Package.objects.create(filename="b", dataspace=self.dataspace) + keyword1 = ComponentKeyword.objects.create(label="Keyword1", dataspace=self.dataspace) + keyword2 = ComponentKeyword.objects.create(label="Keyword2", dataspace=self.dataspace) + + data = { + "_selected_action": [package1.pk, package2.pk], + "action": "mass_update", + "select_across": 0, + "apply": "Update records", + "chk_id_keywords": "on", + "keywords": f"{keyword1.label}, {keyword2.label}", + } + + url = reverse("admin:component_catalog_package_changelist") + response = self.client.post(url, data, follow=True) + self.assertContains(response, "Updated 2 records") + + package1.refresh_from_db() + package2.refresh_from_db() + self.assertEqual([keyword1.label, keyword2.label], package1.keywords) + self.assertEqual([keyword1.label, keyword2.label], package2.keywords) + + def test_subcomponent_str_method(self): + self.sub_1_2.purpose = "Core" + self.sub_1_2.save() + self.assertEqual("Core: c2", str(self.sub_1_2)) + + def test_subcomponent_has_same_license_expression_as_component(self): + self.client.login(username="nexb_user", password="secret") + + self.sub_1_2.license_expression = self.license1.key + self.sub_1_2.save() + self.sub_1_2.child.license_expression = self.license1.key + self.sub_1_2.child.save() + + self.assertEqual(list(self.sub_1_2.licenses.all()), list(self.sub_1_2.child.licenses.all())) + self.assertTrue( + Licensing().is_equivalent( + self.sub_1_2.license_expression, self.sub_1_2.child.license_expression + ) + ) + + self.sub_1_2.license_expression = "" + self.sub_1_2.save() + self.assertFalse( + Licensing().is_equivalent( + self.sub_1_2.license_expression, self.sub_1_2.child.license_expression + ) + ) + + # Special case: 'oracle-bcl-javaee' contains 'or' + self.sub_1_2.license_expression = "oracle-bcl-javaee" + self.sub_1_2.save() + self.assertFalse( + Licensing().is_equivalent( + self.sub_1_2.license_expression, self.sub_1_2.child.license_expression + ) + ) + + def test_component_model_primary_license_property(self): + c = Component(license_expression="") + self.assertIsNone(self.component1.primary_license) + + c = Component(license_expression="{} AND {}".format(self.license2.key, self.license1.key)) + self.assertEqual(self.license2.key, c.primary_license) + + c = Component(license_expression="({} OR {})".format(self.license1.key, self.license2.key)) + self.assertEqual(self.license1.key, c.primary_license) + + c = Component(license_expression=self.license2.key) + self.assertEqual(self.license2.key, c.primary_license) + + def test_component_get_policy_from_primary_license(self): + from policy.models import AssociatedPolicy + from policy.models import UsagePolicy + + # Invalid expression, non-existing license + self.component1.license_expression = "invalid" + self.component1.save() + self.assertEqual("invalid", self.component1.primary_license) + self.assertIsNone(self.component1.get_policy_from_primary_license()) + + self.component1.license_expression = "" + self.component1.save() + self.assertFalse(self.component1.licenses.exists()) + self.assertIsNone(self.component1.get_policy_from_primary_license()) + + self.component1.license_expression = "{} AND {}".format( + self.license1.key, self.license2.key + ) + self.component1.save() + self.component1 = Component.objects.get(id=self.component1.id) + self.assertEqual(self.license1.key, self.component1.primary_license) + self.assertIsNone(self.component1.get_policy_from_primary_license()) + + license_policy = UsagePolicy.objects.create( + label="LicensePolicy", + icon="icon", + content_type=ContentType.objects.get_for_model(License), + dataspace=self.dataspace, + ) + self.license1.usage_policy = license_policy + self.license1.save() + + component_policy = UsagePolicy.objects.create( + label="ComponentPolicy", + icon="icon", + content_type=ContentType.objects.get_for_model(Component), + dataspace=self.dataspace, + ) + + # No association yet + self.assertIsNone(self.component1.get_policy_from_primary_license()) + + AssociatedPolicy.objects.create( + from_policy=license_policy, + to_policy=component_policy, + dataspace=self.dataspace, + ) + self.assertEqual(component_policy, self.component1.get_policy_from_primary_license()) + self.assertEqual(component_policy, self.component1.policy_from_primary_license) + self.assertIsNone(self.component1.usage_policy) + + def test_component_create_save_set_usage_policy_from_license(self): + from policy.models import AssociatedPolicy + from policy.models import UsagePolicy + + self.client.login(username="nexb_user", password="secret") + + license_policy = UsagePolicy.objects.create( + label="LicensePolicy", + icon="icon", + content_type=ContentType.objects.get_for_model(License), + dataspace=self.dataspace, + ) + self.license1.usage_policy = license_policy + self.license1.save() + + component_policy = UsagePolicy.objects.create( + label="ComponentPolicy", + icon="icon", + content_type=ContentType.objects.get_for_model(Component), + dataspace=self.dataspace, + ) + AssociatedPolicy.objects.create( + from_policy=license_policy, + to_policy=component_policy, + dataspace=self.dataspace, + ) + + component1 = Component.objects.create( + name="component1", + dataspace=self.dataspace, + license_expression=self.license1.key, + ) + self.assertIsNone(component1.usage_policy) + + self.dataspace.set_usage_policy_on_new_component_from_licenses = True + self.dataspace.save() + component2 = Component.objects.create( + name="component2", + dataspace=self.dataspace, + license_expression=self.license1.key, + ) + self.assertEqual(component_policy, component2.usage_policy) + + # Edition + component1.save() + self.assertEqual(component_policy, component1.usage_policy) + + # API + data = { + "name": "component3", + "license_expression": self.license1.key, + } + response = self.client.post(reverse("api_v2:component-list"), data) + self.assertEqual(201, response.status_code) + component3 = Component.objects.latest("id") + self.assertEqual(component_policy, component3.usage_policy) + + # API Edit + component4 = Component.objects.create( + name="component4", + dataspace=self.dataspace, + ) + component4_api_url = reverse("api_v2:component-detail", args=[component4.uuid]) + put_data = json.dumps( + { + "name": component4.name, + "license_expression": self.license1.key, + } + ) + response = self.client.put( + component4_api_url, data=put_data, content_type="application/json" + ) + self.assertEqual(200, response.status_code) + component4 = Component.objects.get(id=component4.id) + self.assertEqual(component_policy, component4.usage_policy) + + # Import + formset_data = { + "form-TOTAL_FORMS": "1", + "form-INITIAL_FORMS": "0", + "form-0-name": "component5", + "form-0-curation_level": 0, + "form-0-license_expression": self.license1.key, + } + importer = ComponentImporter(self.user, formset_data=formset_data) + self.assertTrue(importer.formset.is_valid()) + importer.save_all() + component5 = importer.results["added"][0] + self.assertEqual(component_policy, component5.usage_policy) + + def test_subcomponent_get_policy_from_child_component(self): + from policy.models import AssociatedPolicy + from policy.models import UsagePolicy + + self.assertIsNone(self.sub_1_2.get_policy_from_child_component()) + + component_policy = UsagePolicy.objects.create( + label="ComponentPolicy", + icon="icon", + content_type=ContentType.objects.get_for_model(Component), + dataspace=self.dataspace, + ) + subcomponent_policy = UsagePolicy.objects.create( + label="SubcomponentPolicy", + icon="icon", + content_type=ContentType.objects.get_for_model(Subcomponent), + dataspace=self.dataspace, + ) + + self.sub_1_2.child.usage_policy = component_policy + self.sub_1_2.child.save() + + # No association yet + self.assertIsNone(self.sub_1_2.get_policy_from_child_component()) + + AssociatedPolicy.objects.create( + from_policy=component_policy, + to_policy=subcomponent_policy, + dataspace=self.dataspace, + ) + self.assertEqual(subcomponent_policy, self.sub_1_2.get_policy_from_child_component()) + self.assertIsNone(self.sub_1_2.usage_policy) + + def test_component_catalog_models_get_exclude_candidates_fields(self): + input_data = ( + ( + Component, + [ + "reference_notes", + "usage_policy", + "version", + "owner", + "release_date", + "description", + "copyright", + "holder", + "homepage_url", + "vcs_url", + "code_view_url", + "bug_tracking_url", + "primary_language", + "admin_notes", + "notice_text", + "license_expression", + "configuration_status", + "type", + "approval_reference", + "guidance", + "is_active", + "curation_level", + "project", + "completion_level", + "is_license_notice", + "is_copyright_notice", + "is_notice_in_codebase", + "notice_filename", + "notice_url", + "dependencies", + "codescan_identifier", + "website_terms_of_use", + "ip_sensitivity_approved", + "affiliate_obligations", + "affiliate_obligation_triggers", + "concluded_license", + "keywords", + "legal_comments", + "sublicense_allowed", + "express_patent_grant", + "covenant_not_to_assert", + "indemnification", + "legal_reviewed", + "distribution_formats_allowed", + "acceptable_linkages", + "export_restrictions", + "approved_download_location", + "approved_community_interaction", + "cpe", + ], + ), + (ComponentType, ["notes"]), + (ComponentStatus, ["default_on_addition"]), + ( + Subcomponent, + [ + "extra_attribution_text", + "is_deployed", + "is_modified", + "license_expression", + "notes", + "purpose", + "package_paths", + "reference_notes", + "usage_policy", + ], + ), + ( + Package, + [ + "reference_notes", + "usage_policy", + "type", + "namespace", + "name", + "version", + "qualifiers", + "subpath", + "holder", + "keywords", + "cpe", + "homepage_url", + "vcs_url", + "code_view_url", + "bug_tracking_url", + "sha256", + "sha512", + "filename", + "download_url", + "sha1", + "md5", + "size", + "release_date", + "primary_language", + "description", + "project", + "notes", + "license_expression", + "copyright", + "notice_text", + "author", + "dependencies", + "repository_homepage_url", + "repository_download_url", + "api_data_url", + "declared_license", + "datasource_id", + "file_references", + "parties", + ], + ), + ) + + for model_class, expected in input_data: + results = [f.name for f in model_class().get_exclude_candidates_fields()] + self.assertEqual(sorted(expected), sorted(results)) + + def test_component_create_with_or_and_and_in_license_name_and_key(self): + or_license = License.objects.create( + key="orrible", + name="Orrible license", + short_name="orL3", + owner=self.owner, + dataspace=self.dataspace, + ) + and_license = License.objects.create( + key="anddistrophic", + name="Anddistrophic license", + short_name="andL4", + owner=self.owner, + dataspace=self.dataspace, + ) + + expression = "{} AND {} or {} with {}".format( + or_license.key, and_license.key, self.license1.key, self.license2.key + ) + + Component.objects.create( + name="c 1", + dataspace=self.dataspace, + license_expression=expression, + ) + + def test_component_save_license_expression_handles_assigned_licenses_with(self): + c1 = Component.objects.create( + name="c 1", + dataspace=self.dataspace, + license_expression=self.license1.key, + ) + self.assertEqual(1, c1.licenses.count()) + self.assertIn(self.license1, c1.licenses.all()) + + or_license = License.objects.create( + key="orrible", + name="Orrible license", + short_name="orL3", + owner=self.owner, + dataspace=self.dataspace, + ) + + c1.license_expression = or_license.key + c1.save() + + def test_license_expression_mixin_normalized_expression(self): + lem = LicenseExpressionMixin() + lem.license_expression = None + self.assertEqual(None, lem.normalized_expression) + + self.component1.license_expression = f"{self.license1.key} AND {self.license2.key}" + self.component1.save() + + normalized_expression = self.component1.normalized_expression + self.assertEqual("license1 AND license2", str(normalized_expression)) + self.assertEqual(license_expression.AND, type(normalized_expression)) + + def test_license_expression_mixin_get_license_expression_without_expression(self): + lem = LicenseExpressionMixin() + lem.license_expression = None + self.assertEqual(None, lem.get_license_expression()) + + def test_license_expression_mixin_get_license_expression_with_exception(self): + or_license = License.objects.create( + key="orrible", + name="Orrible license", + short_name="orL3", + owner=self.owner, + dataspace=self.dataspace, + ) + and_license = License.objects.create( + key="anddistrophic", + name="Anddistrophic license", + short_name="andL4", + owner=self.owner, + dataspace=self.dataspace, + ) + + expression = "{} AND {} or {} with {}".format( + or_license.key, and_license.key, self.license1.key, self.license2.key + ) + + comp = Component.objects.create( + name="c 1", + dataspace=self.dataspace, + license_expression=expression, + ) + + expected = "(orrible AND anddistrophic) OR (license1 WITH license2)" + self.assertEqual(expected, comp.get_license_expression()) + + expected = ( + "( AND " + ") OR " + "( WITH )" + ) + self.assertEqual( + expected, comp.get_license_expression(template="") + ) + + expected = ( + '(orrible AND ' + 'anddistrophic) OR ' + '(license1 WITH ' + 'license2)' + ) + self.assertEqual(expected, comp.get_license_expression(as_link=True)) + self.assertEqual( + comp.get_license_expression_linked(), comp.get_license_expression(as_link=True) + ) + + expected = ( + '(orL3 AND ' + 'andL4) OR ' + '(L1 WITH L2)' + ) + self.assertEqual(expected, comp.get_license_expression_attribution()) + + def test_license_expression_mixin_get_primary_license(self): + or_license = License.objects.create( + key="orrible", + name="Orrible license", + short_name="orL3", + owner=self.owner, + dataspace=self.dataspace, + ) + and_license = License.objects.create( + key="anddistrophic", + name="Anddistrophic license", + short_name="andL4", + owner=self.owner, + dataspace=self.dataspace, + ) + expression = f"{or_license.key} AND {and_license.key} with {self.license2.key}" + comp = Component.objects.create( + name="c 1", + dataspace=self.dataspace, + license_expression=expression, + ) + + self.assertEqual("orrible AND anddistrophic with license2", str(expression)) + self.assertEqual(or_license.key, comp._get_primary_license()) + + # With exception first + expression = f"{self.license1.key} WITH {self.license2.key} AND {and_license.key}" + self.assertEqual("license1 WITH license2 AND anddistrophic", str(expression)) + comp.license_expression = expression + comp.save() + # Primary license with exceptions are not supported + self.assertEqual(self.license1.key, comp._get_primary_license()) + + def test_component_model_package_property(self): + self.assertIsNone(self.component1.package) + + package = Package.objects.create(filename="package.zip", dataspace=self.dataspace) + ComponentAssignedPackage.objects.create( + component=self.component1, package=package, dataspace=self.dataspace + ) + self.component1 = Component.objects.get(pk=self.component1.pk) + self.assertEqual(package, self.component1.package) + + package2 = Package.objects.create(filename="package2.zip", dataspace=self.dataspace) + ComponentAssignedPackage.objects.create( + component=self.component1, package=package2, dataspace=self.dataspace + ) + self.component1 = Component.objects.get(pk=self.component1.pk) + self.assertIsNone(self.component1.package) + + def test_package_model_unique_constraint(self): + def create_package(dataspace=None, **kwargs): + return Package.objects.create(**kwargs, dataspace=dataspace or self.dataspace) + + def assert_unique(**kwargs): + """ + 1. Create a Package with provided values + 2. Assert it cannot be created twice with same values + 3. Make sure ti can be create with same values in another Dataspace + """ + create_package(**kwargs) + + with self.assertRaises(IntegrityError), transaction.atomic(): + create_package(**kwargs) + + create_package(**kwargs, dataspace=self.other_dataspace) + + filename = { + "filename": "setup.exe", + } + download_url = {"download_url": "https://domain.com/setup.exe"} + simple_purl = { + "type": "deb", + "name": "curl", + } + simple_purl2 = { + **simple_purl, + "type": "git", + } + complete_purl = { + "type": "deb", + "namespace": "debian", + "name": "curl", + "version": "7.50.3-1", + "qualifiers": "arch=i386", + "subpath": "googleapis/api/annotations", + } + + # 1a. filename, no download_url, no purl + assert_unique(**filename) + # 1b. same filename, purl + assert_unique(**filename, **simple_purl) + # 1c. same filename, another purl + assert_unique(**filename, **complete_purl) + # 2g. same filename, optional purl fields + assert_unique(**filename, version="1.0") + assert_unique(**filename, version="1.0", namespace="namespace") + + # 2a. download_url, no filename, no purl + with self.assertRaises(ValidationError), transaction.atomic(): + create_package(**download_url) + + # 2b. download_url, no filename, purl + assert_unique(**download_url, **simple_purl) + # 2c. same download_url, filename, same purl + assert_unique(**download_url, **filename, **simple_purl) + # 2d. same download_url, no filename, different purl + assert_unique(**download_url, **simple_purl2) + # 2e. download_url, filename, no purl + assert_unique(**download_url, **filename) + # 2f. download_url, filename, optional purl fields + assert_unique(**download_url, **filename, version="1.0") + assert_unique(**download_url, **filename, version="1.0", namespace="namespace") + + # 3a. simple purl, no download_url + assert_unique(**simple_purl) + # 3b. complete purl, no download_url + assert_unique(**complete_purl) + # 3b. complete purl, download_url + assert_unique(**complete_purl, **download_url) + + def test_package_model_create_from_data(self): + with self.assertRaises(ValidationError) as cm: + Package.create_from_data(user=self.user, data={}) + self.assertEqual("package_url or filename required", cm.exception.message) + + package_data = { + "not_available": True, + "filename": "filename.zip", + } + package = Package.create_from_data(user=self.user, data=package_data) + self.assertTrue(package.pk) + self.assertEqual(package_data["filename"], package.filename) + + package_data = { + "type": "pypi", + "name": "name", + "version": "1.0", + } + package = Package.create_from_data(user=self.user, data=package_data) + self.assertTrue(package.pk) + self.assertEqual("pkg:pypi/name@1.0", package.package_url) + + def test_package_model_create_from_data_validation(self): + package_data = { + "filename": "filename.zip", + "primary_language": "Python" * 100, + } + + with self.assertRaises(ValidationError) as cm: + Package.create_from_data(user=self.user, data=package_data, validate=True) + + expected = { + "primary_language": ["Ensure this value has at most 50 characters (it has 600)."] + } + self.assertEqual(expected, cm.exception.message_dict) + + with self.assertRaises(DataError) as cm: + Package.create_from_data(user=self.user, data=package_data) + expected = "value too long for type character varying(50)" + self.assertEqual(expected, str(cm.exception).strip()) + + def test_package_model_update_from_data(self): + package = Package.objects.create( + filename="package.zip", + name="name", + dataspace=self.dataspace, + ) + + updated_fields = package.update_from_data(self.user, data={}) + self.assertEqual([], updated_fields) + + new_data = { + "name": "new name", + "version": "1.0", + "unknown_field": "value", + } + updated_fields = package.update_from_data(self.user, data=new_data) + self.assertEqual(["version"], updated_fields) + package.refresh_from_db() + # Already has a value, not updated + self.assertEqual("name", package.name) + # Empty field, updated + self.assertEqual(new_data["version"], package.version) + + updated_fields = package.update_from_data(self.user, data=new_data, override=True) + self.assertEqual(["name"], updated_fields) + package.refresh_from_db() + self.assertEqual(new_data["name"], package.name) + + def test_package_model_get_url_methods(self): + package = Package( + filename="filename.zip", + uuid="dd0afd00-89bd-46d6-b1f0-57b553c44d32", + dataspace=self.dataspace, + ) + self.assertEqual( + "/packages/nexB/filename.zip/dd0afd00-89bd-46d6-b1f0-57b553c44d32/", + package.get_absolute_url(), + ) + self.assertEqual( + "/packages/nexB/filename.zip/dd0afd00-89bd-46d6-b1f0-57b553c44d32/change/", + package.get_change_url(), + ) + self.assertEqual( + "/packages/nexB/dd0afd00-89bd-46d6-b1f0-57b553c44d32/delete/", + package.get_delete_url(), + ) + + package = Package( + filename="", + type="pypi", + name="django", + version="1.0", + subpath="sub/path/", + uuid="dd0afd00-89bd-46d6-b1f0-57b553c44d32", + dataspace=self.dataspace, + ) + self.assertEqual( + "/packages/nexB/pypi/django@1.0/dd0afd00-89bd-46d6-b1f0-57b553c44d32/", + package.get_absolute_url(), + ) + self.assertEqual( + "/packages/nexB/pypi/django@1.0/dd0afd00-89bd-46d6-b1f0-57b553c44d32/change/", + package.get_change_url(), + ) + + def test_package_model_component_property(self): + package = Package.objects.create(filename="package.zip", dataspace=self.dataspace) + self.assertIsNone(package.component) + + ComponentAssignedPackage.objects.create( + component=self.component1, package=package, dataspace=self.dataspace + ) + package = Package.objects.get(pk=package.pk) + self.assertEqual(self.component1, package.component) + + ComponentAssignedPackage.objects.create( + component=self.c2, package=package, dataspace=self.dataspace + ) + package = Package.objects.get(pk=package.pk) + self.assertIsNone(package.component) + + def test_package_model_package_str_repr(self): + package = Package.objects.create( + filename="package.zip", + type="deb", + namespace="debian", + name="curl", + version="7.50.3-1", + qualifiers="arch=i386", + subpath="googleapis/api/annotations", + dataspace=self.dataspace, + ) + + # 1. Short Package URL + expected = "deb/debian/curl@7.50.3-1" + self.assertEqual(expected, str(package)) + + # 2. Full Package URL + expected = "pkg:deb/debian/curl@7.50.3-1?arch=i386#googleapis/api/annotations" + self.assertEqual(expected, str(package.package_url)) + + # 3. Filename + package.type = "" + package.save() + self.assertFalse(package.package_url) + self.assertEqual("package.zip", str(package)) + + def test_package_model_package_save(self): + with self.assertRaises(ValidationError): + Package.objects.create(dataspace=self.dataspace) + + self.assertTrue(Package.objects.create(filename="a", dataspace=self.dataspace)) + self.assertTrue(Package.objects.create(type="a", name="a", dataspace=self.dataspace)) + + def test_package_model_package_url_properties(self): + package = Package.objects.create( + filename="package.zip", + type="deb", + namespace="debian", + name="curl", + version="7.50.3-1", + qualifiers="arch=i386", + subpath="googleapis/api/annotations", + dataspace=self.dataspace, + ) + + expected = "pkg:deb/debian/curl@7.50.3-1?arch=i386#googleapis/api/annotations" + self.assertEqual(expected, package.package_url) + + expected = "deb/debian/curl@7.50.3-1" + self.assertEqual(expected, package.short_package_url) + + expected = "deb_debian_curl_7.50.3-1" + self.assertEqual(expected, package.package_url_filename) + + def test_package_model_set_package_url(self): + package = Package(dataspace=self.dataspace, filename="p1.zip") + + package_url = "pkg:deb/debian/curl@7.50.3-1?arch=i386#googleapis/api/annotations" + package.set_package_url(package_url) + self.assertEqual("deb", package.type) + self.assertEqual("debian", package.namespace) + self.assertEqual("curl", package.name) + self.assertEqual("7.50.3-1", package.version) + self.assertEqual("arch=i386", package.qualifiers) + self.assertEqual("googleapis/api/annotations", package.subpath) + package.save() + + package_url = "pkg:deb/curl" + package.set_package_url(package_url) + self.assertEqual("deb", package.type) + self.assertEqual("", package.namespace) + self.assertEqual("curl", package.name) + self.assertEqual("", package.version) + self.assertEqual("", package.qualifiers) + self.assertEqual("", package.subpath) + package.save() + + package_url = f'pkg:maven/mysql/mysql-connector-java@%40MYSQL_CJ_.{"version"*100}' + with self.assertRaises(ValidationError) as e: + package.set_package_url(package_url) + self.assertEqual('Value too long for field "version".', e.exception.message) + + def test_package_model_update_package_url(self): + package = Package.objects.create(dataspace=self.dataspace, filename="p1.zip") + self.assertEqual("", package.download_url) + + package_url = package.update_package_url(self.user) + self.assertIsNone(package_url) + self.assertEqual("", package.package_url) + + package.download_url = "http://repo1.maven.org/maven2/jdbm/jdbm/0.20-dev/" + package.save() + self.assertTrue(package.last_modified_date) + initial_modified_date = package.last_modified_date + + package_url = package.update_package_url(self.user, save=True) + purl = "pkg:maven/jdbm/jdbm@0.20-dev" + self.assertEqual(purl, str(package_url)) + package.refresh_from_db() + self.assertEqual(purl, package.package_url) + self.assertNotEqual(initial_modified_date, package.last_modified_date) + self.assertFalse(History.objects.get_for_object(package).exists()) + + package_url = package.update_package_url(self.user, save=True) + self.assertIsNone(package_url) + + package_url = package.update_package_url(self.user, save=True, overwrite=True) + self.assertIsNone(package_url) + + package.download_url = "http://repo1.maven.org/maven2/jdbm/jdbm/1.0/" + package.save() + package_url = package.update_package_url(self.user, save=True, overwrite=True, history=True) + purl = "pkg:maven/jdbm/jdbm@1.0" + self.assertEqual(purl, str(package_url)) + package.refresh_from_db() + self.assertEqual(purl, package.package_url) + + history_entry = History.objects.get_for_object(package).get() + expected_messages = "Set Package URL from Download URL" + self.assertEqual(expected_messages, history_entry.change_message) + + def test_package_model_as_about(self): + package_purl_only = Package.objects.create( + type="type", name="name", dataspace=self.dataspace + ) + + expected = {"about_resource": ".", "name": "name", "package_url": "pkg:type/name"} + self.assertEqual(expected, package_purl_only.as_about()) + expected = "about_resource: .\nname: name\npackage_url: pkg:type/name\n" + self.assertEqual(expected, package_purl_only.as_about_yaml()) + self.assertEqual("type_name.ABOUT", package_purl_only.about_file_name) + + package = Package.objects.create( + filename="package.zip", + download_url="http://domain.com/package.zip", + copyright="copyright on package", + dataspace=self.dataspace, + ) + + # No Components assigned + expected = { + "about_resource": "package.zip", + "copyright": "copyright on package", + "download_url": "http://domain.com/package.zip", + "notice_file": "package.zip.NOTICE", + } + + extra = {"notice_file": "package.zip.NOTICE"} + self.assertEqual(expected, package.as_about(extra)) + self.assertEqual("package.zip.ABOUT", package.about_file_name) + + # Assigning 1 Component + self.component1.copyright = "copyright on component" + self.component1.save() + ComponentAssignedPackage.objects.create( + component=self.component1, package=package, dataspace=self.dataspace + ) + package = Package.objects.get(pk=package.pk) + + expected = { + "about_resource": "package.zip", + "copyright": "copyright on package", + "name": "a", + "version": "1.0", + "download_url": "http://domain.com/package.zip", + "owner": "Owner", + } + + self.assertEqual(expected, package.as_about()) + + expected_yaml = ( + "about_resource: package.zip\n" + "name: a\n" + "version: '1.0'\n" + "download_url: http://domain.com/package.zip\n" + "copyright: copyright on package\n" + "owner: Owner\n" + ) + self.assertEqual(expected_yaml, package.as_about_yaml()) + + # No copyright on Package, taken from Component + package.copyright = "" + package.save() + expected["copyright"] = "copyright on component" + self.assertEqual(expected, package.as_about()) + + # More than 1 Component associated + ComponentAssignedPackage.objects.create( + component=self.c2, package=package, dataspace=self.dataspace + ) + package = Package.objects.get(pk=package.pk) + + expected = { + "about_resource": "package.zip", + "download_url": "http://domain.com/package.zip", + } + self.assertEqual(expected, package.as_about()) + + package_with_purl = Package.objects.create( + filename="package.zip", + type="deb", + namespace="debian", + name="curl", + version="7.50.3-1", + qualifiers="arch=i386", + subpath="googleapis/api/annotations", + dataspace=self.dataspace, + ) + expected = { + "about_resource": "package.zip", + "name": "curl", + "package_url": "pkg:deb/debian/curl@7.50.3-1?arch=i386#googleapis/api/annotations", + "version": "7.50.3-1", + } + self.assertEqual(expected, package_with_purl.as_about()) + + def test_package_model_about_file_and_notice_file_filename(self): + p1 = Package.objects.create( + filename="package.zip", + dataspace=self.dataspace, + ) + self.assertEqual("package.zip.ABOUT", p1.about_file_name) + self.assertEqual("package.zip.NOTICE", p1.notice_file_name) + + p2 = Package.objects.create( + type="deb", + name="name", + version="1.0 beta", + dataspace=self.dataspace, + ) + self.assertEqual("deb_name_1.020beta.ABOUT", p2.about_file_name) + self.assertEqual("deb_name_1.020beta.NOTICE", p2.notice_file_name) + + def test_package_model_get_about_files(self): + # Using a CRLF (windows) line endings to ensure it's converted to LF (unix) in the output + self.license1.full_text = "line1\r\nline2" + self.license1.save() + + package = Package.objects.create( + filename="package.zip", + download_url="htp://domain.com/package.zip", + copyright="copyright on package", + notice_text="Notice\r\nText", + license_expression=f"{self.license1.key} AND {self.license2.key}", + dataspace=self.dataspace, + ) + + expected_yaml = ( + "about_resource: package.zip\n" + "download_url: htp://domain.com/package.zip\n" + "license_expression: license1 AND license2\n" + "copyright: copyright on package\n" + "notice_file: package.zip.NOTICE\n" + "licenses:\n" + " - key: license1\n" + " name: License1\n" + " file: license1.LICENSE\n" + " - key: license2\n" + " name: License2\n" + " file: license2.LICENSE\n" + ) + + expected = [ + ("package.zip.NOTICE", "Notice\nText"), + ("license1.LICENSE", "line1\nline2"), + ("license2.LICENSE", ""), + ("package.zip.ABOUT", expected_yaml), + ] + + self.assertEqual(expected, package.get_about_files()) + + def test_component_model_get_about_files(self): + package1 = Package.objects.create( + filename="package1.zip", + download_url="htp://domain.com/package.zip", + copyright="copyright on package", + notice_text="Notice\r\nText", + license_expression=f"{self.license1.key} AND {self.license2.key}", + dataspace=self.dataspace, + ) + ComponentAssignedPackage.objects.create( + component=self.component1, package=package1, dataspace=self.dataspace + ) + + package2 = Package.objects.create( + filename="package2.zip", + notice_text="Notice", + license_expression=f"{self.license1.key}", + dataspace=self.dataspace, + ) + ComponentAssignedPackage.objects.create( + component=self.component1, package=package2, dataspace=self.dataspace + ) + + expected_yaml1 = ( + "about_resource: package1.zip\n" + "name: a\n" + "version: '1.0'\n" + "download_url: htp://domain.com/package.zip\n" + "license_expression: license1 AND license2\n" + "copyright: copyright on package\n" + "notice_file: package1.zip.NOTICE\n" + "owner: Owner\n" + "licenses:\n" + " - key: license1\n" + " name: License1\n" + " file: license1.LICENSE\n" + " - key: license2\n" + " name: License2\n" + " file: license2.LICENSE\n" + ) + + expected_yaml2 = ( + "about_resource: package2.zip\n" + "name: a\n" + "version: '1.0'\n" + "license_expression: license1\n" + "notice_file: package2.zip.NOTICE\n" + "owner: Owner\n" + "licenses:\n" + " - key: license1\n" + " name: License1\n" + " file: license1.LICENSE\n" + ) + + expected = [ + ("package1.zip.NOTICE", "Notice\nText"), + ("license1.LICENSE", ""), + ("license2.LICENSE", ""), + ("package1.zip.ABOUT", expected_yaml1), + ("package2.zip.NOTICE", "Notice"), + ("license1.LICENSE", ""), + ("package2.zip.ABOUT", expected_yaml2), + ] + + self.assertEqual(expected, self.component1.get_about_files()) + + def test_component_model_as_spdx(self): + self.component1.license_expression = f"{self.license1.key} AND {self.license2.key}" + self.component1.copyright = "copyright on component" + self.component1.homepage_url = "https://homepage.url" + self.component1.description = "Description" + self.component1.release_date = "2020-10-10" + self.component1.notice_text = ("Notice\r\nText",) + self.component1.save() + + expected = { + "name": "a", + "SPDXID": f"SPDXRef-dejacode-component-{self.component1.uuid}", + "attributionTexts": [("Notice\r\nText",)], + "downloadLocation": "NOASSERTION", + "licenseConcluded": "SPDX-1 AND LicenseRef-dejacode-license2", + "licenseDeclared": "SPDX-1 AND LicenseRef-dejacode-license2", + "copyrightText": "copyright on component", + "filesAnalyzed": False, + "supplier": "Organization: Owner", + "versionInfo": "1.0", + "homepage": "https://homepage.url", + "description": "Description", + "releaseDate": "2020-10-10T00:00:00Z", + } + self.assertEqual(expected, self.component1.as_spdx().as_dict()) + + def test_component_model_get_spdx_packages(self): + self.assertEqual([self.component1], self.component1.get_spdx_packages()) + + def test_package_model_as_spdx(self): + package1 = Package.objects.create( + filename="package1.zip", + download_url="htp://domain.com/package.zip", + copyright="copyright on package", + notice_text="Notice\r\nText", + license_expression=f"{self.license1.key} AND {self.license2.key}", + sha1="5ba93c9db0cff93f52b521d7420e43f6eda2784f", + md5="93b885adfe0da089cdf634904fd59f71", + cpe="cpe:2.3:a:djangoproject:django:0.95:*:*:*:*:*:*:*", + release_date="2020-10-10", + homepage_url="https://homepage.url", + description="Description", + notes="Notes", + dataspace=self.dataspace, + ) + package_url = "pkg:deb/debian/curl@7.50.3-1?arch=i386#subpath" + package1.set_package_url(package_url) + package1.save() + + expected = { + "name": "curl", + "SPDXID": f"SPDXRef-dejacode-package-{package1.uuid}", + "downloadLocation": "htp://domain.com/package.zip", + "licenseConcluded": "SPDX-1 AND LicenseRef-dejacode-license2", + "licenseDeclared": "SPDX-1 AND LicenseRef-dejacode-license2", + "copyrightText": "copyright on package", + "filesAnalyzed": False, + "versionInfo": "7.50.3-1", + "homepage": "https://homepage.url", + "packageFileName": "package1.zip", + "description": "Description", + "releaseDate": "2020-10-10T00:00:00Z", + "comment": "Notes", + "checksums": [ + {"algorithm": "SHA1", "checksumValue": "5ba93c9db0cff93f52b521d7420e43f6eda2784f"}, + {"algorithm": "MD5", "checksumValue": "93b885adfe0da089cdf634904fd59f71"}, + ], + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:deb/debian/curl@7.50.3-1?arch=i386#subpath", + }, + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:djangoproject:django:0.95:*:*:*:*:*:*:*", + }, + ], + "attributionTexts": ["Notice\r\nText"], + } + self.assertEqual(expected, package1.as_spdx().as_dict()) + + def test_package_model_get_spdx_packages(self): + package1 = Package.objects.create( + filename="package1.zip", + dataspace=self.dataspace, + ) + self.assertEqual([package1], package1.get_spdx_packages()) + + def test_component_model_as_cyclonedx(self): + self.component1.primary_language = "Python" + self.component1.homepage_url = "https://homepage.url" + self.component1.notice_text = "Notice" + cyclonedx_data = self.component1.as_cyclonedx() + expected_repr = "" + self.assertEqual(expected_repr, repr(cyclonedx_data)) + expected = { + "aboutcode:homepage_url": "https://homepage.url", + "aboutcode:notice_text": "Notice", + "aboutcode:primary_language": "Python", + } + properties = {property.name: property.value for property in cyclonedx_data.properties} + self.assertEqual(expected, properties) + + def test_package_model_as_cyclonedx(self): + package = Package.objects.create( + filename="package.zip", + type="deb", + namespace="debian", + name="curl", + version="7.50.3-1", + primary_language="Python", + homepage_url="https://homepage.url", + download_url="https://download.url", + notice_text="Notice", + dataspace=self.dataspace, + ) + cyclonedx_data = package.as_cyclonedx() + expected_repr = "" + self.assertEqual(expected_repr, repr(cyclonedx_data)) + expected = { + "aboutcode:download_url": "https://download.url", + "aboutcode:filename": "package.zip", + "aboutcode:homepage_url": "https://homepage.url", + "aboutcode:notice_text": "Notice", + "aboutcode:primary_language": "Python", + } + properties = {property.name: property.value for property in cyclonedx_data.properties} + self.assertEqual(expected, properties) + + def test_package_model_github_repo_url(self): + urls = [ + ("", None), + ("http://someurl.com/archive.zip", None), + ( + "https://github.com/adobe/brackets/releases/download/release-1.14/" + "Brackets.Release.1.14.32-bit.deb", + "https://github.com/adobe/brackets/tree/release-1.14", + ), + ( + "https://github.com/adobe/brackets/releases/download/release-1.14/" + "Brackets.Release.1.14.64-bit.deb", + "https://github.com/adobe/brackets/tree/release-1.14", + ), + ( + "https://github.com/adobe/brackets/releases/download/release-1.14/" + "Brackets.Release.1.14.dmg", + "https://github.com/adobe/brackets/tree/release-1.14", + ), + ( + "https://github.com/adobe/brackets/releases/download/release-1.14/" + "Brackets.Release.1.14.msi", + "https://github.com/adobe/brackets/tree/release-1.14", + ), + ( + "https://github.com/adobe/brackets/archive/release-1.14.zip", + "https://github.com/adobe/brackets/tree/release-1.14", + ), + ( + "https://github.com/adobe/brackets/archive/release-1.14.tar.gz", + "https://github.com/adobe/brackets/tree/release-1.14", + ), + ( + "https://github.com/adobe/brackets/archive/master.zip", + "https://github.com/adobe/brackets/tree/master", + ), + ] + + for url, expected in urls: + p = Package() + p.download_url = url + self.assertEqual(expected, p.github_repo_url) + + @mock.patch("requests.get") + def test_collect_package_data(self, mock_get): + expected_message = ( + "Could not download content: ftp://ftp.denx.de/pub/u-boot/u-boot-2017.11.tar.bz2" + ) + with self.assertRaisesMessage(DataCollectionException, expected_message): + collect_package_data("ftp://ftp.denx.de/pub/u-boot/u-boot-2017.11.tar.bz2") + + package_url = "http://domain.com/a.zip;?#" + + default_max_length = download.CONTENT_MAX_LENGTH + download.CONTENT_MAX_LENGTH = 0 + + expected_message = "Downloaded content too large (Max: 0\xa0bytes)." + mock_get.return_value = mock.Mock( + content=b"\x00", headers={"content-length": 300000000}, status_code=200 + ) + with self.assertRaisesMessage(DataCollectionException, expected_message): + collect_package_data(package_url) + + download.CONTENT_MAX_LENGTH = default_max_length + mock_get.return_value = mock.Mock( + content=b"\x00", headers={"content-length": 1}, status_code=200 + ) + expected_data = { + "download_url": "http://domain.com/a.zip;?#", + "filename": "a.zip", + "size": 1, + "sha1": "5ba93c9db0cff93f52b521d7420e43f6eda2784f", + "md5": "93b885adfe0da089cdf634904fd59f71", + "sha256": ("6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d"), + "sha512": ( + "b8244d028981d693af7b456af8efa4cad63d282e19ff14942c246e50d9351d2270" + "4a802a71c3580b6370de4ceb293c324a8423342557d4e5c38438f0e36910ee" + ), + } + self.assertEqual(expected_data, collect_package_data(package_url)) + + expected_message = ( + "Exception Value: HTTPConnectionPool" + "(host='mirror.centos.org', port=80): Read timed out." + ) + response = mock.MagicMock(headers={}, status_code=200) + type(response).content = mock.PropertyMock( + side_effect=requests.ConnectionError(expected_message) + ) + mock_get.return_value = response + with self.assertRaisesMessage(DataCollectionException, expected_message): + collect_package_data(package_url) + + headers = { + "content-length": 1, + "content-disposition": 'attachment; filename="another_name.zip"', + } + mock_get.return_value = mock.Mock(content=b"\x00", headers=headers, status_code=200) + expected_data = { + "download_url": "http://domain.com/a.zip;?#", + "filename": "another_name.zip", + "size": 1, + "sha1": "5ba93c9db0cff93f52b521d7420e43f6eda2784f", + "md5": "93b885adfe0da089cdf634904fd59f71", + "sha256": ("6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d"), + "sha512": ( + "b8244d028981d693af7b456af8efa4cad63d282e19ff14942c246e50d9351d2270" + "4a802a71c3580b6370de4ceb293c324a8423342557d4e5c38438f0e36910ee" + ), + } + self.assertEqual(expected_data, collect_package_data(package_url)) + + def test_package_create_save_set_usage_policy_from_license(self): + from policy.models import AssociatedPolicy + from policy.models import UsagePolicy + + self.client.login(username="nexb_user", password="secret") + + license_policy = UsagePolicy.objects.create( + label="LicensePolicy", + icon="icon", + content_type=ContentType.objects.get_for_model(License), + dataspace=self.dataspace, + ) + self.license1.usage_policy = license_policy + self.license1.save() + + package_policy = UsagePolicy.objects.create( + label="PackagePolicy", + icon="icon", + content_type=ContentType.objects.get_for_model(Package), + dataspace=self.dataspace, + ) + AssociatedPolicy.objects.create( + from_policy=license_policy, + to_policy=package_policy, + dataspace=self.dataspace, + ) + + package1 = Package.objects.create( + filename="package1", + dataspace=self.dataspace, + license_expression=self.license1.key, + ) + self.assertIsNone(package1.usage_policy) + + self.dataspace.set_usage_policy_on_new_component_from_licenses = True + self.dataspace.save() + package2 = Package.objects.create( + filename="package2", + dataspace=self.dataspace, + license_expression=self.license1.key, + ) + self.assertEqual(package_policy, package2.usage_policy) + + # Edition + package1.save() + self.assertEqual(package_policy, package1.usage_policy) + + # API Create + data = { + "filename": "package3", + "license_expression": self.license1.key, + } + response = self.client.post(reverse("api_v2:package-list"), data) + self.assertEqual(201, response.status_code) + package3 = Package.objects.latest("id") + self.assertEqual(package_policy, package3.usage_policy) + + # API Edit + package4 = Package.objects.create( + filename="package4", + dataspace=self.dataspace, + ) + package4_api_url = reverse("api_v2:package-detail", args=[package4.uuid]) + put_data = json.dumps( + { + "filename": package4.filename, + "license_expression": self.license1.key, + } + ) + response = self.client.put(package4_api_url, data=put_data, content_type="application/json") + self.assertEqual(200, response.status_code) + package4 = Package.objects.get(id=package4.id) + self.assertEqual(package_policy, package4.usage_policy) + + # Import + formset_data = { + "form-TOTAL_FORMS": "1", + "form-INITIAL_FORMS": "0", + "form-0-filename": "package5", + "form-0-license_expression": self.license1.key, + } + importer = PackageImporter(self.user, formset_data=formset_data) + self.assertTrue(importer.formset.is_valid()) + importer.save_all() + package5 = importer.results["added"][0] + self.assertEqual(package_policy, package5.usage_policy) + + def test_component_model_where_used_property(self): + product1 = Product.objects.create(name="P1", dataspace=self.dataspace) + ProductComponent.objects.create( + product=product1, component=self.component1, dataspace=self.dataspace + ) + + basic_user = create_user("basic_user", self.dataspace) + self.assertEqual("Product 0\n", self.component1.where_used(user=basic_user)) + + self.assertTrue(self.user.is_superuser) + self.assertEqual("Product 1\n", self.component1.where_used(user=self.user)) + + def test_package_model_where_used_property(self): + product1 = Product.objects.create(name="P1", dataspace=self.dataspace) + package1 = Package.objects.create(filename="package", dataspace=self.dataspace) + ProductPackage.objects.create(product=product1, package=package1, dataspace=self.dataspace) + + basic_user = create_user("basic_user", self.dataspace) + self.assertEqual("Product 0\nComponent 0\n", package1.where_used(user=basic_user)) + + self.assertTrue(self.user.is_superuser) + self.assertEqual("Product 1\nComponent 0\n", package1.where_used(user=self.user)) + + ComponentAssignedPackage.objects.create( + component=self.component1, package=package1, dataspace=self.dataspace + ) + self.assertEqual("Product 0\nComponent 1\n", package1.where_used(user=basic_user)) + + def test_package_model_inferred_url_property(self): + package1 = Package.objects.create(filename="package", dataspace=self.dataspace) + self.assertIsNone(package1.inferred_url) + + package1.set_package_url("pkg:pypi/toml@0.10.2") + package1.save() + self.assertEqual("https://pypi.org/project/toml/0.10.2/", package1.inferred_url) + + package1.set_package_url("pkg:github/package-url/packageurl-python@0.10.4?version_prefix=v") + package1.save() + expected = "https://github.com/package-url/packageurl-python/tree/v0.10.4" + self.assertEqual(expected, package1.inferred_url) diff --git a/component_catalog/tests/test_urn.py b/component_catalog/tests/test_urn.py new file mode 100644 index 00000000..f10f940e --- /dev/null +++ b/component_catalog/tests/test_urn.py @@ -0,0 +1,59 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# DejaCode is a trademark of nexB Inc. +# SPDX-License-Identifier: AGPL-3.0-only +# See https://github.com/nexB/dejacode for support or download. +# See https://aboutcode.org for more information about AboutCode FOSS projects. +# + +from django.test import TestCase + +from component_catalog.models import Component +from dje import urn +from dje import urn_resolver +from dje.models import Dataspace +from organization.models import Owner + + +class ComponentCatalogURNTestCase(TestCase): + # See also the tests in dje.URNTestCase + def setUp(self): + self.dataspace1 = Dataspace.objects.create(name="Dataspace") + self.owner1 = Owner.objects.create( + name="CCAD - Combined Conditional Access Development, LLC.", dataspace=self.dataspace1 + ) + self.owner2 = Owner.objects.create(name="Organization2", dataspace=self.dataspace1) + self.component1 = Component.objects.create( + owner=self.owner1, name="Zlib.Ada", version="1.3", dataspace=self.dataspace1 + ) + self.component2 = Component.objects.create( + owner=self.owner2, name="Component2", version="", dataspace=self.dataspace1 + ) + + def test_component_get_run(self): + expected = "urn:dje:component:Zlib.Ada:1.3" + self.assertEqual(expected, self.component1.urn) + + def test_component_urn_resolve(self): + # Using the output from Object.urn + self.assertEqual( + self.component1, urn_resolver.resolve(self.component1.urn, self.dataspace1) + ) + + # We are testing the several steps of validation for a given URN + with self.assertRaises(urn.URNValidationError): + urn_resolver.resolve("urn:dje:component:Component1", self.dataspace1) + + urn_component_no_version = "urn:dje:component:Component2:" + self.assertEqual( + self.component2, urn_resolver.resolve(urn_component_no_version, self.dataspace1) + ) + + # Same without the trailing colon, will raise the Validation error + urn_component_no_version_wrong = "urn:dje:component:Component2" + with self.assertRaises(urn.URNValidationError): + urn_resolver.resolve(urn_component_no_version_wrong, self.dataspace1) + + def test_components_urns_with_colons_in_name_are_valid_urns(self): + comp = Component.objects.create(name="a:na", version="a:ve", dataspace=self.dataspace1) + self.assertEqual("urn:dje:component:a%3Ana:a%3Ave", comp.urn) diff --git a/component_catalog/tests/test_views.py b/component_catalog/tests/test_views.py new file mode 100644 index 00000000..776c82ac --- /dev/null +++ b/component_catalog/tests/test_views.py @@ -0,0 +1,5033 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# DejaCode is a trademark of nexB Inc. +# SPDX-License-Identifier: AGPL-3.0-only +# See https://github.com/nexB/dejacode for support or download. +# See https://aboutcode.org for more information about AboutCode FOSS projects. +# + +import json +import uuid +from os.path import dirname +from os.path import join +from unittest import mock +from urllib.parse import quote + +from django.contrib.auth import get_user_model +from django.contrib.contenttypes.models import ContentType +from django.contrib.sessions.backends.base import SessionBase +from django.http.response import Http404 +from django.test import TestCase +from django.test import TransactionTestCase +from django.test.client import RequestFactory +from django.test.utils import override_settings +from django.urls import reverse +from django.utils import timezone +from django.utils.encoding import force_str + +import requests +from guardian.shortcuts import assign_perm +from notifications.models import Notification + +from component_catalog.forms import ComponentAddToProductForm +from component_catalog.forms import ComponentForm +from component_catalog.forms import PackageAddToProductForm +from component_catalog.forms import PackageForm +from component_catalog.models import Component +from component_catalog.models import ComponentAssignedPackage +from component_catalog.models import ComponentKeyword +from component_catalog.models import ComponentStatus +from component_catalog.models import ComponentType +from component_catalog.models import Package +from component_catalog.models import Subcomponent +from component_catalog.views import ComponentListView +from component_catalog.views import PackageDetailsView +from component_catalog.views import PackageTabScanView +from dejacode_toolkit.scancodeio import ScanCodeIO +from dejacode_toolkit.scancodeio import get_hash_uid +from dejacode_toolkit.scancodeio import get_webhook_url +from dejacode_toolkit.vulnerablecode import VulnerableCode +from dejacode_toolkit.vulnerablecode import get_plain_purls +from dje.copier import copy_object +from dje.models import Dataspace +from dje.models import ExternalReference +from dje.models import ExternalSource +from dje.models import History +from dje.tasks import scancodeio_submit_scan +from dje.tests import add_perm +from dje.tests import add_perms +from dje.tests import create_superuser +from dje.tests import create_user +from license_library.models import License +from license_library.models import LicenseAssignedTag +from license_library.models import LicenseTag +from license_library.models import LicenseTagGroup +from license_library.models import LicenseTagGroupAssignedTag +from organization.models import Owner +from organization.models import Subowner +from policy.models import UsagePolicy +from product_portfolio.models import Product +from product_portfolio.models import ProductComponent +from product_portfolio.models import ProductItemPurpose +from product_portfolio.models import ProductPackage +from product_portfolio.models import ProductRelationStatus +from workflow.models import Request +from workflow.models import RequestTemplate + +User = get_user_model() + + +class ComponentUserViewsTestCase(TestCase): + def setUp(self): + self.nexb_dataspace = Dataspace.objects.create(name="nexB") + self.nexb_user = User.objects.create_superuser( + "nexb_user", "test@test.com", "t3st", self.nexb_dataspace + ) + self.basic_user = create_user("basic_user", self.nexb_dataspace) + self.other_dataspace = Dataspace.objects.create(name="Other") + self.other_user = User.objects.create_superuser( + "other_user", "other@test.com", "t3st2", self.other_dataspace + ) + self.owner1 = Owner.objects.create(name="Test Organization", dataspace=self.nexb_dataspace) + self.owner2 = Owner.objects.create( + name="Test Organization 2", dataspace=self.nexb_dataspace + ) + self.type1 = ComponentType.objects.create( + label="Type1", notes="notes", dataspace=self.nexb_dataspace + ) + self.license1 = License.objects.create( + key="license1", + name="License1", + short_name="License1", + dataspace=self.nexb_dataspace, + owner=self.owner1, + ) + self.license_tag1 = LicenseTag.objects.create( + label="Tag 1", text="Text for tag1", dataspace=self.nexb_dataspace + ) + self.license_assigned_tag1 = LicenseAssignedTag.objects.create( + license=self.license1, + license_tag=self.license_tag1, + value=True, + dataspace=self.nexb_dataspace, + ) + self.license2 = License.objects.create( + key="license2", + name="License2", + short_name="License2", + dataspace=self.nexb_dataspace, + owner=self.owner1, + ) + self.component1 = Component.objects.create( + name="Component1", + version="0.1", + homepage_url="http://localhost.com", + license_expression="{} AND {}".format(self.license1.key, self.license2.key), + dataspace=self.nexb_dataspace, + ) + self.component2 = Component.objects.create( + name="Component2", + version="0.2", + homepage_url="http://localhost.com", + dataspace=self.nexb_dataspace, + ) + self.component3 = Component.objects.create( + name="Component3", + version="0.3", + homepage_url="http://localhost.com", + dataspace=self.nexb_dataspace, + ) + self.component4 = Component.objects.create( + name="Component4", + version="0.4", + homepage_url="http://localhost.com", + dataspace=self.nexb_dataspace, + ) + self.component5 = Component.objects.create( + name="ArchLinux", + version="2012.11.01", + homepage_url="http://localhost.com", + dataspace=self.nexb_dataspace, + ) + self.component6 = Component.objects.create( + name="Apache", + version="2", + homepage_url="http://localhost.com", + dataspace=self.nexb_dataspace, + ) + self.sub_2_1 = Subcomponent.objects.create( + parent=self.component2, child=self.component1, dataspace=self.nexb_dataspace + ) + self.sub_5_1 = Subcomponent.objects.create( + parent=self.component5, child=self.component1, dataspace=self.nexb_dataspace + ) + self.sub_1_3 = Subcomponent.objects.create( + parent=self.component1, child=self.component3, dataspace=self.nexb_dataspace + ) + self.sub_1_6 = Subcomponent.objects.create( + parent=self.component1, child=self.component6, dataspace=self.nexb_dataspace + ) + + def test_component_catalog_detail_view_content(self): + self.client.login(username="nexb_user", password="t3st") + self.component1.is_active = False + self.component1.save() + + url = self.component1.get_absolute_url() + response = self.client.get(url) + # Make sure it's not accessible if not is_active + self.assertContains(response, "Page not found", status_code=404) + + # Make sure it's accessible if is_active + self.component1.is_active = True + self.component1.license_expression = "{} AND {}".format( + self.license1.key, self.license2.key + ) + self.component1.save() + response = self.client.get(url) + self.assertEqual(200, response.status_code) + self.assertContains(response, "{}".format(self.component1)) + + # Check the relations tabs presence + self.assertContains(response, 'id="tab_license"') + self.assertContains(response, 'id="tab_hierarchy"') + + # Legal tab is only displayed if one a the legal field is set + self.assertNotContains(response, 'id="tab_legal"') + self.component1.legal_comments = "Comments" + self.component1.save() + response = self.client.get(url) + self.assertContains(response, 'id="tab_legal"') + + # A user in any dataspace can look at reference data + self.assertTrue(self.component1.dataspace.is_reference) + self.client.login(username="other_user", password="t3st2") + response = self.client.get(url) + self.assertEqual(200, response.status_code) + + # Moving the component to a non-reference dataspace + self.component1.dataspace = Dataspace.objects.create(name="NotReference") + self.component1.save() + response = self.client.get(url) + self.assertContains(response, "Page not found", status_code=404) + + def test_component_catalog_detail_view_reference_data_label(self): + url = self.component1.get_absolute_url() + expected = '{}".format(license2_str), html=True) + # Link to the license as in LL + self.assertContains(response, self.license1.key) + # Check the Guidance for License1 is present + self.assertContains(response, "{}".format(self.license1.guidance), html=True) + + # Check the tag set to True is displayed + self.assertTrue(self.license_assigned_tag1.value) + # self.assertContains(response, f'{self.license_tag1.label}') + self.assertContains(response, f' data-bs-content="{self.license_tag1.text}"') + + # Check the ordering of the tables respect the license_expression ordering + self.license1.is_active = False + self.license1.save() + response = self.client.get(url) + self.assertEqual("license1 AND license2", self.component1.license_expression) + + def no_whitespace(s): + return "".join(force_str(s).split()) + + expected = "{}{}".format(license1_str, license2_str) + self.assertIn(no_whitespace(expected), no_whitespace(response.content)) + + self.component1.license_expression = "{} AND {}".format( + self.license2.key, self.license1.key + ) + self.component1.save() + response = self.client.get(url) + expected = "{}{}".format(license2_str, license1_str) + self.assertIn(no_whitespace(expected), no_whitespace(response.content)) + + def test_return_to_component_from_license_details(self): + # Making sure a 'Return to Component' link is available on a License + # details view when coming from a Component details view + self.client.login(username="nexb_user", password="t3st") + response = self.client.get( + self.license1.get_absolute_url(), HTTP_REFERER=self.component1.get_absolute_url() + ) + self.assertContains(response, "Return to component") + + def test_component_catalog_hierarchy_tab(self): + self.client.login(username="nexb_user", password="t3st") + url = self.component1.get_absolute_url() + + # Make component2 in CC and component3 not in CC + self.component2.is_active = True + self.component2.save() + self.component3.is_active = False + self.component3.save() + + response = self.client.get(url) + self.assertContains( + response, + f'{self.component2}', + ) + self.assertNotContains( + response, + f'{self.component3}', + ) + + self.assertTrue("Hierarchy" in response.context["tabsets"]) + hierarchy_fields = response.context["tabsets"]["Hierarchy"]["fields"] + parents = hierarchy_fields[0][1]["related_parents"] + children = hierarchy_fields[0][1]["related_children"] + # Assert that parents and children are in alphabetical order + self.assertEqual(["ArchLinux", "Component2"], [x.parent.name for x in parents]) + self.assertEqual(["Apache", "Component3"], [x.child.name for x in children]) + + expected1 = f'
      ' + expected2 = f'
      ' + expected3 = f"source: 'component_{self.component1.id}'" + expected4 = f"target: 'component_{self.component2.id}'" + + self.assertContains(response, expected1) + self.assertContains(response, expected2) + self.assertContains(response, expected3) + self.assertContains(response, expected4) + + expected = f""" +
      +
        + + + + + {self.component1} + +
        + {self.component1.get_license_expression_linked()} +
        +
        + """ + self.assertContains(response, expected, html=True) + + product1 = Product.objects.create( + name="Product1", version="1.0", dataspace=self.nexb_dataspace + ) + ProductComponent.objects.create( + product=product1, component=self.component1, dataspace=self.nexb_dataspace + ) + self.assertEqual(1, product1.productcomponents.count()) + response = self.client.get(url) + expected = f'
        ' + self.assertContains(response, expected) + self.assertContains(response, f"target: 'product_{product1.id}'") + + def test_component_catalog_history_tab(self): + url = self.component1.get_absolute_url() + + History.log_addition(self.nexb_user, self.component1) + History.log_change(self.nexb_user, self.component1, "Changed name.") + History.log_change(self.basic_user, self.component1, "Changed version.") + + with override_settings(ANONYMOUS_USERS_DATASPACE=self.nexb_dataspace.name): + response = self.client.get(url) + self.assertFalse("History" in response.context["tabsets"]) + + self.client.login(username="nexb_user", password="t3st") + response = self.client.get(url) + self.assertTrue("History" in response.context["tabsets"]) + history_fields = response.context["tabsets"]["History"]["fields"] + expected = [ + "Created date", + "Created by", + "Last modified date", + "Last modified by", + "Changes", + ] + self.assertEqual(expected, [field[0] for field in history_fields]) + + self.assertContains(response, "now by basic_user") + self.assertContains(response, "Changed version.") + self.assertContains(response, "now by nexb_user") + self.assertContains(response, "Changed name.") + + def test_component_catalog_productcomponent_secured_hierarchy_and_product_usage(self): + component1 = Component.objects.create(name="c1", dataspace=self.nexb_dataspace) + product1 = Product.objects.create(name="p1", dataspace=self.nexb_dataspace) + ProductComponent.objects.create( + product=product1, component=component1, dataspace=self.nexb_dataspace + ) + url = component1.get_absolute_url() + + # Product data in Component views are not available for AnonymousUser for security reason + with override_settings(ANONYMOUS_USERS_DATASPACE=self.nexb_dataspace.name): + response = self.client.get(url) + tabsets = response.context["tabsets"] + self.assertIn("Essentials", tabsets) + self.assertNotIn("Hierarchy", tabsets) + self.assertNotIn("Product usage", tabsets) + self.assertNotContains(response, product1.get_absolute_url()) + self.assertNotContains(response, product1.name) + + self.client.login(username="nexb_user", password="t3st") + response = self.client.get(url) + tabsets = response.context["tabsets"] + self.assertIn("Hierarchy", tabsets) + self.assertIn("Product usage", tabsets) + self.assertContains(response, product1.get_absolute_url()) + self.assertContains(response, product1.name) + + self.client.login(username=self.basic_user.username, password="secret") + response = self.client.get(url) + tabsets = response.context["tabsets"] + self.assertNotIn("Hierarchy", tabsets) + self.assertNotIn("Product usage", tabsets) + self.assertNotContains(response, product1.get_absolute_url()) + self.assertNotContains(response, product1.name) + + assign_perm("view_product", self.basic_user, product1) + response = self.client.get(url) + tabsets = response.context["tabsets"] + self.assertIn("Hierarchy", tabsets) + self.assertIn("Product usage", tabsets) + self.assertContains(response, product1.get_absolute_url()) + self.assertContains(response, product1.name) + + def test_component_catalog_productpackage_secured_hierarchy_and_product_usage(self): + package1 = Package.objects.create(filename="package1", dataspace=self.nexb_dataspace) + product1 = Product.objects.create(name="product1", dataspace=self.nexb_dataspace) + ProductPackage.objects.create( + product=product1, package=package1, dataspace=self.nexb_dataspace + ) + url = package1.get_absolute_url() + + # Product data in Package views are not available for AnonymousUser for security reason + with override_settings(ANONYMOUS_USERS_DATASPACE=self.nexb_dataspace.name): + response = self.client.get(url) + tabsets = response.context["tabsets"] + self.assertIn("Essentials", tabsets) + self.assertNotIn("Product usage", tabsets) + self.assertNotContains(response, product1.get_absolute_url()) + self.assertNotContains(response, product1.name) + + self.client.login(username="nexb_user", password="t3st") + response = self.client.get(url) + tabsets = response.context["tabsets"] + self.assertIn("Product usage", tabsets) + self.assertContains(response, product1.get_absolute_url()) + self.assertContains(response, product1.name) + + self.client.login(username=self.basic_user.username, password="secret") + response = self.client.get(url) + tabsets = response.context["tabsets"] + self.assertNotIn("Product usage", tabsets) + self.assertNotContains(response, product1.get_absolute_url()) + self.assertNotContains(response, product1.name) + + assign_perm("view_product", self.basic_user, product1) + response = self.client.get(url) + tabsets = response.context["tabsets"] + self.assertIn("Product usage", tabsets) + self.assertContains(response, product1.get_absolute_url()) + self.assertContains(response, product1.name) + + def test_component_catalog_detail_view_owner_tab_hierarchy_availability(self): + # The js code related to the Owner hierarchy should is only embedded + # is the Owner has relatives + self.component1.owner = self.owner1 + self.component1.save() + self.assertFalse(self.owner1.has_parent_or_child()) + self.client.login(username="nexb_user", password="t3st") + url = self.component1.get_absolute_url() + response = self.client.get(url) + self.assertNotContains(response, "jsPlumbOwnerHierarchy") + self.assertNotContains(response, "Selected Owner") + self.assertNotContains(response, "Child Owners") + + child_owner = Owner.objects.create(name="ChildOwner", dataspace=self.nexb_dataspace) + Subowner.objects.create( + parent=self.owner1, child=child_owner, dataspace=self.nexb_dataspace + ) + + response = self.client.get(url) + self.assertContains(response, "jsPlumb") + self.assertContains(response, "Selected Owner") + self.assertContains(response, "Child Owners") + self.assertContains(response, child_owner.name) + + self.assertContains( + response, '
        '.format(self.owner1.id) + ) + self.assertContains( + response, '
        '.format(child_owner.id) + ) + self.assertContains( + response, f"{{source: 'owner_{child_owner.id}', target: 'owner_{self.owner1.id}'}}" + ) + + def test_component_catalog_list_view_sort_keep_active_filters(self): + self.client.login(username="nexb_user", password="t3st") + + url = reverse("component_catalog:component_list") + data = { + "q": "a", + "licenses": self.license1.key, + } + response = self.client.get(url, data=data) + + # Sort filter + self.assertContains( + response, + '', + ) + # Sort in the headers + self.assertContains( + response, + '', + ) + self.assertContains( + response, + '', + ) + + data["sort"] = "name" + response = self.client.get(url, data=data) + self.assertContains( + response, + '', + ) + self.assertContains( + response, + '', + ) + + data["sort"] = "-name" + response = self.client.get(url, data=data) + self.assertContains( + response, + '', + ) + self.assertContains( + response, + '', + ) + + def test_component_catalog_list_view_filters_breadcrumbs(self): + self.client.login(username="nexb_user", password="t3st") + + url = reverse("component_catalog:component_list") + data = { + "q": "a", + "sort": "name", + "licenses": [self.license1.key, self.license2.key], + "not_a_valid_entry": "not_a_valid_entry", + "type": "not_a_valid_entry", + } + response = self.client.get(url, data=data) + + href1 = ( + "?q=a&sort=name&licenses=license1&licenses=license2" + "&not_a_valid_entry=not_a_valid_entry" + ) + href2 = ( + "?q=a&sort=name&not_a_valid_entry=not_a_valid_entry" + "&type=not_a_valid_entry&licenses=license2" + ) + href3 = ( + "?q=a&sort=name&not_a_valid_entry=not_a_valid_entry" + "&type=not_a_valid_entry&licenses=license1" + ) + href4 = ( + "?sort=name&licenses=license1&licenses=license2" + "&not_a_valid_entry=not_a_valid_entry&type=not_a_valid_entry" + ) + href5 = ( + "?q=a&licenses=license1&licenses=license2" + "&not_a_valid_entry=not_a_valid_entry&type=not_a_valid_entry" + ) + + expected = f""" + + """ + self.assertContains(response, expected, html=True) + + def test_component_catalog_list_view_bootstrap_select_filters(self): + self.client.login(username="nexb_user", password="t3st") + url = reverse("component_catalog:component_list") + data = { + "licenses": self.license1.key, + } + response = self.client.get(url, data=data) + expected = """ + + """ + self.assertContains(response, expected, html=True) + + def test_component_catalog_component_list_view_add_to_product(self): + self.client.login(username=self.basic_user.username, password="secret") + url = reverse("component_catalog:component_list") + + expected1 = "column-selection" + expected2 = ( + f'' + ) + expected3 = 'data-bs-target="#add-to-product-modal"' + expected4 = 'id="add-to-product-modal"' + + response = self.client.get(url) + self.assertNotContains(response, expected1) + self.assertNotContains(response, expected2) + self.assertNotContains(response, expected3) + self.assertNotContains(response, expected4) + + add_perm(self.basic_user, "add_productcomponent") + response = self.client.get(url) + self.assertNotContains(response, expected1) + self.assertNotContains(response, expected2) + self.assertNotContains(response, expected3) + self.assertNotContains(response, expected4) + + product1 = Product.objects.create(name="Product1", dataspace=self.nexb_dataspace) + assign_perm("change_product", self.basic_user, product1) + assign_perm("view_product", self.basic_user, product1) + response = self.client.get(url) + self.assertContains(response, expected1) + self.assertContains(response, expected2) + self.assertContains(response, expected3) + self.assertContains(response, expected4) + + # form_invalid + data = { + "product": 999999, + "ids": 999999, + "submit": "Add to Product", + } + response = self.client.post(url, data) + self.assertRedirects(response, url) + + data["product"] = product1.id + data["ids"] = f"{self.component1.id}, {self.component2.id}" + response = self.client.post(url, data, follow=True) + self.assertContains(response, f"2 component(s) added to "{product1}".") + self.assertEqual(2, product1.components.count()) + + product1.refresh_from_db() + history_entries = History.objects.get_for_object(product1) + expected_messages = sorted( + [ + 'Added component "Component1 0.1"', + 'Added component "Component2 0.2"', + ] + ) + self.assertEqual( + expected_messages, sorted([entry.change_message for entry in history_entries]) + ) + self.assertEqual(self.basic_user, product1.last_modified_by) + + def test_component_catalog_details_external_reference_tab(self): + self.client.login(username="nexb_user", password="t3st") + url = self.component1.get_absolute_url() + + response = self.client.get(url) + expected = 'id="tab_external-references"' + self.assertNotContains(response, expected) + + source1 = ExternalSource.objects.create( + label="GitHub", + dataspace=self.nexb_dataspace, + ) + + ext_ref1 = ExternalReference.objects.create_for_content_object( + content_object=self.component1, + external_source=source1, + external_id="dejacode external id", + ) + + response = self.client.get(url) + self.assertContains(response, expected) + self.assertContains(response, ext_ref1.external_id) + self.assertContains(response, source1.label) + + def test_component_catalog_detail_view_admin_edit_component_admin_link(self): + url = self.component1.get_absolute_url() + perm = "component_catalog.change_component" + + expected1 = "Edit Component" + expected2 = self.component1.get_admin_url() + + self.nexb_user.is_superuser = False + self.nexb_user.save() + self.assertTrue(self.nexb_user.is_staff) + self.assertEqual(self.nexb_user.dataspace, self.component1.dataspace) + self.assertFalse(self.nexb_user.has_perm(perm)) + + self.client.login(username=self.nexb_user.username, password="t3st") + response = self.client.get(url) + self.assertNotContains(response, expected1) + self.assertNotContains(response, expected2) + + self.nexb_user = add_perm(self.nexb_user, "change_component") + self.assertTrue(self.nexb_user.has_perm(perm)) + response = self.client.get(url) + self.assertContains(response, expected1) + self.assertContains(response, expected2) + + self.client.login(username=self.other_user.username, password="t3st2") + response = self.client.get(url) + self.assertTrue(self.other_user.is_superuser) + self.assertTrue(self.other_user.has_perm(perm)) + self.assertNotEqual(self.other_user.dataspace, self.component1.dataspace) + self.assertNotContains(response, expected1) + self.assertNotContains(response, expected2) + + def test_component_catalog_detail_view_admin_manage_subcomponent_relationships_link(self): + url = self.component1.get_absolute_url() + perm = "component_catalog.change_subcomponent" + + expected1 = "Manage Subcomponent relationships" + expected2 = "{}?parent__id__exact={}".format( + reverse("admin:component_catalog_subcomponent_changelist"), self.component1.pk + ) + + self.nexb_user.is_superuser = False + self.nexb_user.save() + self.assertTrue(self.nexb_user.is_staff) + self.assertEqual(self.nexb_user.dataspace, self.component1.dataspace) + self.assertFalse(self.nexb_user.has_perm(perm)) + + self.client.login(username=self.nexb_user.username, password="t3st") + response = self.client.get(url) + self.assertNotContains(response, expected1) + self.assertNotContains(response, expected2) + + self.nexb_user = add_perm(self.nexb_user, "change_subcomponent") + self.assertTrue(self.nexb_user.has_perm(perm)) + response = self.client.get(url) + self.assertContains(response, expected1) + self.assertContains(response, expected2) + + self.client.login(username=self.other_user.username, password="t3st2") + response = self.client.get(url) + self.assertTrue(self.other_user.is_superuser) + self.assertTrue(self.other_user.has_perm(perm)) + self.assertNotEqual(self.other_user.dataspace, self.component1.dataspace) + self.assertNotContains(response, expected1) + self.assertNotContains(response, expected2) + + def test_component_catalog_detail_view_admin_manage_packages_link(self): + url = self.component1.get_absolute_url() + perm = "component_catalog.change_package" + + expected1 = "Manage Packages" + expected2 = "{}?component__id__exact={}".format( + reverse("admin:component_catalog_package_changelist"), self.component1.pk + ) + + self.nexb_user.is_superuser = False + self.nexb_user.save() + self.assertTrue(self.nexb_user.is_staff) + self.assertEqual(self.nexb_user.dataspace, self.component1.dataspace) + self.assertFalse(self.nexb_user.has_perm(perm)) + + self.client.login(username=self.nexb_user.username, password="t3st") + response = self.client.get(url) + self.assertNotContains(response, expected1) + self.assertNotContains(response, expected2) + + self.nexb_user = add_perm(self.nexb_user, "change_package") + self.assertTrue(self.nexb_user.has_perm(perm)) + response = self.client.get(url) + self.assertContains(response, expected1) + self.assertContains(response, expected2) + + self.client.login(username=self.other_user.username, password="t3st2") + response = self.client.get(url) + self.assertTrue(self.other_user.is_superuser) + self.assertTrue(self.other_user.has_perm(perm)) + self.assertNotEqual(self.other_user.dataspace, self.component1.dataspace) + self.assertNotContains(response, expected1) + self.assertNotContains(response, expected2) + + def test_component_catalog_details_view_num_queries(self): + self.client.login(username="nexb_user", password="t3st") + url = self.component1.get_absolute_url() + + package1 = Package.objects.create(filename="package1", dataspace=self.nexb_dataspace) + ComponentAssignedPackage.objects.create( + component=self.component1, package=package1, dataspace=self.nexb_dataspace + ) + package2 = Package.objects.create(filename="package2", dataspace=self.nexb_dataspace) + ComponentAssignedPackage.objects.create( + component=self.component1, package=package2, dataspace=self.nexb_dataspace + ) + self.assertEqual(2, self.component1.packages.count()) + + self.assertEqual(2, self.component1.licenses.count()) + self.assertEqual(2, self.component1.related_parents.count()) + self.assertEqual(2, self.component1.related_children.count()) + + History.log_change(self.nexb_user, self.component1, "Changed name.") + History.log_change(self.basic_user, self.component1, "Changed version.") + History.log_change(self.nexb_user, self.component1, "Changed notes.") + + with self.assertNumQueries(31): + self.client.get(url) + + def test_component_catalog_details_view_package_tab_fields_visibility(self): + self.client.login(username="nexb_user", password="t3st") + url = self.component1.get_absolute_url() + + package1 = Package.objects.create(filename="package1", dataspace=self.nexb_dataspace) + ComponentAssignedPackage.objects.create( + component=self.component1, package=package1, dataspace=self.nexb_dataspace + ) + + title = ( + "The date that the package file was created, or when it was posted to" + " its original download source." + ) + expected = f""" + + Release date + + """ + + response = self.client.get(url) + self.assertContains(response, 'id="tab_packages"') + self.assertContains(response, "Filename") + self.assertNotContains(response, expected, html=True) + + package1.release_date = timezone.now() + package1.save() + response = self.client.get(url) + self.assertContains(response, expected, html=True) + + def test_component_catalog_details_view_hide_empty_fields(self): + self.client.login(username="nexb_user", password="t3st") + details_url = self.component1.get_absolute_url() + expected = '
         
        ' + + self.assertFalse(self.nexb_dataspace.hide_empty_fields_in_component_details_view) + response = self.client.get(details_url) + self.assertContains(response, expected, html=True) + + self.nexb_dataspace.hide_empty_fields_in_component_details_view = True + self.nexb_dataspace.save() + response = self.client.get(details_url) + self.assertNotContains(response, expected, html=True) + + def test_component_catalog_details_view_acceptable_linkages_in_policy_tab(self): + self.client.login(username="nexb_user", password="t3st") + details_url = self.component1.get_absolute_url() + self.component1.acceptable_linkages = ["linkage1", "linkage2"] + self.component1.save() + + response = self.client.get(details_url) + expected = ( + '
        linkage1 linkage2
        ' + ) + self.assertContains(response, expected, html=True) + + @mock.patch("dejacode_toolkit.vulnerablecode.VulnerableCode.get_vulnerabilities_by_cpe") + @mock.patch("dejacode_toolkit.vulnerablecode.VulnerableCode.is_configured") + def test_component_details_view_tab_vulnerabilities( + self, mock_is_configured, mock_get_vulnerabilities_by_cpe + ): + mock_is_configured.return_value = True + + self.nexb_dataspace.enable_vulnerablecodedb_access = True + self.nexb_dataspace.save() + + self.component1.cpe = "cpe:2.3:a:djangoproject:django:0.95:*:*:*:*:*:*:*" + self.component1.save() + + mock_get_vulnerabilities_by_cpe.return_value = [ + { + "vulnerability_id": "VULCOID-5U6", + "summary": "django.contrib.sessions in Django before 1.2.7", + "references": [ + { + "reference_url": "https://nvd.nist.gov/vuln/detail/CVE-2011-4136", + "reference_id": "CVE-2011-4136", + } + ], + } + ] + + self.client.login(username="nexb_user", password="t3st") + response = self.client.get(self.component1.details_url) + + expected = ( + ' +
        Scan running
        + +
        Created date
        +
        + +
        June 21, 2018, 12:32 PM UTC
        +
        +
        Start date
        +
        + +
        June 21, 2018, 12:32 PM UTC
        +
        +
        End date
        +
        +
         
        +
        +
        ScanCode.io version
        +
        + +
        31.0.0
        +
        + + """ + self.assertContains(response, expected, html=True) + self.assertNotContains(response, "Set values to Package") + + @mock.patch("dejacode_toolkit.scancodeio.ScanCodeIO.fetch_scan_data") + @mock.patch("dejacode_toolkit.scancodeio.ScanCodeIO.fetch_scan_info") + def test_package_details_view_scan_tab_scan_success( + self, mock_fetch_scan_info, mock_fetch_scan_data + ): + self.client.login(username=self.super_user.username, password="secret") + + mock_fetch_scan_info.return_value = { + "count": 1, + "results": [ + { + "name": "70265cfaec8cab26f8c88e968e84f23efd886ced663c10bfe408ff20c26a103f", + "url": "/api/projects/f622d852-2d6a-4fb5-ab89-a90db54a4581/", + "uuid": "f622d852-2d6a-4fb5-ab89-a90db54a4581", + "runs": [ + { + "status": "success", + } + ], + } + ], + } + + exception = License.objects.create( + key="e", + name="e", + short_name="e", + is_exception=True, + owner=self.owner1, + dataspace=self.dataspace, + ) + license_expression = f"{self.license1.key} AND {self.license2.key} WITH {exception.key}" + + mock_fetch_scan_data.return_value = { + "declared_license_expression": license_expression, + "license_clarity_score": { + "score": 90, + "declared_license": True, + "identification_precision": True, + "has_license_text": True, + "declared_copyrights": True, + "conflicting_license_categories": False, + "ambiguous_compound_licensing": False, + }, + "declared_holder": "Hank Hill", + "primary_language": "C++", + "other_license_expressions": [{"value": "mit", "count": 3}], + "other_holders": [{"value": "Alex Gaynor", "count": 2}], + "other_languages": [{"value": "Python", "count": 39}], + } + + response = self.client.get(self.package1_tab_scan_url) + self.assertEqual(response.status_code, 404) + + self.dataspace.enable_package_scanning = True + self.dataspace.save() + response = self.client.get(self.package1_tab_scan_url) + + expected_declared_license = """ + + + l1 + AND (l2 + WITH e) + + """ + expected_declared_holder = ( + ' Hank Hill' + ) + expected_primary_language = ( + ' C++' + ) + expected_other_licenses = """ + mit 3 + """ + expected_other_holders = ( + ' Alex Gaynor ' + '2' + ) + expected_other_languages = ( + ' Python ' + '39' + ) + self.assertContains(response, expected_declared_license, html=True) + self.assertContains(response, expected_declared_holder, html=True) + self.assertContains(response, expected_primary_language, html=True) + self.assertContains(response, expected_other_licenses, html=True) + self.assertContains(response, expected_other_holders, html=True) + self.assertContains(response, expected_other_languages, html=True) + self.assertContains(response, "Scan Summary") + self.assertContains(response, "Set values to Package") + self.assertContains(response, "Download Scan data") + + scan_data_as_file_url = reverse( + "component_catalog:scan_data_as_file", + args=["f622d852-2d6a-4fb5-ab89-a90db54a4581", self.package1.filename], + ) + self.assertContains(response, scan_data_as_file_url) + + self.package1.filename = "" + self.package1.type = "deb" + self.package1.name = "name" + self.package1.version = "1.0" + self.package1.save() + scan_data_as_file_url = reverse( + "component_catalog:scan_data_as_file", + args=["f622d852-2d6a-4fb5-ab89-a90db54a4581", self.package1.package_url_filename], + ) + response = self.client.get(self.package1_tab_scan_url) + self.assertContains(response, scan_data_as_file_url) + + @mock.patch("dejacode_toolkit.scancodeio.ScanCodeIO.fetch_scan_data") + @mock.patch("dejacode_toolkit.scancodeio.ScanCodeIO.fetch_scan_info") + def test_package_details_view_scan_tab_scan_failed( + self, mock_fetch_scan_info, mock_fetch_scan_data + ): + self.client.login(username=self.super_user.username, password="secret") + + mock_fetch_scan_data.return_value = {} + mock_fetch_scan_info.return_value = { + "count": 1, + "results": [ + { + "name": "70265cfaec8cab26f8c88e968e84f23efd886ced663c10bfe408ff20c26a103f", + "url": "/api/projects/f622d852-2d6a-4fb5-ab89-a90db54a4581/", + "uuid": "f622d852-2d6a-4fb5-ab89-a90db54a4581", + "runs": [ + { + "status": "failure", + "task_output": "task_output_value", + "log": "log_value", + } + ], + } + ], + } + + response = self.client.get(self.package1_tab_scan_url) + self.assertEqual(response.status_code, 404) + + self.dataspace.enable_package_scanning = True + self.dataspace.save() + response = self.client.get(self.package1_tab_scan_url) + + self.assertContains(response, "Scan failure") + self.assertContains(response, "Task output") + self.assertContains(response, "task_output_value") + self.assertContains(response, "Log") + self.assertContains(response, "log_value") + + @mock.patch("dejacode_toolkit.scancodeio.ScanCodeIO.fetch_scan_data") + @mock.patch("dejacode_toolkit.scancodeio.ScanCodeIO.fetch_scan_info") + def test_package_details_view_scan_tab_detected_package( + self, mock_fetch_scan_info, mock_fetch_scan_data + ): + self.client.login(username=self.super_user.username, password="secret") + self.dataspace.enable_package_scanning = True + self.dataspace.save() + + mock_fetch_scan_info.return_value = { + "count": 1, + "results": [ + { + "name": "70265cfaec8cab26f8c88e968e84f23efd886ced663c10bfe408ff20c26a103f", + "url": "/api/projects/f622d852-2d6a-4fb5-ab89-a90db54a4581/", + "uuid": "f622d852-2d6a-4fb5-ab89-a90db54a4581", + } + ], + } + + detected_package = { + "primary_language": "Java", + "description": "AOP alliance\nAOP Alliance", + "release_date": "2018-01-01", + "homepage_url": "http://aopalliance.sourceforge.net", + "copyright": "Copyright", + "declared_license_expression": "public-domain", + # "other_license_expression": "bsd-simplified", + "notice_text": "NoticeText", + "purl": "pkg:maven/aopalliance/aopalliance@1.0", + } + + mock_fetch_scan_data.return_value = { + "key_files_packages": [detected_package], + } + + response = self.client.get(self.package1_tab_scan_url) + self.assertContains(response, "Detected Package") + for value in detected_package.values(): + self.assertContains(response, value) + + @mock.patch("dejacode_toolkit.scancodeio.ScanCodeIO.fetch_scan_data") + @mock.patch("dejacode_toolkit.scancodeio.ScanCodeIO.fetch_scan_info") + def test_package_details_view_scan_tab_license_clarity( + self, mock_fetch_scan_info, mock_fetch_scan_data + ): + self.client.login(username=self.super_user.username, password="secret") + self.dataspace.enable_package_scanning = True + self.dataspace.save() + + mock_fetch_scan_info.return_value = { + "count": 1, + "results": [ + { + "name": "70265cfaec8cab26f8c88e968e84f23efd886ced663c10bfe408ff20c26a103f", + "url": "/api/projects/f622d852-2d6a-4fb5-ab89-a90db54a4581/", + "uuid": "f622d852-2d6a-4fb5-ab89-a90db54a4581", + } + ], + } + + mock_fetch_scan_data.return_value = { + "license_clarity_score": {}, + } + + response = self.client.get(self.package1_tab_scan_url) + self.assertNotContains(response, "License Clarity") + + mock_fetch_scan_data.return_value = { + "license_clarity_score": { + "score": 90, + "declared_license": True, + "identification_precision": True, + "has_license_text": True, + "declared_copyrights": True, + "conflicting_license_categories": False, + "ambiguous_compound_licensing": True, + } + } + + response = self.client.get(self.package1_tab_scan_url) + self.assertContains(response, "License Clarity") + expected = """ + + +40 + +40 + +10 + +10 + -10 + + + 90 + + + """ + self.assertContains(response, expected, html=True) + + @mock.patch("dejacode_toolkit.scancodeio.ScanCodeIO.fetch_scan_data") + @mock.patch("dejacode_toolkit.scancodeio.ScanCodeIO.fetch_scan_info") + def test_package_details_view_scan_tab_key_files( + self, mock_fetch_scan_info, mock_fetch_scan_data + ): + self.client.login(username=self.super_user.username, password="secret") + self.dataspace.enable_package_scanning = True + self.dataspace.save() + + mock_fetch_scan_info.return_value = { + "count": 1, + "results": [ + { + "name": "70265cfaec8cab26f8c88e968e84f23efd886ced663c10bfe408ff20c26a103f", + "url": "/api/projects/f622d852-2d6a-4fb5-ab89-a90db54a4581/", + "uuid": "f622d852-2d6a-4fb5-ab89-a90db54a4581", + } + ], + } + + mock_fetch_scan_data.return_value = {} + expected_label = "Key files" + response = self.client.get(self.package1_tab_scan_url) + self.assertNotContains(response, expected_label) + + mock_fetch_scan_data.return_value = { + "key_files": [], + } + response = self.client.get(self.package1_tab_scan_url) + self.assertNotContains(response, expected_label) + + key_file_data = { + "authors": [], + "compliance_alert": "ok", + "content": "Copyright 2011 Gary Court.", + "copyrights": [ + {"copyright": "Copyright 2011 Gary Court", "end_line": 1, "start_line": 1} + ], + "detected_license_expression": "bsd-2-clause-views", + "detected_license_expression_spdx": "BSD-2-Clause-Views", + "emails": [], + "extension": "", + "extra_data": {}, + "file_type": "ASCII text, with very long lines (717)", + "for_packages": ["pkg:npm/uri-js@4.4.1?uuid=8e657f21-de14-4bc7-bd45-a47e0169e5a2"], + "holders": [{"end_line": 1, "holder": "Gary Court", "start_line": 1}], + "is_archive": False, + "is_binary": False, + "is_key_file": True, + "is_media": False, + "is_text": True, + "license_clues": [], + "license_detections": [ + { + "identifier": "bsd_2_clause_views-884555cb-5e8e-", + "license_expression": "bsd-2-clause-views", + "matches": [ + { + "end_line": 11, + "license_expression": "bsd-2-clause-views", + "match_coverage": 100.0, + "matched_length": 206, + "matched_text": "Redistribution", + "matcher": "3-seq", + "rule_identifier": "bsd-2-clause-views_11.RULE", + "rule_relevance": 100, + "score": 98.1, + "start_line": 3, + } + ], + } + ], + "md5": "3b55dad4a98748003b5b423477713da1", + "mime_type": "text/plain", + "name": "LICENSE", + "package_data": [], + "path": "package/LICENSE", + "percentage_of_license_text": 94.06, + "programming_language": "", + "sha1": "dc45ad0fa775735dfad6f590f126dee709763efc", + "sha256": "0af366eff4c01ec147c9c61ea9e8ffad64a4294754c9d79355f3fd1b97cb2fb9", + "sha512": "", + "size": 1452, + "status": "application-package", + "tag": "", + "type": "file", + "urls": [], + } + + mock_fetch_scan_data.return_value = {"key_files": [key_file_data]} + response = self.client.get(self.package1_tab_scan_url) + self.assertContains(response, expected_label) + self.assertContains(response, 'data-bs-target="#key-files-modal"') + self.assertContains(response, 'data-filename="LICENSE"') + self.assertContains(response, 'data-size="1.4 KB"') + self.assertContains(response, 'id="key-files-modal"') + self.assertContains(response, 'data-matched-texts="Redistribution // Copyright') + + @mock.patch("dejacode_toolkit.scancodeio.ScanCodeIO.fetch_scan_data") + @mock.patch("dejacode_toolkit.scancodeio.ScanCodeIO.fetch_scan_info") + def test_package_details_view_scan_tab_license_matches( + self, mock_fetch_scan_info, mock_fetch_scan_data + ): + self.client.login(username=self.super_user.username, password="secret") + self.dataspace.enable_package_scanning = True + self.dataspace.save() + + mock_fetch_scan_info.return_value = { + "count": 1, + "results": [ + { + "name": "70265cfaec8cab26f8c88e968e84f23efd886ced663c10bfe408ff20c26a103f", + "url": "/api/projects/f622d852-2d6a-4fb5-ab89-a90db54a4581/", + "uuid": "f622d852-2d6a-4fb5-ab89-a90db54a4581", + } + ], + } + + mock_fetch_scan_data.return_value = { + "other_license_expressions": [ + {"value": "apache-2.0", "count": 3}, + {"value": "mit", "count": 2}, + ], + } + + expected1 = ( + ' apache-2.0 3' + "" + ) + expected2 = ( + ' mit 2' + ) + response = self.client.get(self.package1_tab_scan_url) + self.assertContains(response, expected1, html=True) + self.assertContains(response, expected2, html=True) + + # Using {} in `path` and `matched_text` to test the proper escaping + mock_fetch_scan_data.return_value["license_matches"] = { + "mit": { + "${NameLower}_amethyst-0.11.1/Cargo.toml": [ + { + "license_expression": "mit", + "matches": [ + { + "license_expression": "mit", + "matched_text": 'license = "MIT/Apache-2.0"', + } + ], + } + ], + "amethyst_animation/Cargo.toml": [ + { + "license_expression": "mit", + "matches": [ + {"license_expression": "mit", "matched_text": "[hide].[o].[j55]{[at]}["} + ], + } + ], + }, + "apache-2.0": { + "amethyst-0.11.1/README.md": [ + { + "license_expression": "apache-2.0", + "matches": [ + { + "license_expression": "apache-2.0", + "matched_text": "Apache License 2.0][", + } + ], + } + ], + }, + } + + response = self.client.get(self.package1_tab_scan_url) + self.assertContains(response, "<div class="card mb-3">") + self.assertContains( + response, + 'data-bs-target="#scan-matches-modal" data-license-key="apache-2.0"', + ) + self.assertContains( + response, "[hide].[o].[j55]&lbrace;[at]&rbrace;[</code></pre>" + ) + + @mock.patch("dejacode_toolkit.scancodeio.ScanCodeIO.fetch_scan_data") + @mock.patch("dejacode_toolkit.scancodeio.ScanCodeIO.fetch_scan_info") + def test_package_details_view_scan_to_package(self, mock_fetch_scan_info, mock_fetch_scan_data): + url = self.package1.get_absolute_url() + self.client.login(username=self.basic_user.username, password="secret") + self.dataspace.enable_package_scanning = True + self.dataspace.save() + + mock_fetch_scan_info.return_value = { + "count": 1, + "results": [ + { + "name": "70265cfaec8cab26f8c88e968e84f23efd886ced663c10bfe408ff20c26a103f", + "url": "/api/projects/f622d852-2d6a-4fb5-ab89-a90db54a4581/", + "uuid": "f622d852-2d6a-4fb5-ab89-a90db54a4581", + } + ], + } + + detected_package = { + "primary_language": "Java", + "description": "AOP alliance\nAOP Alliance", + "release_date": "2018-01-01", + "homepage_url": "http://aopalliance.sourceforge.net", + "copyright": "Copyright", + "declared_license_expression": "public-domain", + "notice_text": "NoticeText", + "purl": "pkg:maven/aopalliance/aopalliance@1.0", + "is_key_file": 1, + } + + mock_fetch_scan_data.return_value = { + "key_files_packages": [detected_package], + } + + expected1 = "#scan-to-package-modal" + expected2 = 'id="scan-to-package-form"' + + response = self.client.get(self.package1_tab_scan_url) + self.assertNotContains(response, expected1) + self.assertNotContains(response, expected2) + + add_perm(self.basic_user, "change_package") + response = self.client.get(self.package1_tab_scan_url) + self.assertContains(response, expected1) + self.assertContains(response, expected2) + + detected_package.pop("is_key_file") + for expected_value in detected_package.values(): + self.assertContains(response, f"{expected_value}") + + post_data = { + "scan-to-package-primary_language": "Java", + "scan-to-package-description": "AOP alliance\nAOP Alliance", + "scan-to-package-release_date": "2018-01-01", + "scan-to-package-homepage_url": "http://aopalliance.sourceforge.net", + "scan-to-package-copyright": "Copyright", + "scan-to-package-license_expression": "public-domain", + "scan-to-package-notice_text": "NoticeText", + "scan-to-package-package_url": "pkg:maven/aopalliance/aopalliance@1.0", + "submit-scan-to-package-form": "Set values", + } + + response = self.client.post(url, post_data, follow=True) + messages = list(response.context["messages"]) + self.assertIn("Error assigning values to the package.", str(messages[0])) + self.assertIn("Unknown license key(s): public-domain", str(messages[0])) + + detected_package["declared_license_expression"] = self.license1.key + post_data["scan-to-package-license_expression"] = self.license1.key + response = self.client.post(url, post_data, follow=True) + messages = list(response.context["messages"]) + expected = ( + "Values for package_url, license_expression, copyright, primary_language, " + "description, homepage_url, release_date, notice_text assigned to the package." + ) + self.assertEqual(expected, str(messages[0])) + + self.package1.refresh_from_db() + for field_name, form_value in detected_package.items(): + if field_name == "purl": + field_name = "package_url" + elif field_name == "declared_license_expression": + field_name = "license_expression" + instance_value = getattr(self.package1, field_name, None) + self.assertEqual(str(form_value), str(instance_value), msg=field_name) + + history = History.objects.get_for_object(self.package1, action_flag=History.CHANGE).get() + expected = ( + "Changed Package URL, License expression, Copyright, Primary language, Description, " + "Homepage URL, Release date and Notice text." + ) + self.assertEqual(expected, history.get_change_message()) + + response = self.client.post(url, post_data, follow=True) + messages = list(response.context["messages"]) + self.assertEqual("No new values to assign.", str(messages[0])) + + @mock.patch("dejacode_toolkit.scancodeio.ScanCodeIO.fetch_scan_data") + @mock.patch("dejacode_toolkit.scancodeio.ScanCodeIO.fetch_scan_info") + def test_package_details_view_scan_summary_to_package( + self, mock_fetch_scan_info, mock_fetch_scan_data + ): + url = self.package1.get_absolute_url() + self.client.login(username=self.basic_user.username, password="secret") + self.dataspace.enable_package_scanning = True + self.dataspace.save() + + mock_fetch_scan_info.return_value = { + "count": 1, + "results": [ + { + "name": "70265cfaec8cab26f8c88e968e84f23efd886ced663c10bfe408ff20c26a103f", + "url": "/api/projects/f622d852-2d6a-4fb5-ab89-a90db54a4581/", + "uuid": "f622d852-2d6a-4fb5-ab89-a90db54a4581", + } + ], + } + + scan_summary = { + "declared_license_expression": "bsd-new", + "license_clarity_score": { + "score": 90, + "declared_license": True, + "identification_precision": True, + "has_license_text": True, + "declared_copyrights": True, + "conflicting_license_categories": True, + "ambiguous_compound_licensing": True, + }, + "declared_holder": "The Rust Project Developers", + "primary_programming_language": "Rust", + "other_license_expressions": [ + {"value": "apache-2.0", "count": 5}, + {"value": "mit", "count": 3}, + {"value": "unknown-license-reference", "count": 2}, + ], + "other_holders": [ + {"value": None, "count": 211}, + {"value": "The Rust Project", "count": 1}, + ], + "other_programming_languages": [{"value": "C", "count": 1}], + } + + mock_fetch_scan_data.return_value = scan_summary + + expected1 = "#scan-summary-to-package-modal" + expected2 = 'id="scan-summary-to-package-form"' + + self.basic_user.has_perm("component_catalog.change_package") + response = self.client.get(self.package1_tab_scan_url) + self.assertNotContains(response, expected1) + self.assertNotContains(response, expected2) + + add_perm(self.basic_user, "change_package") + response = self.client.get(self.package1_tab_scan_url) + self.assertContains(response, expected1) + self.assertContains(response, expected2) + + post_data = { + "scan-summary-to-package-license_expression": "mit OR apache-2.0", + "scan-summary-to-package-primary_language": "Rust", + "scan-summary-to-package-holder": "The Rust Project Developers", + "submit-scan-summary-to-package-form": "Set values", + } + + response = self.client.post(url, post_data, follow=True) + messages = list(response.context["messages"]) + self.assertIn("Error assigning values to the package.", str(messages[0])) + self.assertIn("Unknown license key(s): mit, apache-2.0", str(messages[0])) + + post_data["scan-summary-to-package-license_expression"] = self.license1.key + response = self.client.post(url, post_data, follow=True) + messages = list(response.context["messages"]) + expected = ( + "Values for license_expression, primary_language, holder assigned to the package." + ) + self.assertEqual(expected, str(messages[0])) + + self.package1.refresh_from_db() + self.assertEqual(self.license1.key, self.package1.license_expression) + self.assertEqual("Rust", self.package1.primary_language) + self.assertEqual("The Rust Project Developers", self.package1.holder) + + history = History.objects.get_for_object(self.package1, action_flag=History.CHANGE).get() + expected = "Changed License expression, Primary language and Holder." + self.assertEqual(expected, history.get_change_message()) + + response = self.client.post(url, post_data, follow=True) + messages = list(response.context["messages"]) + self.assertEqual("No new values to assign.", str(messages[0])) + + @mock.patch("dejacode_toolkit.scancodeio.ScanCodeIO.fetch_scan_data") + @mock.patch("dejacode_toolkit.scancodeio.ScanCodeIO.fetch_scan_info") + def test_package_details_view_scan_summary_to_package_libc( + self, mock_fetch_scan_info, mock_fetch_scan_data + ): + # https://github.com/rust-lang/libc/archive/refs/tags/0.2.121.tar.gz + url = self.package1.get_absolute_url() + self.client.login(username=self.basic_user.username, password="secret") + self.dataspace.enable_package_scanning = True + self.dataspace.save() + + mock_fetch_scan_info.return_value = { + "count": 1, + "results": [ + { + "name": "70265cfaec8cab26f8c88e968e84f23efd886ced663c10bfe408ff20c26a103f", + "url": "/api/projects/f622d852-2d6a-4fb5-ab89-a90db54a4581/", + "uuid": "f622d852-2d6a-4fb5-ab89-a90db54a4581", + } + ], + } + + scan_summary_location = join( + self.testfiles_location, "summary", "libc-0.2.121-scancode.io-summary.json" + ) + with open(scan_summary_location) as f: + scan_summary = json.load(f) + expected_license_expression = scan_summary["declared_license_expression"] + expected_holder = scan_summary["declared_holder"] + expected_primary_language = scan_summary["primary_language"] + + mock_fetch_scan_data.return_value = scan_summary + + response = self.client.get(self.package1_tab_scan_url) + expected_license_expression_html = ( + '' + ' ' + "apache-2.0 OR mit" + ) + expected_holder_html = ( + ' ' + "The Rust Project" + ) + expected_primary_language_html = ( + ' Rust' + ) + self.assertContains(response, expected_license_expression_html, html=True) + self.assertContains(response, expected_holder_html, html=True) + self.assertContains(response, expected_primary_language_html, html=True) + + expected1 = "#scan-summary-to-package-modal" + expected2 = 'id="scan-summary-to-package-form"' + + self.basic_user.has_perm("component_catalog.change_package") + response = self.client.get(url) + self.assertNotContains(response, expected1) + self.assertNotContains(response, expected2) + + add_perm(self.basic_user, "change_package") + response = self.client.get(self.package1_tab_scan_url) + self.assertContains(response, expected1) + self.assertContains(response, expected2) + + post_data = { + "scan-summary-to-package-license_expression": expected_license_expression, + "scan-summary-to-package-primary_language": expected_primary_language, + "scan-summary-to-package-holder": expected_holder, + "submit-scan-summary-to-package-form": "Set values", + } + response = self.client.post(url, post_data, follow=True) + messages = list(response.context["messages"]) + self.assertIn("Error assigning values to the package.", str(messages[0])) + self.assertIn("Unknown license key(s): apache-2.0, mit", str(messages[0])) + + post_data["scan-summary-to-package-license_expression"] = self.license1.key + response = self.client.post(url, post_data, follow=True) + messages = list(response.context["messages"]) + expected = ( + "Values for license_expression, primary_language, holder assigned to the package." + ) + self.assertEqual(expected, str(messages[0])) + + self.package1.refresh_from_db() + self.assertEqual(self.license1.key, self.package1.license_expression) + self.assertEqual(expected_primary_language, self.package1.primary_language) + self.assertEqual(expected_holder, self.package1.holder) + + history = History.objects.get_for_object(self.package1, action_flag=History.CHANGE).get() + expected = "Changed License expression, Primary language and Holder." + self.assertEqual(expected, history.get_change_message()) + + response = self.client.post(url, post_data, follow=True) + messages = list(response.context["messages"]) + self.assertEqual("No new values to assign.", str(messages[0])) + + def test_package_details_view_get_license_expressions_scan_values(self): + field_data = [{"value": "mit OR apache-2.0", "count": None}] + input_type = "checkbox" + license_matches = { + "mit OR apache-2.0": { + "libc-0.2.121/Cargo.toml.orig": [ + { + "license_expression": "mit OR apache-2.0", + "matches": [ + {"license_expression": "mit OR apache-2.0", "matched_text": ""} + ], + } + ] + } + } + + values = PackageTabScanView.get_license_expressions_scan_values( + self.dataspace, field_data, input_type, license_matches + ) + self.assertEqual(1, len(values)) + self.assertIn("MATCH", values[0]) + + @mock.patch("dejacode_toolkit.scancodeio.ScanCodeIO.fetch_scan_list") + def test_scan_list_view(self, mock_fetch_scan_list): + scan_list_url = reverse("component_catalog:scan_list") + project_uuid = "f622d852-2d6a-4fb5-ab89-a90db54a4581" + + mock_fetch_scan_list.return_value = { + "count": 1, + "results": [ + { + "name": "70265cfaec8cab26f8c88e968e84f23efd886ced663c10bfe408ff20c26a103f", + "url": "/api/projects/f622d852-2d6a-4fb5-ab89-a90db54a4581/", + "uuid": project_uuid, + "created_date": "2018-06-21T12:32:17.860882Z", + "input_sources": [ + { + "filename": self.package1.filename, + "source": self.package1.download_url, + } + ], + "runs": [ + { + "status": "success", + } + ], + } + ], + } + + response = self.client.get(scan_list_url) + self.assertRedirects(response, f"/login/?next={scan_list_url}") + + self.client.login(username=self.super_user.username, password="secret") + response = self.client.get(scan_list_url) + self.assertEqual(404, response.status_code) + + self.dataspace.enable_package_scanning = True + self.dataspace.save() + response = self.client.get(scan_list_url) + self.assertContains(response, "Scans: 1 results") + self.assertContains(response, 'Success', html=True) + self.assertContains(response, self.package1.get_absolute_url()) + + expected = 'All' + self.assertContains(response, expected, html=True) + expected = 'Created by me' + self.assertContains(response, expected, html=True) + + self.assertContains(response, 'id="scan-delete-modal"') + delete_url = reverse("component_catalog:scan_delete", args=[project_uuid]) + self.assertContains(response, f'data-delete-url="{delete_url}"') + + @mock.patch("dejacode_toolkit.scancodeio.ScanCodeIO.delete_scan") + @mock.patch("dejacode_toolkit.scancodeio.ScanCodeIO.fetch_scan_list") + def test_delete_scan_view(self, mock_fetch_scan_list, mock_delete_scan): + project_uuid = "348df847-f48f-4ac7-b864-5785b44c65e2" + delete_url = reverse("component_catalog:scan_delete", args=[project_uuid]) + + response = self.client.get(delete_url) + self.assertRedirects(response, f"/login/?next={delete_url}") + + self.client.login(username=self.super_user.username, password="secret") + response = self.client.get(delete_url) + self.assertEqual(404, response.status_code) + + self.dataspace.enable_package_scanning = True + self.dataspace.save() + mock_fetch_scan_list.return_value = None + response = self.client.get(delete_url) + self.assertEqual(404, response.status_code) + + mock_fetch_scan_list.return_value = {"count": 1} + mock_delete_scan.return_value = True + response = self.client.get(delete_url, follow=True) + scan_list_url = reverse("component_catalog:scan_list") + self.assertRedirects(response, scan_list_url) + self.assertContains(response, "Scan deleted.") + + mock_delete_scan.return_value = False + response = self.client.get(delete_url) + self.assertEqual(404, response.status_code) + + @mock.patch("dejacode_toolkit.scancodeio.ScanCodeIO.stream_scan_data") + def test_send_scan_data_as_file_view(self, mock_stream_scan_data): + project_uuid = "348df847-f48f-4ac7-b864-5785b44c65e2" + url = reverse( + "component_catalog:scan_data_as_file", args=[project_uuid, self.package1.filename] + ) + + response = self.client.get(url) + self.assertRedirects(response, f"/login/?next={url}") + + self.client.login(username=self.super_user.username, password="secret") + response = self.client.get(url) + self.assertEqual(404, response.status_code) + + self.dataspace.enable_package_scanning = True + self.dataspace.save() + response = self.client.get(url) + self.assertEqual(200, response.status_code) + self.assertEqual("application/json", response["content-type"]) + self.assertEqual( + 'attachment; filename="package1_scan.json"', response["content-disposition"] + ) + + @mock.patch("requests.head") + @mock.patch("dejacode_toolkit.scancodeio.ScanCodeIO.submit_scan") + def test_scancodeio_submit_scan_task(self, mock_submit_scan, mock_request_head): + user_uuid = self.super_user.uuid + dataspace_uuid = self.super_user.dataspace.uuid + + mock_request_head.side_effect = requests.RequestException + scancodeio_submit_scan(["no_protocol.com"], user_uuid, dataspace_uuid) + self.assertEqual([], mock_submit_scan.mock_calls) + + uris = { + "http://okurl.com": mock.Mock(status_code=200), + "https://okurl2.com": mock.Mock(status_code=200), + "http://private_url.com": mock.Mock(status_code=404), + } + mock_request_head.side_effect = lambda arg, allow_redirects: uris[arg] + scancodeio_submit_scan(list(uris.keys()), user_uuid, dataspace_uuid) + + expected = [ + mock.call("http://okurl.com", user_uuid, dataspace_uuid), + mock.call("https://okurl2.com", user_uuid, dataspace_uuid), + ] + self.assertEqual(expected, mock_submit_scan.mock_calls) + + @mock.patch("requests.sessions.Session.get") + def test_scancodeio_fetch_scan_list(self, mock_session_get): + scancodeio = ScanCodeIO(self.basic_user) + self.assertIsNone(scancodeio.fetch_scan_list()) + self.assertFalse(mock_session_get.called) + + scancodeio.fetch_scan_list(user=self.basic_user) + params = mock_session_get.call_args.kwargs["params"] + expected = {"format": "json", "name__endswith": get_hash_uid(self.basic_user.uuid)} + self.assertEqual(expected, params) + + scancodeio.fetch_scan_list(dataspace=self.basic_user.dataspace) + params = mock_session_get.call_args.kwargs["params"] + expected = { + "format": "json", + "name__contains": get_hash_uid(self.basic_user.dataspace.uuid), + } + self.assertEqual(expected, params) + + scancodeio.fetch_scan_list( + user=self.basic_user, + dataspace=self.basic_user.dataspace, + extra_params="extra", + ) + params = mock_session_get.call_args.kwargs["params"] + expected = { + "format": "json", + "name__contains": get_hash_uid(self.basic_user.dataspace.uuid), + "name__endswith": get_hash_uid(self.basic_user.uuid), + "extra_params": "extra", + } + self.assertEqual(expected, params) + + @mock.patch("requests.sessions.Session.get") + def test_scancodeio_fetch_scan_info(self, mock_session_get): + uri = "https://uri" + scancodeio = ScanCodeIO(self.basic_user) + + scancodeio.fetch_scan_info(uri=uri) + params = mock_session_get.call_args.kwargs["params"] + expected = {"format": "json", "name__startswith": get_hash_uid(uri)} + self.assertEqual(expected, params) + + scancodeio.fetch_scan_info( + uri=uri, + user=self.basic_user, + dataspace=self.basic_user.dataspace, + ) + params = mock_session_get.call_args.kwargs["params"] + expected = { + "format": "json", + "name__startswith": get_hash_uid(uri), + "name__contains": get_hash_uid(self.basic_user.dataspace.uuid), + "name__endswith": get_hash_uid(self.basic_user.uuid), + } + self.assertEqual(expected, params) + + @mock.patch("dejacode_toolkit.scancodeio.ScanCodeIO.request_get") + def test_scancodeio_find_project(self, mock_request_get): + scancodeio = ScanCodeIO(self.basic_user) + scancodeio.find_project(name="project_name") + params = mock_request_get.call_args.kwargs["params"] + expected = {"name": "project_name"} + self.assertEqual(expected, params) + + project_data = { + "name": "project_name", + "url": "/api/projects/f622d852-2d6a-4fb5-ab89-a90db54a4581/", + "uuid": "f622d852-2d6a-4fb5-ab89-a90db54a4581", + } + mock_request_get.return_value = { + "count": 1, + "results": [ + project_data, + ], + } + self.assertEqual(project_data, scancodeio.find_project(name="project_name")) + + mock_request_get.return_value = { + "count": 0, + "results": [], + } + self.assertIsNone(scancodeio.find_project(name="not-existing")) + + mock_request_get.return_value = { + "count": 2, + "results": [ + project_data, + project_data, + ], + } + self.assertIsNone(scancodeio.find_project(name="project_name")) + + @mock.patch("dejacode_toolkit.scancodeio.ScanCodeIO.get_scan_results") + @mock.patch("dejacode_toolkit.scancodeio.ScanCodeIO.fetch_scan_data") + def test_scancodeio_update_from_scan(self, mock_fetch_scan_data, mock_get_scan_results): + scancodeio = ScanCodeIO(self.basic_user) + + mock_get_scan_results.return_value = None + mock_fetch_scan_data.return_value = None + + updated_fields = scancodeio.update_from_scan(self.package1, self.super_user) + self.assertEqual([], updated_fields) + + mock_get_scan_results.return_value = {"url": "https://scancode.io/"} + updated_fields = scancodeio.update_from_scan(self.package1, self.super_user) + self.assertEqual([], updated_fields) + + mock_fetch_scan_data.return_value = {"error": "Summary file not available"} + updated_fields = scancodeio.update_from_scan(self.package1, self.super_user) + self.assertEqual([], updated_fields) + + mock_fetch_scan_data.return_value = { + "declared_license_expression": "mit", + "declared_holder": "Jeremy Thomas", + "primary_language": "JavaScript", + "key_files_packages": [ + { + "purl": "pkg:npm/bulma@0.9.4", + "type": "npm", + "namespace": "", + "name": "bulma", + "version": "0.9.4", + "qualifiers": "", + "subpath": "", + "primary_language": "JavaScript_from_package", + "description": "Modern CSS framework", + "release_date": None, + "homepage_url": "https://bulma.io", + "bug_tracking_url": "https://github.com/jgthms/bulma/issues", + "code_view_url": "", + "vcs_url": "git+https://github.com/jgthms/bulma.git", + "copyright": "", + "license_expression": "mit", + "notice_text": "", + "dependencies": [], + "keywords": ["css", "sass", "flexbox", "responsive", "framework"], + } + ], + } + updated_fields = scancodeio.update_from_scan(self.package1, self.super_user) + expected = ["holder", "primary_language", "description", "homepage_url", "copyright"] + self.assertEqual(expected, updated_fields) + + self.package1.refresh_from_db() + self.assertEqual("Jeremy Thomas", self.package1.holder) + self.assertEqual("JavaScript_from_package", self.package1.primary_language) + self.assertEqual("Modern CSS framework", self.package1.description) + self.assertEqual("https://bulma.io", self.package1.homepage_url) + self.assertEqual("Copyright Jeremy Thomas", self.package1.copyright) + # expected_keywords = ['css', 'sass', 'flexbox', 'responsive', 'framework'] + # self.assertEqual(expected_keywords, self.package1.keywords) + + self.assertEqual(self.super_user, self.package1.last_modified_by) + history_entry = History.objects.get_for_object(self.package1).get() + expected = ( + "Automatically updated holder, primary_language, description, " + "homepage_url, copyright from scan results" + ) + self.assertEqual(expected, history_entry.change_message) + + # Inferred Copyright statement + mock_fetch_scan_data.return_value = {"key_files_packages": [{"name": "package1"}]} + self.package1.copyright = "" + self.package1.save() + updated_fields = scancodeio.update_from_scan(self.package1, self.super_user) + self.assertEqual(["copyright"], updated_fields) + self.package1.refresh_from_db() + self.assertEqual("Copyright package1 project contributors", self.package1.copyright) + + mock_fetch_scan_data.return_value = {"some_key": "some_value"} + self.package1.name = "bulma" + self.package1.copyright = "" + self.package1.save() + updated_fields = scancodeio.update_from_scan(self.package1, self.super_user) + self.assertEqual(["copyright"], updated_fields) + self.package1.refresh_from_db() + self.assertEqual("Copyright bulma project contributors", self.package1.copyright) + + def test_scancodeio_map_detected_package_data(self): + detected_package = { + "purl": "pkg:maven/aopalliance/aopalliance@1.0", + "primary_language": "Java", + "declared_license_expression": "mit AND mit", + "other_license_expression": "apache-20 AND apache-20", + "keywords": [ + "json", + "Development Status :: 5 - Production/Stable", + "Operating System :: OS Independent", + ], + # skipped, no values + "description": "", + # skipped, not a SCAN_PACKAGE_FIELD + "is_key_file": 1, + } + + expected = { + "package_url": "pkg:maven/aopalliance/aopalliance@1.0", + "purl": "pkg:maven/aopalliance/aopalliance@1.0", + "license_expression": "mit", + "primary_language": "Java", + # 'keywords': [ + # 'json', + # 'Development Status :: 5 - Production/Stable', + # 'Operating System :: OS Independent', + # ], + } + mapped_data = ScanCodeIO.map_detected_package_data(detected_package) + self.assertEqual(expected, mapped_data) + + @mock.patch("dejacode_toolkit.scancodeio.ScanCodeIO.request_get") + def test_scancodeio_fetch_project_packages(self, mock_request_get): + scancodeio = ScanCodeIO(self.basic_user) + + mock_request_get.return_value = None + with self.assertRaises(Exception): + scancodeio.fetch_project_packages(project_uuid="abcd") + + mock_request_get.return_value = { + "next": None, + "results": ["p1", "p2"], + } + packages = scancodeio.fetch_project_packages(project_uuid="abcd") + self.assertEqual(["p1", "p2"], packages) + + @mock.patch("dejacode_toolkit.vulnerablecode.VulnerableCode.get_vulnerable_purls") + @mock.patch("dejacode_toolkit.vulnerablecode.VulnerableCode.is_configured") + def test_package_list_view_vulnerabilities(self, mock_is_configured, mock_vulnerable_purls): + purl = "pkg:pypi/django@2.1" + mock_is_configured.return_value = True + + self.package1.set_package_url(purl) + self.package1.save() + + self.dataspace.enable_vulnerablecodedb_access = True + self.dataspace.save() + + mock_vulnerable_purls.return_value = [purl] + + self.client.login(username=self.super_user.username, password="secret") + package_list_url = reverse("component_catalog:package_list") + response = self.client.get(package_list_url) + + self.assertContains(response, self.package1.identifier) + self.assertContains(response, "#vulnerabilities") + expected = '' + self.assertContains(response, expected, html=True) + + def test_package_details_view_get_vulnerability_fields(self): + self.package1.set_package_url("pkg:nginx/nginx@1.11.1") + self.package1.save() + copy_object(self.package1, Dataspace.objects.create(name="Other"), self.basic_user) + + get_vulnerability_fields = PackageDetailsView.get_vulnerability_fields + fields = get_vulnerability_fields(vulnerability={}, dataspace=self.dataspace) + self.assertEqual(fields[0], ("Summary", None, "Summary of the vulnerability")) + + vulnerability = { + "vulnerability_id": "42d0a7c4-99e9-4506-b0c6-338ec2993147", + "summary": "SQL Injection", + "references": [ + { + "reference_id": "", + "reference_url": "http://www.openwall.com/lists/oss-security/2022/01/18/4", + "scores": [], + }, + { + "reference_id": "CVE-2022-23305", + "reference_url": "https://nvd.nist.gov/vuln/detail/CVE-2022-23305", + "scores": [], + }, + ], + "fixed_packages": [ + {"purl": "pkg:nginx/nginx@1.11.1"}, + {"purl": "pkg:nginx/nginx@1.10.1"}, + ], + } + fields = get_vulnerability_fields( + vulnerability=vulnerability, + dataspace=self.dataspace, + ) + self.assertEqual(fields[0], ("Summary", "SQL Injection", "Summary of the vulnerability")) + self.assertEqual(fields[1][0], "Fixed packages") + fixed_package_values = fields[1][1] + self.assertIn("nginx/nginx@1.10.1", fixed_package_values) + self.assertIn( + 'nginx/nginx@1.11.1', + fixed_package_values, + ) + self.assertEqual( + fields[2][0:2], + ( + "Reference IDs", + '' + "CVE-2022-23305" + "", + ), + ) + self.assertEqual( + fields[3][0:2], + ( + "Reference URLs", + 'http://www.openwall.com/lists/oss-security/2022/01/18/4', + ), + ) + + @mock.patch("dejacode_toolkit.vulnerablecode.VulnerableCode.get_vulnerabilities_by_purl") + @mock.patch("dejacode_toolkit.vulnerablecode.VulnerableCode.is_configured") + def test_package_details_view_tab_vulnerabilities( + self, mock_is_configured, mock_get_vulnerabilities_by_purl + ): + purl = "pkg:pypi/django@2.1" + mock_is_configured.return_value = True + + self.package1.set_package_url(purl) + self.package1.save() + + self.dataspace.enable_vulnerablecodedb_access = True + self.dataspace.save() + + mock_get_vulnerabilities_by_purl.return_value = [ + { + "purl": "pkg:pypi/django@2.1", + "affected_by_vulnerabilities": [ + { + "summary": "SQL Injection", + "references": [ + { + "reference_id": "CVE-2022-23305", + "reference_url": "https://nvd.nist.gov/vuln/detail/CVE-2022-23305", + } + ], + }, + ], + } + ] + + self.client.login(username=self.super_user.username, password="secret") + response = self.client.get(self.package1.details_url) + + expected = ( + '" + ) + + values.append(f'{html_content}') + + return values + + @staticmethod + def license_clarity_fields(license_clarity_score): + if not license_clarity_score: + return [] + + field_context = { + "pretitle": "License Clarity", + "help_text": "License Clarity is a set of metrics that indicate how clearly, " + "comprehensively and accurately a software project has defined " + "and communicated the licensing that applies to the software.", + } + tab_fields = [ + (None, field_context, None, "includes/field_pretitle.html"), + ] + + table = [] + for label, field, help_text, weight in ScanCodeIO.LICENSE_CLARITY_FIELDS: + field_value = license_clarity_score.get(field, "") + td_class = "text-center" + th_class = None + + if field_value is True: + badge_color = "text-bg-success" if weight.startswith("+") else "text-bg-danger" + value = f'{weight}' + + elif field == "score": + td_class += " bg-light" + th_class = "bg-light" + badge_color = "text-bg-primary" + value = f'{field_value}' + + else: + value = "" + + table.append( + { + "label": label, + "value": format_html(value), + "help_text": help_text, + "td_class": td_class, + "th_class": th_class, + } + ) + + tab_fields.append((None, table, None, "includes/field_table.html")) + return tab_fields + + @staticmethod + def get_key_file_summary(key_file_data): + dt = '
        {}
        ' + dd = '
        {}
        ' + summary = "" + + for label, field, value_key in ScanCodeIO.KEY_FILE_DETECTION_FIELDS: + values = key_file_data.get(field) + if not values: + continue + + if isinstance(values, list): + if isinstance(values[0], dict): + values = [value.get(value_key) for value in values] + else: + values = [values] + + values = [ + f'{escape(value)} {count}' + for value, count in Counter(values).items() + ] + summary += dt.format(label) + summary += dd.format("\n".join(values)) + + return summary + + def key_files_fields(self, key_files): + if not key_files: + return [] + + field_context = { + "pretitle": "Key files", + "help_text": "Key files are top-level codebase files such as " + "COPYING, README and package manifests", + } + + for key_file in key_files: + key_file["summary"] = self.get_key_file_summary(key_file) + + # Inject the matched values as a grouped list for usage in the template + # Limit the matched_text to 300 chars to prevent rendering issues. + MATCHED_MAX_LENGTH = 300 + license_detections = key_file.get("license_detections", []) + + matched_texts = [] + for detection_data in license_detections: + for match in detection_data.get("matches"): + matched_text = match.get("matched_text") + if matched_text and len(matched_text) < MATCHED_MAX_LENGTH: + matched_texts.append(matched_text) + + key_file_fields = [ + ("copyrights", "copyright"), + ("holders", "holder"), + ("authors", "author"), + ] + for field, value_field in key_file_fields: + matched_texts += [ + escape(entry.get(value_field)) + for entry in key_file.get(field, []) + if len(entry.get(value_field)) < MATCHED_MAX_LENGTH + ] + + key_file["matched_texts"] = matched_texts + + if detected_license_expression := key_file["detected_license_expression"]: + licensing, show_policy = self._get_licensing_for_formatted_render( + self.object.dataspace, [{"value": detected_license_expression}] + ) + key_file["formatted_expression"] = get_formatted_expression( + licensing, + detected_license_expression, + show_policy, + show_category=True, + ) + + tab_fields = [ + (None, field_context, None, "includes/field_pretitle.html"), + ("", key_files, None, "component_catalog/tabs/field_key_files_table.html"), + ] + + return tab_fields + + def github_repo_url_field(self): + github_repo_url = self.object.github_repo_url + if github_repo_url: + field_context = { + "href": github_repo_url, + "target": "_blank", + "icon_class": "fas fa-external-link-alt", + "btn_class": "btn-sm float-end", + } + return "View code repository", field_context, None, "includes/field_button.html" + + def scan_detected_package_fields(self, key_files_packages): + user = self.request.user + has_change_package_permission = user.has_perm("component_catalog.change_package") + detected_package_fields = [] + + for detected_package in key_files_packages[:1]: # Only 1 package entry is supported + has_package_values = any( + [field[1] in detected_package for field in ScanCodeIO.SCAN_PACKAGE_FIELD] + ) + if not has_package_values: + continue + + right_button_data_target = "" + if has_change_package_permission: + right_button_data_target = "#scan-to-package-modal" + + field_context = { + "pretitle": "Detected Package", + "help_text": ( + "A normalized top-level package manifest, if available, that contains " + "package metadata such as a name, version, etc.\n" + "Package manifests include files such as npm's package.json, " + "Apache Maven pom.xml, RPM or Debian metadata, etc. " + "that contain structured information about a package." + ), + "right_button_data_target": right_button_data_target, + } + detected_package_fields.append( + (None, field_context, None, "includes/field_pretitle.html") + ) + + self.detected_package_data = ScanCodeIO.map_detected_package_data(detected_package) + + # Add the supported Package fields in the tab UI. + for label, scan_field in ScanCodeIO.SCAN_PACKAGE_FIELD: + if scan_field == "declared_license_expression": + scan_field = "license_expression" + value = self.detected_package_data.get(scan_field) + if value: + if isinstance(value, list): + value = format_html("
        ".join(([escape(entry) for entry in value]))) + else: + value = escape(value) + detected_package_fields.append((label, value)) + + detected_package_fields.append(FieldSeparator) + + return detected_package_fields + + def scan_summary_fields(self, scan_summary): + scan_summary_fields = [] + user = self.request.user + has_change_package_permission = user.has_perm("component_catalog.change_package") + + right_button_data_target = "" + if has_change_package_permission: + right_button_data_target = "#scan-summary-to-package-modal" + + summary_pretitle_context = { + "pretitle": "Scan Summary", + "help_text": ( + "A top-level summary of the collected scanned data such as license expressions, " + "copyright statements and copyright holders.\n" + "For each value the summary has a count of the occurrences of a data item." + ), + "right_button_data_target": right_button_data_target, + } + + summary_fields = ScanCodeIO.SCAN_SUMMARY_FIELDS + has_summary_values = any(field[1] in scan_summary for field in summary_fields) + if not has_summary_values: + return + + scan_summary_fields.append( + (None, summary_pretitle_context, None, "includes/field_pretitle.html") + ) + license_matches = scan_summary.get("license_matches") or {} + self.object.has_license_matches = bool(license_matches) + + for label, field, model_field_name, input_type in summary_fields: + field_data = scan_summary.get(field, []) + if field in ("declared_license_expression", "other_license_expressions"): + if field == "declared_license_expression": + field_data = [{"value": field_data, "count": None}] + checked = True + else: + checked = False + values = self.get_license_expressions_scan_values( + user.dataspace, field_data, input_type, license_matches, checked + ) + + elif field in ("declared_holder", "primary_language"): + values = [] + if field_data: + values.append( + f' {escape(field_data)}' + ) + + else: + values = [ + ( + f'' + f' {escape(entry.get("value"))}' + f' {entry.get("count")}' + f"" + ) + for entry in field_data + if entry.get("value") + ] + + scan_summary_fields.append((label, format_html("\n".join(values)))) + + scan_summary_fields.append(FieldSeparator) + return scan_summary_fields + + @staticmethod + def readable_date(date): + if date: + return date_format(parse_datetime(date), "N j, Y, f A T") + + def scan_status_fields(self, scan): + scan_status_fields = [] + scan_run = scan.get("runs", [{}])[-1] + status = scan_run.get("status") + issue_statuses = ["failure", "stale", "stopped"] + completed_statuses = ["success", *issue_statuses] + + scan_issue_request_template = settings.SCAN_ISSUE_REQUEST_TEMPLATE + dataspace_name = self.object.dataspace.name + request_template_uuid = scan_issue_request_template.get(dataspace_name) + if request_template_uuid and status in completed_statuses: + request_form_url = reverse("workflow:request_add", args=[request_template_uuid]) + field_context = { + "href": f"{request_form_url}?content_object_id={self.object.id}", + "target": "_blank", + "btn_class": "btn-outline-request", + "icon_class": "fas fa-bug", + } + scan_status_fields.append( + ("Report Scan Issues", field_context, None, "includes/field_button.html") + ) + + scan_status_fields.extend( + [ + ("Status", f"Scan {status}"), + ("Created date", self.readable_date(scan_run.get("created_date"))), + ("Start date", self.readable_date(scan_run.get("task_start_date"))), + ("End date", self.readable_date(scan_run.get("task_end_date"))), + ("ScanCode.io version", scan_run.get("scancodeio_version")), + ] + ) + + if status in issue_statuses: + log = scan_run.get("log") + if log: + scan_status_fields.append(("Log", log, None, "includes/field_log.html")) + task_output = scan_run.get("task_output") + if task_output: + scan_status_fields.append( + ("Task output", task_output, None, "includes/field_log.html") + ) + + if status == "success": + filename = self.object.filename or self.object.package_url_filename + field_context = { + "href": reverse( + "component_catalog:scan_data_as_file", + args=[scan.get("uuid"), quote_plus(filename)], + ), + "target": "_blank", + "icon_class": "fas fa-download", + } + scan_status_fields.append( + ("Download Scan data", field_context, None, "includes/field_button.html") + ) + + return scan_status_fields + + def tab_scan(self): + user = self.request.user + scancodeio = ScanCodeIO(user) + + scan = scancodeio.get_scan_results( + download_url=self.object.download_url, + dataspace=user.dataspace, + ) + + if not scan: + return + + summary_url = scan.get("url").split("?")[0] + "summary/" + scan_summary = scancodeio.fetch_scan_data(summary_url) + + tab_fields = [] + + github_repo_url_field = self.github_repo_url_field() + if github_repo_url_field: + tab_fields.append(github_repo_url_field) + + if scan_summary: + license_clarity_score = scan_summary.get("license_clarity_score") + license_clarity_fields = self.license_clarity_fields(license_clarity_score) + tab_fields.extend(license_clarity_fields) + + key_files = scan_summary.get("key_files") + key_files_fields = self.key_files_fields(key_files) + if key_files_fields: + tab_fields.extend(key_files_fields) + + if key_files_packages := scan_summary.get("key_files_packages", []): + tab_fields.extend(self.scan_detected_package_fields(key_files_packages)) + + scan_summary_fields = self.scan_summary_fields(scan_summary) + if scan_summary_fields: + tab_fields.extend(scan_summary_fields) + + scan_status_fields = self.scan_status_fields(scan) + if scan_status_fields: + tab_fields.extend(scan_status_fields) + + return {"fields": tab_fields} + + +class PackageTabPurlDBView(AcceptAnonymousMixin, TabContentView): + model = Package + slug_url_kwarg = "uuid" + template_name = "tabs/tab_content.html" + + def get_context_data(self, **kwargs): + context_data = super().get_context_data(**kwargs) + user = self.request.user + + if not user.dataspace.enable_purldb_access: + raise Http404("PurlDB access is not enabled.") + + if tab_fields := self.get_tab_fields(): + context_data["tab_context"] = normalize_tab_fields(tab_fields) + else: + alert_context = { + "message": "No entries found in the PurlDB for this package.", + "full_width": True, + "alert_class": "alert-light disable-tab", + } + tab_fields = [("", alert_context, None, "includes/field_alert.html")] + context_data["tab_context"] = {"fields": tab_fields} + + return context_data + + def get_tab_fields(self): + from purldb.views import get_purldb_tab_fields + + purldb_entries = self.object.get_purldb_entries( + user=self.request.user, + max_request_call=1, + timeout=5, + ) + if not purldb_entries: + return + + tab_fields = [] + + alert_context = { + "message": ( + "You are looking at the details for this software package as defined " + "in the PurlDB which was mined and scanned automatically from a public " + "source." + ), + "full_width": True, + "alert_class": "alert-primary", + } + tab_fields.append(("", alert_context, None, "includes/field_alert.html")) + + if len(purldb_entries) > 1: + alert_context = { + "message": "There are multiple entries in the PurlDB for this Package.", + "full_width": True, + "alert_class": "alert-warning", + } + tab_fields.append(("", alert_context, None, "includes/field_alert.html")) + + user = self.request.user + for purldb_entry in purldb_entries: + tab_fields.extend(get_purldb_tab_fields(purldb_entry, user.dataspace)) + + return {"fields": tab_fields} diff --git a/component_catalog/widgets.py b/component_catalog/widgets.py new file mode 100644 index 00000000..849fcba6 --- /dev/null +++ b/component_catalog/widgets.py @@ -0,0 +1,29 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# DejaCode is a trademark of nexB Inc. +# SPDX-License-Identifier: AGPL-3.0-only +# See https://github.com/nexB/dejacode for support or download. +# See https://aboutcode.org for more information about AboutCode FOSS projects. +# + +from django.contrib.admin.widgets import RelatedFieldWidgetWrapper +from django.utils.html import format_html + + +class UsagePolicyWidgetWrapper(RelatedFieldWidgetWrapper): + def __init__(self, *args, **kwargs): + self.object_instance = kwargs.pop("object_instance") + super().__init__(*args, **kwargs) + + def render(self, name, value, *args, **kwargs): + rendered = super().render(name, value, *args, **kwargs) + + value_from_license = "" + if self.object_instance and self.object_instance.policy_from_primary_license: + value_from_license = format_html( + '
        Value from primary license {}: {}
        ', + self.object_instance.primary_license, + self.object_instance.policy_from_primary_license, + ) + + return rendered + value_from_license diff --git a/data/postgresql/cc-by-sa-4.0.LICENSE b/data/postgresql/cc-by-sa-4.0.LICENSE new file mode 100644 index 00000000..eea12e95 --- /dev/null +++ b/data/postgresql/cc-by-sa-4.0.LICENSE @@ -0,0 +1,428 @@ +Attribution-ShareAlike 4.0 International + +======================================================================= + +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + +======================================================================= + +Creative Commons Attribution-ShareAlike 4.0 International Public +License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution-ShareAlike 4.0 International Public License ("Public +License"). To the extent this Public License may be interpreted as a +contract, You are granted the Licensed Rights in consideration of Your +acceptance of these terms and conditions, and the Licensor grants You +such rights in consideration of benefits the Licensor receives from +making the Licensed Material available under these terms and +conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. + + c. BY-SA Compatible License means a license listed at + creativecommons.org/compatiblelicenses, approved by Creative + Commons as essentially the equivalent of this Public License. + + d. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + e. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + f. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + g. License Elements means the license attributes listed in the name + of a Creative Commons Public License. The License Elements of this + Public License are Attribution and ShareAlike. + + h. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + i. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + j. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + k. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + l. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + m. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part; and + + b. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. Additional offer from the Licensor -- Adapted Material. + Every recipient of Adapted Material from You + automatically receives an offer from the Licensor to + exercise the Licensed Rights in the Adapted Material + under the conditions of the Adapter's License You apply. + + c. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified + form), You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + b. ShareAlike. + + In addition to the conditions in Section 3(a), if You Share + Adapted Material You produce, the following conditions also apply. + + 1. The Adapter's License You apply must be a Creative Commons + license with the same License Elements, this version or + later, or a BY-SA Compatible License. + + 2. You must include the text of, or the URI or hyperlink to, the + Adapter's License You apply. You may satisfy this condition + in any reasonable manner based on the medium, means, and + context in which You Share Adapted Material. + + 3. You may not offer or impose any additional or different terms + or conditions on, or apply any Effective Technological + Measures to, Adapted Material that restrict exercise of the + rights granted under the Adapter's License You apply. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material, + including for purposes of Section 3(b); and + + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + + +======================================================================= + +Creative Commons is not a party to its public +licenses. Notwithstanding, Creative Commons may elect to apply one of +its public licenses to material it publishes and in those instances +will be considered the “Licensor.†The text of the Creative Commons +public licenses is dedicated to the public domain under the CC0 Public +Domain Dedication. Except for the limited purpose of indicating that +material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the +public licenses. + +Creative Commons may be contacted at creativecommons.org. + diff --git a/data/postgresql/initdb.sql.gz b/data/postgresql/initdb.sql.gz new file mode 100644 index 00000000..349f256b Binary files /dev/null and b/data/postgresql/initdb.sql.gz differ diff --git a/data/postgresql/initdb.sql.gz.ABOUT b/data/postgresql/initdb.sql.gz.ABOUT new file mode 100644 index 00000000..9860bf74 --- /dev/null +++ b/data/postgresql/initdb.sql.gz.ABOUT @@ -0,0 +1,7 @@ +about_resource: initdb.sql.gz +name: initdb.sql.gz +license_expression: cc-by-sa-4.0 +copyright: Copyright (c) nexB Inc. +attribute: yes +license_file: cc-by-sa-4.0.LICENSE +notes: these are sample, seed data for the DejaCode reference dataspace. \ No newline at end of file diff --git a/dejacode/__init__.py b/dejacode/__init__.py new file mode 100644 index 00000000..f3c43ab6 --- /dev/null +++ b/dejacode/__init__.py @@ -0,0 +1,24 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# DejaCode is a trademark of nexB Inc. +# SPDX-License-Identifier: AGPL-3.0-only +# See https://github.com/nexB/dejacode for support or download. +# See https://aboutcode.org for more information about AboutCode FOSS projects. +# + +import os +import sys + +from dejacode.celery import app as celery_app + +VERSION = "5.0.0" +__version__ = VERSION +__all__ = ["celery_app"] + + +def command_line(): + """Command line entry point.""" + from django.core.management import execute_from_command_line + + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dejacode.settings") + execute_from_command_line(sys.argv) diff --git a/dejacode/celery.py b/dejacode/celery.py new file mode 100644 index 00000000..7ef9e0a6 --- /dev/null +++ b/dejacode/celery.py @@ -0,0 +1,23 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# DejaCode is a trademark of nexB Inc. +# SPDX-License-Identifier: AGPL-3.0-only +# See https://github.com/nexB/dejacode for support or download. +# See https://aboutcode.org for more information about AboutCode FOSS projects. +# + +import os + +from celery import Celery + +# set the default Django settings module for the 'celery' program. +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dejacode.settings") + +app = Celery("dejacode") + +# Using a string here means the worker doesn't have to serialize +# the configuration object to child processes. +# - namespace='CELERY' means all celery-related configuration keys +# should have a `CELERY_` prefix. +app.config_from_object("django.conf:settings", namespace="CELERY") +app.autodiscover_tasks() diff --git a/dejacode/formats/__init__.py b/dejacode/formats/__init__.py new file mode 100644 index 00000000..7aa35de2 --- /dev/null +++ b/dejacode/formats/__init__.py @@ -0,0 +1,7 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# DejaCode is a trademark of nexB Inc. +# SPDX-License-Identifier: AGPL-3.0-only +# See https://github.com/nexB/dejacode for support or download. +# See https://aboutcode.org for more information about AboutCode FOSS projects. +# diff --git a/dejacode/formats/cu/__init__.py b/dejacode/formats/cu/__init__.py new file mode 100644 index 00000000..7aa35de2 --- /dev/null +++ b/dejacode/formats/cu/__init__.py @@ -0,0 +1,7 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# DejaCode is a trademark of nexB Inc. +# SPDX-License-Identifier: AGPL-3.0-only +# See https://github.com/nexB/dejacode for support or download. +# See https://aboutcode.org for more information about AboutCode FOSS projects. +# diff --git a/dejacode/formats/cu/formats.py b/dejacode/formats/cu/formats.py new file mode 100644 index 00000000..797a19a0 --- /dev/null +++ b/dejacode/formats/cu/formats.py @@ -0,0 +1,10 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# DejaCode is a trademark of nexB Inc. +# SPDX-License-Identifier: AGPL-3.0-only +# See https://github.com/nexB/dejacode for support or download. +# See https://aboutcode.org for more information about AboutCode FOSS projects. +# + +DATE_FORMAT = "N d, Y" +DATETIME_FORMAT = "N d, Y, h:i:s a T" diff --git a/dejacode/formats/en/__init__.py b/dejacode/formats/en/__init__.py new file mode 100644 index 00000000..7aa35de2 --- /dev/null +++ b/dejacode/formats/en/__init__.py @@ -0,0 +1,7 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# DejaCode is a trademark of nexB Inc. +# SPDX-License-Identifier: AGPL-3.0-only +# See https://github.com/nexB/dejacode for support or download. +# See https://aboutcode.org for more information about AboutCode FOSS projects. +# diff --git a/dejacode/formats/en/formats.py b/dejacode/formats/en/formats.py new file mode 100644 index 00000000..797a19a0 --- /dev/null +++ b/dejacode/formats/en/formats.py @@ -0,0 +1,10 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# DejaCode is a trademark of nexB Inc. +# SPDX-License-Identifier: AGPL-3.0-only +# See https://github.com/nexB/dejacode for support or download. +# See https://aboutcode.org for more information about AboutCode FOSS projects. +# + +DATE_FORMAT = "N d, Y" +DATETIME_FORMAT = "N d, Y, h:i:s a T" diff --git a/dejacode/localization/README b/dejacode/localization/README new file mode 100644 index 00000000..0f3933f7 --- /dev/null +++ b/dejacode/localization/README @@ -0,0 +1,10 @@ +Localization +============ + +Compilation of .mo files +------------------------ + +This requires "gettext" to be installed. For more details see: +https://docs.djangoproject.com/en/dev/ref/django-admin/#compilemessages + + $ ./manage.py compilemessages diff --git a/dejacode/localization/en/LC_MESSAGES/django.mo b/dejacode/localization/en/LC_MESSAGES/django.mo new file mode 100644 index 00000000..70cfda24 Binary files /dev/null and b/dejacode/localization/en/LC_MESSAGES/django.mo differ diff --git a/dejacode/localization/en/LC_MESSAGES/django.po b/dejacode/localization/en/LC_MESSAGES/django.po new file mode 100644 index 00000000..5fcc3e39 --- /dev/null +++ b/dejacode/localization/en/LC_MESSAGES/django.po @@ -0,0 +1,28 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# DejaCode is a trademark of nexB Inc. +# SPDX-License-Identifier: AGPL-3.0-only +# See https://github.com/nexB/dejacode for support or download. +# See https://aboutcode.org for more information about AboutCode FOSS projects. +# + +# WARNING: The following header is required by translation system +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-12-22 12:36-0800\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "Home" +msgstr "Administration" + +# Placeholers +msgid "copyright_placeholder" +msgstr "" diff --git a/dejacode/localization/en_GB/LC_MESSAGES/django.mo b/dejacode/localization/en_GB/LC_MESSAGES/django.mo new file mode 100644 index 00000000..9804c3f2 Binary files /dev/null and b/dejacode/localization/en_GB/LC_MESSAGES/django.mo differ diff --git a/dejacode/localization/en_GB/LC_MESSAGES/django.po b/dejacode/localization/en_GB/LC_MESSAGES/django.po new file mode 100644 index 00000000..707e4181 --- /dev/null +++ b/dejacode/localization/en_GB/LC_MESSAGES/django.po @@ -0,0 +1,41 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# DejaCode is a trademark of nexB Inc. +# SPDX-License-Identifier: AGPL-3.0-only +# See https://github.com/nexB/dejacode for support or download. +# See https://aboutcode.org for more information about AboutCode FOSS projects. +# + +# WARNING: The following header is required by translation system +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-12-22 12:36-0800\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +# Wording translation +msgid "Return to list" +msgstr "Cancel" + +msgid "license profile" +msgstr "attribution type" + +msgid "License profile" +msgstr "Attribution type" + +msgid "License Profile" +msgstr "Attribution Type" + +msgid "license profile assigned tag" +msgstr "attribution type assigned tag" + +# Placeholers +msgid "copyright_placeholder" +msgstr "NOTE: At this time the copyright notice for this component is not cataloged, please consult the source code for this component to determine the copyright notice, which is required to meet Attribution obligations." diff --git a/dejacode/settings.py b/dejacode/settings.py new file mode 100644 index 00000000..d72d1b66 --- /dev/null +++ b/dejacode/settings.py @@ -0,0 +1,714 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# DejaCode is a trademark of nexB Inc. +# SPDX-License-Identifier: AGPL-3.0-only +# See https://github.com/nexB/dejacode for support or download. +# See https://aboutcode.org for more information about AboutCode FOSS projects. +# + +# Common Django settings for all deployments of DejaCode + +import sys +from pathlib import Path + +import environ + +# The home directory of the dejacode user that owns the installation. +PROJECT_DIR = environ.Path(__file__) - 1 +ROOT_DIR = PROJECT_DIR - 1 + +# Environment +ENV_FILE = "/etc/dejacode/.env" +if not Path(ENV_FILE).exists(): + ENV_FILE = ROOT_DIR(".env") + +env = environ.Env() +environ.Env.read_env(ENV_FILE) # Reading the .env file into os.environ + +# Security +SECRET_KEY = env.str("SECRET_KEY") +ALLOWED_HOSTS = env.list( + "ALLOWED_HOSTS", + default=[".localhost", "127.0.0.1", "[::1]", "host.docker.internal"], +) +CSRF_TRUSTED_ORIGINS = env.list("CSRF_TRUSTED_ORIGINS", default=[]) +# SECURITY WARNING: don't run with debug turned on in production +DEBUG = env.bool("DEJACODE_DEBUG", default=False) + +# True if running tests through `./manage test` +IS_TESTS = "test" in sys.argv + +TEST_RUNNER = "dje.tests.DejaCodeTestRunner" + +DEJACODE_LOG_LEVEL = env.str("DEJACODE_LOG_LEVEL", "INFO") + +# A list of all the people who get code error notifications. +# Example: DEJACODE_ADMINS="Blake , Alice Judge " +ADMINS = env.list("DEJACODE_ADMINS", default=[]) +MANAGERS = ADMINS + +# Database settings +DATABASES = { + "default": { + "ENGINE": env.str("DATABASE_ENGINE", "django.db.backends.postgresql"), + "HOST": env.str("DATABASE_HOST", "localhost"), + "PORT": env.str("DATABASE_PORT", "5432"), + "NAME": env.str("DATABASE_NAME", "dejacode_db"), + "USER": env.str("DATABASE_USER", default="dejacode"), + "PASSWORD": env.str("DATABASE_PASSWORD", default=""), + "ATOMIC_REQUESTS": True, + "CONN_MAX_AGE": 600, # 10min lifetime connection + } +} + +# Allow Anonymous users to access @accept_anonymous views with the `public` data. +ANONYMOUS_USERS_DATASPACE = env.str("ANONYMOUS_USERS_DATASPACE", default=None) + +# guardian: ANONYMOUS_USER_NAME is set to None, anonymous user object +# permissions-are disabled. +ANONYMOUS_USER_NAME = env.str("ANONYMOUS_USER_NAME", default=None) + +# An administrative User in the Reference Dataspace can see and copy data from +# every Dataspace; otherwise, the User can only see data from +# his assigned Dataspace and copy from the Reference Dataspace. +# The default Reference Dataspace is always 'nexB' unless the following +# setting is set to another existing Dataspace. +# If set to an empty value or a non-existing Dataspace, 'nexB' will be +# considered the Reference Dataspace. +REFERENCE_DATASPACE = env.str("REFERENCE_DATASPACE", default="nexB") + +# Enable the CloneDataset view using the following dataspace as reference. +TEMPLATE_DATASPACE = env.str("TEMPLATE_DATASPACE", default=None) + +# Local time zone for this installation. Choices can be found here: +# https://en.wikipedia.org/wiki/List_of_tz_zones_by_name +# although not all choices may be available on all operating systems. +# On Unix systems, a value of None will cause Django to use the same +# timezone as the operating system. +TIME_ZONE = env.str("TIME_ZONE", default="US/Pacific") + +SITE_ID = env.int("SITE_ID", default=1) +SITE_URL = env.str("SITE_URL", default="") +ENABLE_SELF_REGISTRATION = env.bool("ENABLE_SELF_REGISTRATION", default=False) + +# This instructs the browser to only send these cookies over HTTPS connections. +# Note that this will mean that sessions will not work over HTTP, and the CSRF +# protection will prevent any POST data being accepted over HTTP +# (which is fine as we are redirecting all HTTP traffic to HTTPS). +SESSION_COOKIE_SECURE = env.bool("SESSION_COOKIE_SECURE", default=False) +CSRF_COOKIE_SECURE = env.bool("CSRF_COOKIE_SECURE", default=False) + +# DejaCode User Model +AUTH_USER_MODEL = "dje.DejacodeUser" + + +# This is defined here as a do-nothing function because we can't import +# django.utils.translation -- that module depends on the settings. +def gettext_noop(s): + return s + + +# Available Language code for this installation. +# Add support for a custom translation using the 'en-gb' one, as the +# language locale needs to exist in django/conf/locale/ to avoid an IOError. +LANGUAGES = [ + ("en", gettext_noop("English")), + ("en-gb", gettext_noop("Custom English")), +] + +# Set the language for this installation. More choices can be found here: +LANGUAGE_CODE = env.str("LANGUAGE_CODE", default="en") + +# If you set this to False, Django will make some optimizations so as not +# to load the internationalization machinery. +USE_I18N = env.bool("USE_I18N", default=True) + +# Using 'locale' for the directory name in the project folder clashes with +# Python standard library module and raise an ImportWarning. +LOCALE_PATHS = [ + PROJECT_DIR("localization"), +] + +# A full Python path to a Python package that contains format definitions for +# project locales. If not None, Django will check for a formats.py file, under +# the directory named as the current locale, and will use the formats defined +# on this file. +FORMAT_MODULE_PATH = "dejacode.formats" + +# Absolute path to the directory static files should be collected to. +# Hardcoded to avoid any path resolution issues, especially with symlinks. +STATIC_ROOT = env.str("STATIC_ROOT", default="var/dejacode/static/") + +# URL prefix for static files. +STATIC_URL = env.str("STATIC_URL", default="/static/") + +# This setting defines the additional locations the staticfiles app will traverse if the +# FileSystemFinder finder is enabled, e.g. if you use the collectstatic or findstatic +# management command or use the static file serving view. +STATICFILES_DIRS = [ + PROJECT_DIR("static"), +] + +# Absolute filesystem path to the directory that will hold user-uploaded files. +# Hardcoded to avoid any path resolution issues, especially with symlinks. +# Note that we do not rely on the MEDIA_URL system to send files. +MEDIA_ROOT = env.str("MEDIA_ROOT", default="var/dejacode/media/") + +# File upload +MAX_UPLOAD_SIZE = env.int("MAX_UPLOAD_SIZE", default=31457280) # 30M +FILE_UPLOAD_PERMISSIONS = 0o644 # -rw-rw-r-- + +# https://docs.djangoproject.com/en/dev/ref/settings/#data-upload-max-number-fields +# Set to 10 times the default value (1,000) +DATA_UPLOAD_MAX_NUMBER_FIELDS = 10000 + +# hCaptcha script location for registration form +HCAPTCHA_JS_API_URL = env.str("HCAPTCHA_JS_API_URL", default="/static/js/hcaptcha.js") + +EXTRA_MIDDLEWARE = env.list("EXTRA_MIDDLEWARE", default=[]) + +MIDDLEWARE = [ + # GZipMiddleware should be placed before any other middleware that need to + # read or write the response body so that compression happens afterward. + "django.middleware.gzip.GZipMiddleware", + "django.middleware.security.SecurityMiddleware", + "dje.middleware.ProhibitInQueryStringMiddleware", + "django.middleware.http.ConditionalGetMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + # OTPMiddleware needs to come after AuthenticationMiddleware + "django_otp.middleware.OTPMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "dje.middleware.LastAPIAccessMiddleware", + *EXTRA_MIDDLEWARE, + # AxesMiddleware should be the last middleware in the MIDDLEWARE list. + # It only formats user lockout messages and renders Axes lockout responses + # on failed user authentication attempts from login views. + # If you do not want Axes to override the authentication response + # you can skip installing the middleware and use your own views. + "axes.middleware.AxesMiddleware", +] + +# Security +SECURE_CONTENT_TYPE_NOSNIFF = env.bool("SECURE_CONTENT_TYPE_NOSNIFF", default=True) +SECURE_CROSS_ORIGIN_OPENER_POLICY = env.str( + "SECURE_CROSS_ORIGIN_OPENER_POLICY", default="same-origin" +) + +X_FRAME_OPTIONS = "DENY" +# Note: The CSRF_COOKIE_HTTPONLY cannot be activated yet without breaking all +# the AJAX (POST, PUT, etc..) requests, like the annotation system for example. +# It will be required to configure the following: +# https://docs.djangoproject.com/en/dev/ref/csrf/ +# CSRF_COOKIE_HTTPONLY = True +# Also, security.W004 SECURE_HSTS_SECONDS and security.W008 SECURE_SSL_REDIRECT +# are handled at the web server level. +SILENCED_SYSTEM_CHECKS = ["security.W004", "security.W008", "security.W017", "urls.W005"] + +# Set the following to True to enable ClamAV scan on uploaded files +# This requires the installation of ClamAV +# See https://www.clamav.net/documents/installing-clamav +# Also, you need to set the StreamMaxLength value in /etc/clamav/clamd.conf +# bigger than the MAX_UPLOAD_SIZE setting. +CLAMD_ENABLED = env.bool("CLAMD_ENABLED", default=False) +# Use ClamdNetworkSocket in place of ClamdUnixSocket +CLAMD_USE_TCP = env.bool("CLAMD_USE_TCP", default=True) +CLAMD_TCP_ADDR = env.str("CLAMD_TCP_ADDR", default="127.0.0.1") + +# The following password validation apply to 3 the password entry locations: +# - Change password: /account/password_change/ +# - Password reset: /account/password_reset/ +# - User registration: /account/register/ +# Notes: CommonPasswordValidator is not useful since none of the password in that list contain +# a special character +AUTH_PASSWORD_VALIDATORS = [ + { + "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", + }, + { + "NAME": "dje.validators.SpecialCharacterPasswordValidator", + }, + { + "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", + }, +] + +AUTHENTICATION_BACKENDS = env.list( + "AUTHENTICATION_BACKENDS", + default=[ + "django.contrib.auth.backends.ModelBackend", + ], +) + +# AxesBackend should be the first backend in the AUTHENTICATION_BACKENDS list. +AUTHENTICATION_BACKENDS = [ + "axes.backends.AxesBackend", + *AUTHENTICATION_BACKENDS, + "guardian.backends.ObjectPermissionBackend", +] + +ROOT_URLCONF = "dejacode.urls" + +WSGI_APPLICATION = "dejacode.wsgi.application" + +SECURE_PROXY_SSL_HEADER = env.tuple( + "SECURE_PROXY_SSL_HEADER", default=("HTTP_X_FORWARDED_PROTO", "https") +) + +# Using named URL patterns to reduce configuration duplication and URL +# translation compatibility. +LOGIN_URL = env.str("LOGIN_URL", default="login") +LOGIN_REDIRECT_URL = env.str("LOGIN_REDIRECT_URL", default="home") + +TEMPLATES = [ + { + "BACKEND": "django.template.backends.django.DjangoTemplates", + "APP_DIRS": True, + "OPTIONS": { + "debug": DEBUG, + "context_processors": [ + "django.contrib.auth.context_processors.auth", + "django.template.context_processors.debug", + "django.template.context_processors.i18n", + "django.template.context_processors.media", + "django.template.context_processors.static", + "django.template.context_processors.request", + "django.contrib.messages.context_processors.messages", + "dje.context_processors.dejacode_context", + "dje.client_data.client_data_context_processor", + ], + }, + } +] + +# The order of INSTALLED_APPS is significant! +# Django will look for a template in the INSTALLED_APP order and will use the +# one it finds first. +# Therefore, all templates overrides regarding the admin should be located in +# 'dje' app template/ folder. +# Also, 'dje' must come before 'grappelli', and 'grappelli' before +# 'django.contrib.admin'. +# On the other hand, the management commands will override each others if the +# app is declared after another in the list. +PREREQ_APPS = [ + "dje", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + "django.contrib.sitemaps", + "django.contrib.sites", + "django.contrib.humanize", + "django.contrib.postgres", + "grappelli.dashboard", + "grappelli", + "django.contrib.admin", + "rest_framework", + "rest_framework.authtoken", + "crispy_forms", + "crispy_bootstrap5", + "guardian", + "django_filters", + "rest_hooks", + "notifications", + "axes", + "django_otp", + "django_otp.plugins.otp_totp", + "hcaptcha_field", +] + +PROJECT_APPS = [ + "organization", + "license_library", + "component_catalog", + "product_portfolio", + "workflow", + "reporting", + "purldb", + "policy", + "notification", +] + +EXTRA_APPS = env.list("EXTRA_APPS", default=[]) + +INSTALLED_APPS = PREREQ_APPS + PROJECT_APPS + EXTRA_APPS + +SITE_TITLE = env.str("SITE_TITLE", default="DejaCode") +HEADER_TEMPLATE = env.str("HEADER_TEMPLATE", default="includes/header.html") +FOOTER_TEMPLATE = env.str("FOOTER_TEMPLATE", default="includes/footer.html") + +GRAPPELLI_INDEX_DASHBOARD = "dje.dashboard.DejaCodeDashboard" +GRAPPELLI_CLEAN_INPUT_TYPES = False + +FAVICON_HREF = env.str("FAVICON_HREF", default=f"{STATIC_URL}img/favicon.ico") + +CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5" +CRISPY_TEMPLATE_PACK = "bootstrap5" + +# Instead of sending out real emails the console backend just writes the emails +# that would be sent to the standard output. +EMAIL_BACKEND = env.str("EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend") +EMAIL_HOST = env.str("EMAIL_HOST", default="") +EMAIL_HOST_USER = env.str("EMAIL_HOST_USER", default="") +EMAIL_HOST_PASSWORD = env.str("EMAIL_HOST_PASSWORD", default="") +EMAIL_PORT = env.int("EMAIL_PORT", default=587) +DEFAULT_FROM_EMAIL = env.str("DEFAULT_FROM_EMAIL", default="") +EMAIL_USE_TLS = env.bool("EMAIL_USE_TLS", default=True) + +# Enable this setting to display a "Tools" section in the navbar including +# links to the "Requests" and "Reporting" views. +SHOW_TOOLS_IN_NAV = env.bool("SHOW_TOOLS_IN_NAV", default=True) + +# Set False to hide the "Product Portfolio" section in the navbar. +SHOW_PP_IN_NAV = env.bool("SHOW_PP_IN_NAV", default=True) + +# An integer specifying how many objects should be displayed per page. +PAGINATE_BY = env.int("PAGINATE_BY", default=None) + +ADMIN_FORMS_CONFIGURATION = env.dict("ADMIN_FORMS_CONFIGURATION", default={}) + +# Location of the changelog file +CHANGELOG_PATH = ROOT_DIR("CHANGELOG.rst") + +# Display a "Report Scan Issues" button in Scan tab +# Format: "dataspace_name=request_template_UUID," +SCAN_ISSUE_REQUEST_TEMPLATE = env.dict("SCAN_ISSUE_REQUEST_TEMPLATE", default={}) + +# https://docs.djangoproject.com/en/dev/topics/http/sessions/ +SESSION_ENGINE = "django.contrib.sessions.backends.cached_db" +SESSION_EXPIRE_AT_BROWSER_CLOSE = env.bool("SESSION_EXPIRE_AT_BROWSER_CLOSE", default=False) +SESSION_COOKIE_AGE = env.int("SESSION_COOKIE_AGE", default=28800) # 8 hours +# Prevent from erasing a session of another Django app when running multiple `runserver` +# instances side by side on the on same host (different ports). +SESSION_COOKIE_NAME = "dejacode_sessionid" + +# Removed the "django.core.files.uploadhandler.MemoryFileUploadHandler", +# to force every file to be uploaded to the temp folder, even if smaller than +# FILE_UPLOAD_MAX_MEMORY_SIZE +FILE_UPLOAD_HANDLERS = [ + "django.core.files.uploadhandler.TemporaryFileUploadHandler", +] + +REDIS_URL = env.str("REDIS_URL", default="redis://127.0.0.1:6379") + +# Default setup for the cache +# See https://docs.djangoproject.com/en/dev/topics/cache/ +# Set CACHE_BACKEND="django.core.cache.backends.locmem.LocMemCache" in dev mode +CACHE_BACKEND = env.str("CACHE_BACKEND", default="django.core.cache.backends.redis.RedisCache") +CACHES = { + "default": { + "BACKEND": CACHE_BACKEND, + "LOCATION": REDIS_URL, + "TIMEOUT": 900, # 15 minutes, in seconds + }, + "vulnerabilities": { + "BACKEND": CACHE_BACKEND, + "LOCATION": REDIS_URL, + "TIMEOUT": 3600, # 1 hour, in seconds + "KEY_PREFIX": "vuln", + }, +} + +# https://docs.djangoproject.com/en/dev/topics/logging/#configuring-logging +LOGGING = { + "version": 1, + "disable_existing_loggers": True, + "formatters": { + "verbose": { + "format": "%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s" + }, + "simple": {"format": "%(levelname)s %(message)s"}, + "django.server": { + "()": "django.utils.log.ServerFormatter", + "format": "[%(server_time)s] %(message)s", + }, + }, + "filters": {"require_debug_false": {"()": "django.utils.log.RequireDebugFalse"}}, + "handlers": { + "null": { + "class": "logging.NullHandler", + }, + "console": { + "class": "logging.StreamHandler", + "formatter": "simple", + }, + "mail_admins": { + "level": "ERROR", + "filters": ["require_debug_false"], + "class": "django.utils.log.AdminEmailHandler", + }, + "django.server": { + "level": "DEBUG", + "class": "logging.StreamHandler", + "formatter": "django.server", + }, + # "dejacode.api": { + # "level": "INFO", + # "class": "logging.FileHandler", + # "filename": "/var/dejacode/log/dejacode_api.log", + # }, + }, + "loggers": { + "django": { + "handlers": ["null"] if IS_TESTS else ["console"], + "propagate": True, + "level": DEJACODE_LOG_LEVEL, + }, + # This is required to send the email notification on ERRORS. + "django.request": { + "handlers": ["mail_admins"], + "level": "ERROR", + "propagate": True, + }, + # Set DEJACODE_LOG_LEVEL=DEBUG to display all SQL queries in the console. + "django.db.backends": { + "level": DEJACODE_LOG_LEVEL, + }, + "dje": { + "handlers": ["null"] if IS_TESTS else ["console"], + "propagate": False, + "level": DEJACODE_LOG_LEVEL, + }, + "dje.tasks": { + "handlers": ["null"] if IS_TESTS else ["console"], + "propagate": False, + "level": DEJACODE_LOG_LEVEL, + }, + # "dejacode.api": { + # "handlers": ["dejacode.api"], + # "propagate": False, + # "level": "INFO", + # }, + "dejacode_toolkit": { + "handlers": ["null"] if IS_TESTS else ["console"], + "propagate": False, + "level": DEJACODE_LOG_LEVEL, + }, + "django_auth_ldap": { + "handlers": ["null"] if IS_TESTS else ["console"], + "propagate": False, + "level": DEJACODE_LOG_LEVEL, + }, + }, +} + +# https://django-grappelli.readthedocs.org/en/latest/customization.html#autocomplete-lookups +GRAPPELLI_AUTOCOMPLETE_SEARCH_FIELDS = { + "license_library": { + "license": [ + "key__icontains", + "name__icontains", + "short_name__icontains", + ], + }, + "organization": { + "owner": ["name__icontains"], + }, + "component_catalog": { + "component": ["name_version__icontains"], + "package": [ + "filename__icontains", + "type_name_version__icontains", + ], + }, + "product_portfolio": { + "product": ["name_version__icontains"], + "codebaseresource": ["path__icontains"], + "productcomponent": ["component__name__icontains"], + "productpackage": [ + "package__filename__icontains", + "package__download_url__icontains", + ], + }, +} + +REST_API_RATE_THROTTLE = env.str("REST_API_RATE_THROTTLE", default="2/second") + +REST_FRAMEWORK = { + "DEFAULT_AUTHENTICATION_CLASSES": ( + "rest_framework.authentication.SessionAuthentication", + "rest_framework.authentication.TokenAuthentication", + ), + "DEFAULT_PERMISSION_CLASSES": ( + "rest_framework.permissions.IsAuthenticated", + "rest_framework.permissions.DjangoModelPermissions", + ), + "DEFAULT_FILTER_BACKENDS": ( + "django_filters.rest_framework.DjangoFilterBackend", + "dje.api_custom.DJEOrderingFilter", + "dje.api_custom.DJESearchFilter", + ), + "DEFAULT_RENDERER_CLASSES": ( + "rest_framework.renderers.JSONRenderer", + "dje.api_custom.DJEBrowsableAPIRenderer", + ), + "DEFAULT_THROTTLE_CLASSES": [ + "rest_framework.throttling.UserRateThrottle", + ], + "DEFAULT_THROTTLE_RATES": { + "user": REST_API_RATE_THROTTLE, + }, + "DEFAULT_PAGINATION_CLASS": "dje.api_custom.PageSizePagination", + "DEFAULT_SCHEMA_CLASS": "rest_framework.schemas.coreapi.AutoSchema", + "VIEW_NAME_FUNCTION": "dje.api_custom.get_view_name", + "URL_FIELD_NAME": "api_url", # Default 'url' used as a field on the Package model +} + +# django-registration +# The registration is only available on dejacode.com instances +# Although, this setting and registration views are needed for the user creation. +ACCOUNT_ACTIVATION_DAYS = 10 + +# https://github.com/zapier/django-rest-hooks +HOOK_FINDER = "notification.models.find_and_fire_hook" +HOOK_DELIVERER = "notification.tasks.deliver_hook_wrapper" +HOOK_EVENTS = { + # 'any.event.name': 'App.Model.Action' (created/updated/deleted) + "request.added": "workflow.Request.created+", + "request.updated": "workflow.Request.updated+", + "request_comment.added": "workflow.RequestComment.created+", + "user.added_or_updated": None, + "user.locked_out": None, +} +# Provide context variables to the `Webhook` values such as `extra_headers`. +HOOK_ENV = env.dict("HOOK_ENV", default={}) + +# Django-axes +# Enable or disable Axes plugin functionality +AXES_ENABLED = env.bool("AXES_ENABLED", default=False) +# The integer number of login attempts allowed before the user is locked out. +AXES_FAILURE_LIMIT = env.int("AXES_FAILURE_LIMIT", default=5) +# If set, specifies a template to render when a user is locked out. +AXES_LOCKOUT_TEMPLATE = env.str("AXES_LOCKOUT_TEMPLATE", default="axes_lockout.html") +# If True, only lock based on username, and never lock based on IP +# if attempts to exceed the limit. +AXES_ONLY_USER_FAILURES = True +# If True, a successful login will reset the number of failed logins. +AXES_RESET_ON_SUCCESS = True +# If True, disable writing login and logout access logs to database, +# so the admin interface will not have user login trail for successful user +# authentication. +AXES_DISABLE_ACCESS_LOG = True + +# Celery +CELERY_BROKER_URL = env.str("CELERY_BROKER_URL", default="redis://") +CELERY_TASK_ALWAYS_EAGER = env.bool("CELERY_TASK_ALWAYS_EAGER", default=False) + +# 2FA with django-otp +OTP_TOTP_ISSUER = "DejaCode" + +# https://docs.djangoproject.com/en/dev/releases/3.2/#customizing-type-of-auto-created-primary-keys +DEFAULT_AUTO_FIELD = "django.db.models.AutoField" + +DATASPACE_DOMAIN_REDIRECT = env.dict("DATASPACE_DOMAIN_REDIRECT", default={}) +LOGIN_FORM_ALERT = env.str("LOGIN_FORM_ALERT", default="") + +# Debug toolbar +DEBUG_TOOLBAR = env.bool("DEJACODE_DEBUG_TOOLBAR", default=False) +if DEBUG and DEBUG_TOOLBAR: + INSTALLED_APPS.append("debug_toolbar") + MIDDLEWARE.append("debug_toolbar.middleware.DebugToolbarMiddleware") + INTERNAL_IPS = ["127.0.0.1"] + +if IS_TESTS: + CELERY_TASK_ALWAYS_EAGER = True + CELERY_EAGER_PROPAGATES_EXCEPTIONS = True + # Silent the django-axes logging during tests + LOGGING["loggers"].update({"axes": {"handlers": ["null"]}}) + # Set a faster hashing algorithm for running the tests + # https://docs.djangoproject.com/en/dev/topics/testing/overview/#password-hashing + PASSWORD_HASHERS = [ + "django.contrib.auth.hashers.MD5PasswordHasher", + ] + # Disabled the migration system when running tests to save time + # DATABASES["default"].update({"TEST": {"MIGRATE": False}}) + # High throttle rate when running tests + REST_FRAMEWORK["DEFAULT_THROTTLE_RATES"]["user"] = "1000/second" + + +# LDAP Configuration + +import ldap +from django_auth_ldap.config import GroupOfNamesType +from django_auth_ldap.config import LDAPSearch + +# This authentication backend enables users to authenticate against an +# LDAP server. +# To enable LDAP Authentication, first, set the following in your .env +# and provide values for the following settings. +# AUTHENTICATION_BACKENDS=dje.ldap_backend.DejaCodeLDAPBackend + +# The URI of the LDAP server. "ldap://ldap.server.com:389" +AUTH_LDAP_SERVER_URI = env.str("AUTH_LDAP_SERVER_URI", default="") + +# By default, LDAP connections are unencrypted. +# If you need a secure connection to the LDAP server, you can either use an +# 'ldaps://' URI or enable the StartTLS extension. +AUTH_LDAP_START_TLS = env.bool("AUTH_LDAP_START_TLS", default=True) + +# AUTH_LDAP_BIND_DN and AUTH_LDAP_BIND_PASSWORD should be set with the +# distinguished name and password to use when binding to the LDAP server. +# Use empty strings (the default) for an anonymous bind. +AUTH_LDAP_BIND_DN = env.str("AUTH_LDAP_BIND_DN", default="") +AUTH_LDAP_BIND_PASSWORD = env.str("AUTH_LDAP_BIND_PASSWORD", default="") + +# The following setting is required to locate a user in the LDAP directory. +# The filter parameter should contain the placeholder %(user)s for the username. +# It must return exactly one result for authentication to succeed. +# The distinguished name of the search base. +# AUTH_LDAP_USER_DN="ou=users,dc=example,dc=com" +AUTH_LDAP_USER_DN = env.str("AUTH_LDAP_USER_DN", default="") +# An optional filter string (e.g. ‘(objectClass=person)’). +# In order to be valid, filterstr must be enclosed in parentheses. +# AUTH_LDAP_USER_FILTERSTR="(uid=%(user)s)" +AUTH_LDAP_USER_FILTERSTR = env.str("AUTH_LDAP_USER_FILTERSTR", default="") + +AUTH_LDAP_USER_SEARCH = LDAPSearch(AUTH_LDAP_USER_DN, ldap.SCOPE_SUBTREE, AUTH_LDAP_USER_FILTERSTR) + +# When AUTH_LDAP_AUTOCREATE_USER is True (default), a new DejaCode user will be +# created in the database with the minimum permission (a read-only user). +# Enabling this setting also requires a valid dataspace name for the +# AUTH_LDAP_DATASPACE setting (see below). +# Set AUTH_LDAP_AUTOCREATE_USER to False in order to limit authentication to +# users that already exist in the database only, in which case new users must be +# manually created by a DejaCode administrator using the application. +AUTH_LDAP_AUTOCREATE_USER = env.bool("AUTH_LDAP_AUTOCREATE_USER", default=True) + +# The following value is required when AUTH_LDAP_AUTOCREATE_USER is True. +# New DejaCode users created on the first LDAP authentication will be located in +# this Dataspace. +AUTH_LDAP_DATASPACE = env.str("AUTH_LDAP_DATASPACE", default="") + +# AUTH_LDAP_USER_ATTR_MAP is used to copy LDAP directory information into +# DejaCode user objects, at creation time (see AUTH_LDAP_AUTOCREATE_USER) or +# during updates (see AUTH_LDAP_ALWAYS_UPDATE_USER). +# This dictionary maps DejaCode user fields to (case-insensitive) LDAP attribute +# names. +# AUTH_LDAP_USER_ATTR_MAP=first_name=givenName,last_name=sn,email=mail +AUTH_LDAP_USER_ATTR_MAP = env.dict("AUTH_LDAP_USER_ATTR_MAP", default={}) + +# By default, all mapped user fields will be updated each time the user logs in. +# To disable this, set AUTH_LDAP_ALWAYS_UPDATE_USER to False. +AUTH_LDAP_ALWAYS_UPDATE_USER = env.bool("AUTH_LDAP_ALWAYS_UPDATE_USER", default=True) + +# To associate LDAP groups and DejaCode groups: +# 1. Create the DejaCode groups and associate permissions through the DejaCode +# admin interface. From the Admin dashboard: Administration > Groups. +# 2. Enable the following settings to enable LDAP groups retrieval. +# Set the proper AUTH_LDAP_GROUP_DN and AUTH_LDAP_GROUP_FILTERSTR values matching +# for your LDAP configuration. +AUTH_LDAP_FIND_GROUP_PERMS = env.bool("AUTH_LDAP_FIND_GROUP_PERMS", default=False) +# AUTH_LDAP_GROUP_DN="ou=groups,dc=example,dc=com" +AUTH_LDAP_GROUP_DN = env.str("AUTH_LDAP_GROUP_DN", default="") +# In order to be valid, filterstr must be enclosed in parentheses. +# AUTH_LDAP_GROUP_FILTERSTR="(objectClass=groupOfNames)" +AUTH_LDAP_GROUP_FILTERSTR = env.str("AUTH_LDAP_GROUP_FILTERSTR", default="") + +AUTH_LDAP_GROUP_TYPE = GroupOfNamesType() +AUTH_LDAP_GROUP_SEARCH = LDAPSearch( + AUTH_LDAP_GROUP_DN, ldap.SCOPE_SUBTREE, AUTH_LDAP_GROUP_FILTERSTR +) diff --git a/dejacode/static/README.rst b/dejacode/static/README.rst new file mode 100644 index 00000000..7581e1f5 --- /dev/null +++ b/dejacode/static/README.rst @@ -0,0 +1,3 @@ +This directory contains the runtime version of various JavaScript and +CSS packages used throughout the UI. The original source code, together with +origin and license documentation are available in the thirdparty/ directory. \ No newline at end of file diff --git a/dejacode/static/awesomplete/awesomplete-1.1.5.css b/dejacode/static/awesomplete/awesomplete-1.1.5.css new file mode 100644 index 00000000..696dd2fa --- /dev/null +++ b/dejacode/static/awesomplete/awesomplete-1.1.5.css @@ -0,0 +1,103 @@ +.awesomplete [hidden] { + display: none; +} + +.awesomplete .visually-hidden { + position: absolute; + clip: rect(0, 0, 0, 0); +} + +.awesomplete { + display: inline-block; + position: relative; +} + +.awesomplete > input { + display: block; +} + +.awesomplete > ul { + position: absolute; + left: 0; + z-index: 1; + min-width: 100%; + box-sizing: border-box; + list-style: none; + padding: 0; + margin: 0; + background: #fff; +} + +.awesomplete > ul:empty { + display: none; +} + +.awesomplete > ul { + border-radius: .3em; + margin: .2em 0 0; + background: hsla(0,0%,100%,.9); + background: linear-gradient(to bottom right, white, hsla(0,0%,100%,.8)); + border: 1px solid rgba(0,0,0,.3); + box-shadow: .05em .2em .6em rgba(0,0,0,.2); + text-shadow: none; +} + +@supports (transform: scale(0)) { + .awesomplete > ul { + transition: .3s cubic-bezier(.4,.2,.5,1.4); + transform-origin: 1.43em -.43em; + } + + .awesomplete > ul[hidden], + .awesomplete > ul:empty { + opacity: 0; + transform: scale(0); + display: block; + transition-timing-function: ease; + } +} + + /* Pointer */ + .awesomplete > ul:before { + content: ""; + position: absolute; + top: -.43em; + left: 1em; + width: 0; height: 0; + padding: .4em; + background: white; + border: inherit; + border-right: 0; + border-bottom: 0; + -webkit-transform: rotate(45deg); + transform: rotate(45deg); + } + + .awesomplete > ul > li { + position: relative; + padding: .2em .5em; + cursor: pointer; + } + + .awesomplete > ul > li:hover { + background: hsl(200, 40%, 80%); + color: black; + } + + .awesomplete > ul > li[aria-selected="true"] { + background: hsl(205, 40%, 40%); + color: white; + } + + .awesomplete mark { + background: hsl(65, 100%, 50%); + } + + .awesomplete li:hover mark { + background: hsl(68, 100%, 41%); + } + + .awesomplete li[aria-selected="true"] mark { + background: hsl(86, 100%, 21%); + color: inherit; + } \ No newline at end of file diff --git a/dejacode/static/awesomplete/awesomplete-1.1.5.min.js b/dejacode/static/awesomplete/awesomplete-1.1.5.min.js new file mode 100644 index 00000000..ccaf6b07 --- /dev/null +++ b/dejacode/static/awesomplete/awesomplete-1.1.5.min.js @@ -0,0 +1,2 @@ +// Awesomplete - Lea Verou - MIT license +!function(){function t(t){var e=Array.isArray(t)?{label:t[0],value:t[1]}:"object"==typeof t&&"label"in t&&"value"in t?t:{label:t,value:t};this.label=e.label||e.value,this.value=e.value}function e(t,e,i){for(var n in e){var s=e[n],r=t.input.getAttribute("data-"+n.toLowerCase());"number"==typeof s?t[n]=parseInt(r):!1===s?t[n]=null!==r:s instanceof Function?t[n]=null:t[n]=r,t[n]||0===t[n]||(t[n]=n in i?i[n]:s)}}function i(t,e){return"string"==typeof t?(e||document).querySelector(t):t||null}function n(t,e){return o.call((e||document).querySelectorAll(t))}function s(){n("input.awesomplete").forEach(function(t){new r(t)})}var r=function(t,n){var s=this;r.count=(r.count||0)+1,this.count=r.count,this.isOpened=!1,this.input=i(t),this.input.setAttribute("autocomplete","off"),this.input.setAttribute("aria-expanded","false"),this.input.setAttribute("aria-owns","awesomplete_list_"+this.count),this.input.setAttribute("role","combobox"),this.options=n=n||{},e(this,{minChars:2,maxItems:10,autoFirst:!1,data:r.DATA,filter:r.FILTER_CONTAINS,sort:!1!==n.sort&&r.SORT_BYLENGTH,container:r.CONTAINER,item:r.ITEM,replace:r.REPLACE,tabSelect:!1},n),this.index=-1,this.container=this.container(t),this.ul=i.create("ul",{hidden:"hidden",role:"listbox",id:"awesomplete_list_"+this.count,inside:this.container}),this.status=i.create("span",{className:"visually-hidden",role:"status","aria-live":"assertive","aria-atomic":!0,inside:this.container,textContent:0!=this.minChars?"Type "+this.minChars+" or more characters for results.":"Begin typing for results."}),this._events={input:{input:this.evaluate.bind(this),blur:this.close.bind(this,{reason:"blur"}),keydown:function(t){var e=t.keyCode;s.opened&&(13===e&&s.selected?(t.preventDefault(),s.select()):9===e&&s.selected&&s.tabSelect?s.select():27===e?s.close({reason:"esc"}):38!==e&&40!==e||(t.preventDefault(),s[38===e?"previous":"next"]()))}},form:{submit:this.close.bind(this,{reason:"submit"})},ul:{mousedown:function(t){t.preventDefault()},click:function(t){var e=t.target;if(e!==this){for(;e&&!/li/i.test(e.nodeName);)e=e.parentNode;e&&0===t.button&&(t.preventDefault(),s.select(e,t.target))}}}},i.bind(this.input,this._events.input),i.bind(this.input.form,this._events.form),i.bind(this.ul,this._events.ul),this.input.hasAttribute("list")?(this.list="#"+this.input.getAttribute("list"),this.input.removeAttribute("list")):this.list=this.input.getAttribute("data-list")||n.list||[],r.all.push(this)};r.prototype={set list(t){if(Array.isArray(t))this._list=t;else if("string"==typeof t&&t.indexOf(",")>-1)this._list=t.split(/\s*,\s*/);else if((t=i(t))&&t.children){var e=[];o.apply(t.children).forEach(function(t){if(!t.disabled){var i=t.textContent.trim(),n=t.value||i,s=t.label||i;""!==n&&e.push({label:s,value:n})}}),this._list=e}document.activeElement===this.input&&this.evaluate()},get selected(){return this.index>-1},get opened(){return this.isOpened},close:function(t){this.opened&&(this.input.setAttribute("aria-expanded","false"),this.ul.setAttribute("hidden",""),this.isOpened=!1,this.index=-1,this.status.setAttribute("hidden",""),i.fire(this.input,"awesomplete-close",t||{}))},open:function(){this.input.setAttribute("aria-expanded","true"),this.ul.removeAttribute("hidden"),this.isOpened=!0,this.status.removeAttribute("hidden"),this.autoFirst&&-1===this.index&&this.goto(0),i.fire(this.input,"awesomplete-open")},destroy:function(){if(i.unbind(this.input,this._events.input),i.unbind(this.input.form,this._events.form),!this.options.container){var t=this.container.parentNode;t.insertBefore(this.input,this.container),t.removeChild(this.container)}this.input.removeAttribute("autocomplete"),this.input.removeAttribute("aria-autocomplete");var e=r.all.indexOf(this);-1!==e&&r.all.splice(e,1)},next:function(){var t=this.ul.children.length;this.goto(this.index-1&&e.length>0&&(e[t].setAttribute("aria-selected","true"),this.status.textContent=e[t].textContent+", list item "+(t+1)+" of "+e.length,this.input.setAttribute("aria-activedescendant",this.ul.id+"_item_"+this.index),this.ul.scrollTop=e[t].offsetTop-this.ul.clientHeight+e[t].clientHeight,i.fire(this.input,"awesomplete-highlight",{text:this.suggestions[this.index]}))},select:function(t,e){if(t?this.index=i.siblingIndex(t):t=this.ul.children[this.index],t){var n=this.suggestions[this.index];i.fire(this.input,"awesomplete-select",{text:n,origin:e||t})&&(this.replace(n),this.close({reason:"select"}),i.fire(this.input,"awesomplete-selectcomplete",{text:n}))}},evaluate:function(){var e=this,i=this.input.value;i.length>=this.minChars&&this._list&&this._list.length>0?(this.index=-1,this.ul.innerHTML="",this.suggestions=this._list.map(function(n){return new t(e.data(n,i))}).filter(function(t){return e.filter(t,i)}),!1!==this.sort&&(this.suggestions=this.suggestions.sort(this.sort)),this.suggestions=this.suggestions.slice(0,this.maxItems),this.suggestions.forEach(function(t,n){e.ul.appendChild(e.item(t,i,n))}),0===this.ul.children.length?(this.status.textContent="No results found",this.close({reason:"nomatches"})):(this.open(),this.status.textContent=this.ul.children.length+" results found")):(this.close({reason:"nomatches"}),this.status.textContent="No results found")}},r.all=[],r.FILTER_CONTAINS=function(t,e){return RegExp(i.regExpEscape(e.trim()),"i").test(t)},r.FILTER_STARTSWITH=function(t,e){return RegExp("^"+i.regExpEscape(e.trim()),"i").test(t)},r.SORT_BYLENGTH=function(t,e){return t.length!==e.length?t.length-e.length:t$&"),role:"option","aria-selected":"false",id:"awesomplete_list_"+this.count+"_item_"+n})},r.REPLACE=function(t){this.input.value=t.value},r.DATA=function(t){return t},Object.defineProperty(t.prototype=Object.create(String.prototype),"length",{get:function(){return this.label.length}}),t.prototype.toString=t.prototype.valueOf=function(){return""+this.label};var o=Array.prototype.slice;i.create=function(t,e){var n=document.createElement(t);for(var s in e){var r=e[s];if("inside"===s)i(r).appendChild(n);else if("around"===s){var o=i(r);o.parentNode.insertBefore(n,o),n.appendChild(o),null!=o.getAttribute("autofocus")&&o.focus()}else s in n?n[s]=r:n.setAttribute(s,r)}return n},i.bind=function(t,e){if(t)for(var i in e){var n=e[i];i.split(/\s+/).forEach(function(e){t.addEventListener(e,n)})}},i.unbind=function(t,e){if(t)for(var i in e){var n=e[i];i.split(/\s+/).forEach(function(e){t.removeEventListener(e,n)})}},i.fire=function(t,e,i){var n=document.createEvent("HTMLEvents");n.initEvent(e,!0,!0);for(var s in i)n[s]=i[s];return t.dispatchEvent(n)},i.regExpEscape=function(t){return t.replace(/[-\\^$*+?.()|[\]{}]/g,"\\$&")},i.siblingIndex=function(t){for(var e=0;t=t.previousElementSibling;e++);return e},"undefined"!=typeof self&&(self.Awesomplete=r),"undefined"!=typeof Document&&("loading"!==document.readyState?s():document.addEventListener("DOMContentLoaded",s)),r.$=i,r.$$=n,"object"==typeof module&&module.exports&&(module.exports=r)}(); diff --git a/dejacode/static/bootstrap-5.3.2/css/bootstrap.min.css b/dejacode/static/bootstrap-5.3.2/css/bootstrap.min.css new file mode 100644 index 00000000..f5910acc --- /dev/null +++ b/dejacode/static/bootstrap-5.3.2/css/bootstrap.min.css @@ -0,0 +1,6 @@ +@charset "UTF-8";/*! + * Bootstrap v5.3.2 (https://getbootstrap.com/) + * Copyright 2011-2023 The Bootstrap Authors + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */:root,[data-bs-theme=light]{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:#6f42c1;--bs-pink:#d63384;--bs-red:#dc3545;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#198754;--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-black:#000;--bs-white:#fff;--bs-gray:#6c757d;--bs-gray-dark:#343a40;--bs-gray-100:#f8f9fa;--bs-gray-200:#e9ecef;--bs-gray-300:#dee2e6;--bs-gray-400:#ced4da;--bs-gray-500:#adb5bd;--bs-gray-600:#6c757d;--bs-gray-700:#495057;--bs-gray-800:#343a40;--bs-gray-900:#212529;--bs-primary:#0d6efd;--bs-secondary:#6c757d;--bs-success:#198754;--bs-info:#0dcaf0;--bs-warning:#ffc107;--bs-danger:#dc3545;--bs-light:#f8f9fa;--bs-dark:#212529;--bs-primary-rgb:13,110,253;--bs-secondary-rgb:108,117,125;--bs-success-rgb:25,135,84;--bs-info-rgb:13,202,240;--bs-warning-rgb:255,193,7;--bs-danger-rgb:220,53,69;--bs-light-rgb:248,249,250;--bs-dark-rgb:33,37,41;--bs-primary-text-emphasis:#052c65;--bs-secondary-text-emphasis:#2b2f32;--bs-success-text-emphasis:#0a3622;--bs-info-text-emphasis:#055160;--bs-warning-text-emphasis:#664d03;--bs-danger-text-emphasis:#58151c;--bs-light-text-emphasis:#495057;--bs-dark-text-emphasis:#495057;--bs-primary-bg-subtle:#cfe2ff;--bs-secondary-bg-subtle:#e2e3e5;--bs-success-bg-subtle:#d1e7dd;--bs-info-bg-subtle:#cff4fc;--bs-warning-bg-subtle:#fff3cd;--bs-danger-bg-subtle:#f8d7da;--bs-light-bg-subtle:#fcfcfd;--bs-dark-bg-subtle:#ced4da;--bs-primary-border-subtle:#9ec5fe;--bs-secondary-border-subtle:#c4c8cb;--bs-success-border-subtle:#a3cfbb;--bs-info-border-subtle:#9eeaf9;--bs-warning-border-subtle:#ffe69c;--bs-danger-border-subtle:#f1aeb5;--bs-light-border-subtle:#e9ecef;--bs-dark-border-subtle:#adb5bd;--bs-white-rgb:255,255,255;--bs-black-rgb:0,0,0;--bs-font-sans-serif:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue","Noto Sans","Liberation Sans",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--bs-font-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--bs-gradient:linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));--bs-body-font-family:var(--bs-font-sans-serif);--bs-body-font-size:1rem;--bs-body-font-weight:400;--bs-body-line-height:1.5;--bs-body-color:#212529;--bs-body-color-rgb:33,37,41;--bs-body-bg:#fff;--bs-body-bg-rgb:255,255,255;--bs-emphasis-color:#000;--bs-emphasis-color-rgb:0,0,0;--bs-secondary-color:rgba(33, 37, 41, 0.75);--bs-secondary-color-rgb:33,37,41;--bs-secondary-bg:#e9ecef;--bs-secondary-bg-rgb:233,236,239;--bs-tertiary-color:rgba(33, 37, 41, 0.5);--bs-tertiary-color-rgb:33,37,41;--bs-tertiary-bg:#f8f9fa;--bs-tertiary-bg-rgb:248,249,250;--bs-heading-color:inherit;--bs-link-color:#0d6efd;--bs-link-color-rgb:13,110,253;--bs-link-decoration:underline;--bs-link-hover-color:#0a58ca;--bs-link-hover-color-rgb:10,88,202;--bs-code-color:#d63384;--bs-highlight-color:#212529;--bs-highlight-bg:#fff3cd;--bs-border-width:1px;--bs-border-style:solid;--bs-border-color:#dee2e6;--bs-border-color-translucent:rgba(0, 0, 0, 0.175);--bs-border-radius:0.375rem;--bs-border-radius-sm:0.25rem;--bs-border-radius-lg:0.5rem;--bs-border-radius-xl:1rem;--bs-border-radius-xxl:2rem;--bs-border-radius-2xl:var(--bs-border-radius-xxl);--bs-border-radius-pill:50rem;--bs-box-shadow:0 0.5rem 1rem rgba(0, 0, 0, 0.15);--bs-box-shadow-sm:0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);--bs-box-shadow-lg:0 1rem 3rem rgba(0, 0, 0, 0.175);--bs-box-shadow-inset:inset 0 1px 2px rgba(0, 0, 0, 0.075);--bs-focus-ring-width:0.25rem;--bs-focus-ring-opacity:0.25;--bs-focus-ring-color:rgba(13, 110, 253, 0.25);--bs-form-valid-color:#198754;--bs-form-valid-border-color:#198754;--bs-form-invalid-color:#dc3545;--bs-form-invalid-border-color:#dc3545}[data-bs-theme=dark]{color-scheme:dark;--bs-body-color:#dee2e6;--bs-body-color-rgb:222,226,230;--bs-body-bg:#212529;--bs-body-bg-rgb:33,37,41;--bs-emphasis-color:#fff;--bs-emphasis-color-rgb:255,255,255;--bs-secondary-color:rgba(222, 226, 230, 0.75);--bs-secondary-color-rgb:222,226,230;--bs-secondary-bg:#343a40;--bs-secondary-bg-rgb:52,58,64;--bs-tertiary-color:rgba(222, 226, 230, 0.5);--bs-tertiary-color-rgb:222,226,230;--bs-tertiary-bg:#2b3035;--bs-tertiary-bg-rgb:43,48,53;--bs-primary-text-emphasis:#6ea8fe;--bs-secondary-text-emphasis:#a7acb1;--bs-success-text-emphasis:#75b798;--bs-info-text-emphasis:#6edff6;--bs-warning-text-emphasis:#ffda6a;--bs-danger-text-emphasis:#ea868f;--bs-light-text-emphasis:#f8f9fa;--bs-dark-text-emphasis:#dee2e6;--bs-primary-bg-subtle:#031633;--bs-secondary-bg-subtle:#161719;--bs-success-bg-subtle:#051b11;--bs-info-bg-subtle:#032830;--bs-warning-bg-subtle:#332701;--bs-danger-bg-subtle:#2c0b0e;--bs-light-bg-subtle:#343a40;--bs-dark-bg-subtle:#1a1d20;--bs-primary-border-subtle:#084298;--bs-secondary-border-subtle:#41464b;--bs-success-border-subtle:#0f5132;--bs-info-border-subtle:#087990;--bs-warning-border-subtle:#997404;--bs-danger-border-subtle:#842029;--bs-light-border-subtle:#495057;--bs-dark-border-subtle:#343a40;--bs-heading-color:inherit;--bs-link-color:#6ea8fe;--bs-link-hover-color:#8bb9fe;--bs-link-color-rgb:110,168,254;--bs-link-hover-color-rgb:139,185,254;--bs-code-color:#e685b5;--bs-highlight-color:#dee2e6;--bs-highlight-bg:#664d03;--bs-border-color:#495057;--bs-border-color-translucent:rgba(255, 255, 255, 0.15);--bs-form-valid-color:#75b798;--bs-form-valid-border-color:#75b798;--bs-form-invalid-color:#ea868f;--bs-form-invalid-border-color:#ea868f}*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-body-font-family);font-size:var(--bs-body-font-size);font-weight:var(--bs-body-font-weight);line-height:var(--bs-body-line-height);color:var(--bs-body-color);text-align:var(--bs-body-text-align);background-color:var(--bs-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;border:0;border-top:var(--bs-border-width) solid;opacity:.25}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2;color:var(--bs-heading-color)}.h1,h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){.h1,h1{font-size:2.5rem}}.h2,h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){.h2,h2{font-size:2rem}}.h3,h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){.h3,h3{font-size:1.75rem}}.h4,h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){.h4,h4{font-size:1.5rem}}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}.small,small{font-size:.875em}.mark,mark{padding:.1875em;color:var(--bs-highlight-color);background-color:var(--bs-highlight-bg)}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:rgba(var(--bs-link-color-rgb),var(--bs-link-opacity,1));text-decoration:underline}a:hover{--bs-link-color-rgb:var(--bs-link-hover-color-rgb)}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:var(--bs-font-monospace);font-size:1em}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:var(--bs-code-color);word-wrap:break-word}a>code{color:inherit}kbd{padding:.1875rem .375rem;font-size:.875em;color:var(--bs-body-bg);background-color:var(--bs-body-color);border-radius:.25rem}kbd kbd{padding:0;font-size:1em}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:var(--bs-secondary-color);text-align:left}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator{display:none!important}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:left;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:left}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}::file-selector-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:calc(1.625rem + 4.5vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-1{font-size:5rem}}.display-2{font-size:calc(1.575rem + 3.9vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-2{font-size:4.5rem}}.display-3{font-size:calc(1.525rem + 3.3vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-3{font-size:4rem}}.display-4{font-size:calc(1.475rem + 2.7vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-4{font-size:3.5rem}}.display-5{font-size:calc(1.425rem + 2.1vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-5{font-size:3rem}}.display-6{font-size:calc(1.375rem + 1.5vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-6{font-size:2.5rem}}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:.875em;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote>:last-child{margin-bottom:0}.blockquote-footer{margin-top:-1rem;margin-bottom:1rem;font-size:.875em;color:#6c757d}.blockquote-footer::before{content:"— "}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:var(--bs-body-bg);border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius);max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:.875em;color:var(--bs-secondary-color)}.container,.container-fluid,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{--bs-gutter-x:1.5rem;--bs-gutter-y:0;width:100%;padding-right:calc(var(--bs-gutter-x) * .5);padding-left:calc(var(--bs-gutter-x) * .5);margin-right:auto;margin-left:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}@media (min-width:1400px){.container,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{max-width:1320px}}:root{--bs-breakpoint-xs:0;--bs-breakpoint-sm:576px;--bs-breakpoint-md:768px;--bs-breakpoint-lg:992px;--bs-breakpoint-xl:1200px;--bs-breakpoint-xxl:1400px}.row{--bs-gutter-x:1.5rem;--bs-gutter-y:0;display:flex;flex-wrap:wrap;margin-top:calc(-1 * var(--bs-gutter-y));margin-right:calc(-.5 * var(--bs-gutter-x));margin-left:calc(-.5 * var(--bs-gutter-x))}.row>*{flex-shrink:0;width:100%;max-width:100%;padding-right:calc(var(--bs-gutter-x) * .5);padding-left:calc(var(--bs-gutter-x) * .5);margin-top:var(--bs-gutter-y)}.col{flex:1 0 0%}.row-cols-auto>*{flex:0 0 auto;width:auto}.row-cols-1>*{flex:0 0 auto;width:100%}.row-cols-2>*{flex:0 0 auto;width:50%}.row-cols-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-4>*{flex:0 0 auto;width:25%}.row-cols-5>*{flex:0 0 auto;width:20%}.row-cols-6>*{flex:0 0 auto;width:16.66666667%}.col-auto{flex:0 0 auto;width:auto}.col-1{flex:0 0 auto;width:8.33333333%}.col-2{flex:0 0 auto;width:16.66666667%}.col-3{flex:0 0 auto;width:25%}.col-4{flex:0 0 auto;width:33.33333333%}.col-5{flex:0 0 auto;width:41.66666667%}.col-6{flex:0 0 auto;width:50%}.col-7{flex:0 0 auto;width:58.33333333%}.col-8{flex:0 0 auto;width:66.66666667%}.col-9{flex:0 0 auto;width:75%}.col-10{flex:0 0 auto;width:83.33333333%}.col-11{flex:0 0 auto;width:91.66666667%}.col-12{flex:0 0 auto;width:100%}.offset-1{margin-left:8.33333333%}.offset-2{margin-left:16.66666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.33333333%}.offset-5{margin-left:41.66666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.33333333%}.offset-8{margin-left:66.66666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.33333333%}.offset-11{margin-left:91.66666667%}.g-0,.gx-0{--bs-gutter-x:0}.g-0,.gy-0{--bs-gutter-y:0}.g-1,.gx-1{--bs-gutter-x:0.25rem}.g-1,.gy-1{--bs-gutter-y:0.25rem}.g-2,.gx-2{--bs-gutter-x:0.5rem}.g-2,.gy-2{--bs-gutter-y:0.5rem}.g-3,.gx-3{--bs-gutter-x:1rem}.g-3,.gy-3{--bs-gutter-y:1rem}.g-4,.gx-4{--bs-gutter-x:1.5rem}.g-4,.gy-4{--bs-gutter-y:1.5rem}.g-5,.gx-5{--bs-gutter-x:3rem}.g-5,.gy-5{--bs-gutter-y:3rem}@media (min-width:576px){.col-sm{flex:1 0 0%}.row-cols-sm-auto>*{flex:0 0 auto;width:auto}.row-cols-sm-1>*{flex:0 0 auto;width:100%}.row-cols-sm-2>*{flex:0 0 auto;width:50%}.row-cols-sm-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-sm-4>*{flex:0 0 auto;width:25%}.row-cols-sm-5>*{flex:0 0 auto;width:20%}.row-cols-sm-6>*{flex:0 0 auto;width:16.66666667%}.col-sm-auto{flex:0 0 auto;width:auto}.col-sm-1{flex:0 0 auto;width:8.33333333%}.col-sm-2{flex:0 0 auto;width:16.66666667%}.col-sm-3{flex:0 0 auto;width:25%}.col-sm-4{flex:0 0 auto;width:33.33333333%}.col-sm-5{flex:0 0 auto;width:41.66666667%}.col-sm-6{flex:0 0 auto;width:50%}.col-sm-7{flex:0 0 auto;width:58.33333333%}.col-sm-8{flex:0 0 auto;width:66.66666667%}.col-sm-9{flex:0 0 auto;width:75%}.col-sm-10{flex:0 0 auto;width:83.33333333%}.col-sm-11{flex:0 0 auto;width:91.66666667%}.col-sm-12{flex:0 0 auto;width:100%}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.33333333%}.offset-sm-2{margin-left:16.66666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.33333333%}.offset-sm-5{margin-left:41.66666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.33333333%}.offset-sm-8{margin-left:66.66666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.33333333%}.offset-sm-11{margin-left:91.66666667%}.g-sm-0,.gx-sm-0{--bs-gutter-x:0}.g-sm-0,.gy-sm-0{--bs-gutter-y:0}.g-sm-1,.gx-sm-1{--bs-gutter-x:0.25rem}.g-sm-1,.gy-sm-1{--bs-gutter-y:0.25rem}.g-sm-2,.gx-sm-2{--bs-gutter-x:0.5rem}.g-sm-2,.gy-sm-2{--bs-gutter-y:0.5rem}.g-sm-3,.gx-sm-3{--bs-gutter-x:1rem}.g-sm-3,.gy-sm-3{--bs-gutter-y:1rem}.g-sm-4,.gx-sm-4{--bs-gutter-x:1.5rem}.g-sm-4,.gy-sm-4{--bs-gutter-y:1.5rem}.g-sm-5,.gx-sm-5{--bs-gutter-x:3rem}.g-sm-5,.gy-sm-5{--bs-gutter-y:3rem}}@media (min-width:768px){.col-md{flex:1 0 0%}.row-cols-md-auto>*{flex:0 0 auto;width:auto}.row-cols-md-1>*{flex:0 0 auto;width:100%}.row-cols-md-2>*{flex:0 0 auto;width:50%}.row-cols-md-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-md-4>*{flex:0 0 auto;width:25%}.row-cols-md-5>*{flex:0 0 auto;width:20%}.row-cols-md-6>*{flex:0 0 auto;width:16.66666667%}.col-md-auto{flex:0 0 auto;width:auto}.col-md-1{flex:0 0 auto;width:8.33333333%}.col-md-2{flex:0 0 auto;width:16.66666667%}.col-md-3{flex:0 0 auto;width:25%}.col-md-4{flex:0 0 auto;width:33.33333333%}.col-md-5{flex:0 0 auto;width:41.66666667%}.col-md-6{flex:0 0 auto;width:50%}.col-md-7{flex:0 0 auto;width:58.33333333%}.col-md-8{flex:0 0 auto;width:66.66666667%}.col-md-9{flex:0 0 auto;width:75%}.col-md-10{flex:0 0 auto;width:83.33333333%}.col-md-11{flex:0 0 auto;width:91.66666667%}.col-md-12{flex:0 0 auto;width:100%}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.33333333%}.offset-md-2{margin-left:16.66666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.33333333%}.offset-md-5{margin-left:41.66666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.33333333%}.offset-md-8{margin-left:66.66666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.33333333%}.offset-md-11{margin-left:91.66666667%}.g-md-0,.gx-md-0{--bs-gutter-x:0}.g-md-0,.gy-md-0{--bs-gutter-y:0}.g-md-1,.gx-md-1{--bs-gutter-x:0.25rem}.g-md-1,.gy-md-1{--bs-gutter-y:0.25rem}.g-md-2,.gx-md-2{--bs-gutter-x:0.5rem}.g-md-2,.gy-md-2{--bs-gutter-y:0.5rem}.g-md-3,.gx-md-3{--bs-gutter-x:1rem}.g-md-3,.gy-md-3{--bs-gutter-y:1rem}.g-md-4,.gx-md-4{--bs-gutter-x:1.5rem}.g-md-4,.gy-md-4{--bs-gutter-y:1.5rem}.g-md-5,.gx-md-5{--bs-gutter-x:3rem}.g-md-5,.gy-md-5{--bs-gutter-y:3rem}}@media (min-width:992px){.col-lg{flex:1 0 0%}.row-cols-lg-auto>*{flex:0 0 auto;width:auto}.row-cols-lg-1>*{flex:0 0 auto;width:100%}.row-cols-lg-2>*{flex:0 0 auto;width:50%}.row-cols-lg-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-lg-4>*{flex:0 0 auto;width:25%}.row-cols-lg-5>*{flex:0 0 auto;width:20%}.row-cols-lg-6>*{flex:0 0 auto;width:16.66666667%}.col-lg-auto{flex:0 0 auto;width:auto}.col-lg-1{flex:0 0 auto;width:8.33333333%}.col-lg-2{flex:0 0 auto;width:16.66666667%}.col-lg-3{flex:0 0 auto;width:25%}.col-lg-4{flex:0 0 auto;width:33.33333333%}.col-lg-5{flex:0 0 auto;width:41.66666667%}.col-lg-6{flex:0 0 auto;width:50%}.col-lg-7{flex:0 0 auto;width:58.33333333%}.col-lg-8{flex:0 0 auto;width:66.66666667%}.col-lg-9{flex:0 0 auto;width:75%}.col-lg-10{flex:0 0 auto;width:83.33333333%}.col-lg-11{flex:0 0 auto;width:91.66666667%}.col-lg-12{flex:0 0 auto;width:100%}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.33333333%}.offset-lg-2{margin-left:16.66666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.33333333%}.offset-lg-5{margin-left:41.66666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.33333333%}.offset-lg-8{margin-left:66.66666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.33333333%}.offset-lg-11{margin-left:91.66666667%}.g-lg-0,.gx-lg-0{--bs-gutter-x:0}.g-lg-0,.gy-lg-0{--bs-gutter-y:0}.g-lg-1,.gx-lg-1{--bs-gutter-x:0.25rem}.g-lg-1,.gy-lg-1{--bs-gutter-y:0.25rem}.g-lg-2,.gx-lg-2{--bs-gutter-x:0.5rem}.g-lg-2,.gy-lg-2{--bs-gutter-y:0.5rem}.g-lg-3,.gx-lg-3{--bs-gutter-x:1rem}.g-lg-3,.gy-lg-3{--bs-gutter-y:1rem}.g-lg-4,.gx-lg-4{--bs-gutter-x:1.5rem}.g-lg-4,.gy-lg-4{--bs-gutter-y:1.5rem}.g-lg-5,.gx-lg-5{--bs-gutter-x:3rem}.g-lg-5,.gy-lg-5{--bs-gutter-y:3rem}}@media (min-width:1200px){.col-xl{flex:1 0 0%}.row-cols-xl-auto>*{flex:0 0 auto;width:auto}.row-cols-xl-1>*{flex:0 0 auto;width:100%}.row-cols-xl-2>*{flex:0 0 auto;width:50%}.row-cols-xl-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-xl-4>*{flex:0 0 auto;width:25%}.row-cols-xl-5>*{flex:0 0 auto;width:20%}.row-cols-xl-6>*{flex:0 0 auto;width:16.66666667%}.col-xl-auto{flex:0 0 auto;width:auto}.col-xl-1{flex:0 0 auto;width:8.33333333%}.col-xl-2{flex:0 0 auto;width:16.66666667%}.col-xl-3{flex:0 0 auto;width:25%}.col-xl-4{flex:0 0 auto;width:33.33333333%}.col-xl-5{flex:0 0 auto;width:41.66666667%}.col-xl-6{flex:0 0 auto;width:50%}.col-xl-7{flex:0 0 auto;width:58.33333333%}.col-xl-8{flex:0 0 auto;width:66.66666667%}.col-xl-9{flex:0 0 auto;width:75%}.col-xl-10{flex:0 0 auto;width:83.33333333%}.col-xl-11{flex:0 0 auto;width:91.66666667%}.col-xl-12{flex:0 0 auto;width:100%}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.33333333%}.offset-xl-2{margin-left:16.66666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.33333333%}.offset-xl-5{margin-left:41.66666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.33333333%}.offset-xl-8{margin-left:66.66666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.33333333%}.offset-xl-11{margin-left:91.66666667%}.g-xl-0,.gx-xl-0{--bs-gutter-x:0}.g-xl-0,.gy-xl-0{--bs-gutter-y:0}.g-xl-1,.gx-xl-1{--bs-gutter-x:0.25rem}.g-xl-1,.gy-xl-1{--bs-gutter-y:0.25rem}.g-xl-2,.gx-xl-2{--bs-gutter-x:0.5rem}.g-xl-2,.gy-xl-2{--bs-gutter-y:0.5rem}.g-xl-3,.gx-xl-3{--bs-gutter-x:1rem}.g-xl-3,.gy-xl-3{--bs-gutter-y:1rem}.g-xl-4,.gx-xl-4{--bs-gutter-x:1.5rem}.g-xl-4,.gy-xl-4{--bs-gutter-y:1.5rem}.g-xl-5,.gx-xl-5{--bs-gutter-x:3rem}.g-xl-5,.gy-xl-5{--bs-gutter-y:3rem}}@media (min-width:1400px){.col-xxl{flex:1 0 0%}.row-cols-xxl-auto>*{flex:0 0 auto;width:auto}.row-cols-xxl-1>*{flex:0 0 auto;width:100%}.row-cols-xxl-2>*{flex:0 0 auto;width:50%}.row-cols-xxl-3>*{flex:0 0 auto;width:33.33333333%}.row-cols-xxl-4>*{flex:0 0 auto;width:25%}.row-cols-xxl-5>*{flex:0 0 auto;width:20%}.row-cols-xxl-6>*{flex:0 0 auto;width:16.66666667%}.col-xxl-auto{flex:0 0 auto;width:auto}.col-xxl-1{flex:0 0 auto;width:8.33333333%}.col-xxl-2{flex:0 0 auto;width:16.66666667%}.col-xxl-3{flex:0 0 auto;width:25%}.col-xxl-4{flex:0 0 auto;width:33.33333333%}.col-xxl-5{flex:0 0 auto;width:41.66666667%}.col-xxl-6{flex:0 0 auto;width:50%}.col-xxl-7{flex:0 0 auto;width:58.33333333%}.col-xxl-8{flex:0 0 auto;width:66.66666667%}.col-xxl-9{flex:0 0 auto;width:75%}.col-xxl-10{flex:0 0 auto;width:83.33333333%}.col-xxl-11{flex:0 0 auto;width:91.66666667%}.col-xxl-12{flex:0 0 auto;width:100%}.offset-xxl-0{margin-left:0}.offset-xxl-1{margin-left:8.33333333%}.offset-xxl-2{margin-left:16.66666667%}.offset-xxl-3{margin-left:25%}.offset-xxl-4{margin-left:33.33333333%}.offset-xxl-5{margin-left:41.66666667%}.offset-xxl-6{margin-left:50%}.offset-xxl-7{margin-left:58.33333333%}.offset-xxl-8{margin-left:66.66666667%}.offset-xxl-9{margin-left:75%}.offset-xxl-10{margin-left:83.33333333%}.offset-xxl-11{margin-left:91.66666667%}.g-xxl-0,.gx-xxl-0{--bs-gutter-x:0}.g-xxl-0,.gy-xxl-0{--bs-gutter-y:0}.g-xxl-1,.gx-xxl-1{--bs-gutter-x:0.25rem}.g-xxl-1,.gy-xxl-1{--bs-gutter-y:0.25rem}.g-xxl-2,.gx-xxl-2{--bs-gutter-x:0.5rem}.g-xxl-2,.gy-xxl-2{--bs-gutter-y:0.5rem}.g-xxl-3,.gx-xxl-3{--bs-gutter-x:1rem}.g-xxl-3,.gy-xxl-3{--bs-gutter-y:1rem}.g-xxl-4,.gx-xxl-4{--bs-gutter-x:1.5rem}.g-xxl-4,.gy-xxl-4{--bs-gutter-y:1.5rem}.g-xxl-5,.gx-xxl-5{--bs-gutter-x:3rem}.g-xxl-5,.gy-xxl-5{--bs-gutter-y:3rem}}.table{--bs-table-color-type:initial;--bs-table-bg-type:initial;--bs-table-color-state:initial;--bs-table-bg-state:initial;--bs-table-color:var(--bs-emphasis-color);--bs-table-bg:var(--bs-body-bg);--bs-table-border-color:var(--bs-border-color);--bs-table-accent-bg:transparent;--bs-table-striped-color:var(--bs-emphasis-color);--bs-table-striped-bg:rgba(var(--bs-emphasis-color-rgb), 0.05);--bs-table-active-color:var(--bs-emphasis-color);--bs-table-active-bg:rgba(var(--bs-emphasis-color-rgb), 0.1);--bs-table-hover-color:var(--bs-emphasis-color);--bs-table-hover-bg:rgba(var(--bs-emphasis-color-rgb), 0.075);width:100%;margin-bottom:1rem;vertical-align:top;border-color:var(--bs-table-border-color)}.table>:not(caption)>*>*{padding:.5rem .5rem;color:var(--bs-table-color-state,var(--bs-table-color-type,var(--bs-table-color)));background-color:var(--bs-table-bg);border-bottom-width:var(--bs-border-width);box-shadow:inset 0 0 0 9999px var(--bs-table-bg-state,var(--bs-table-bg-type,var(--bs-table-accent-bg)))}.table>tbody{vertical-align:inherit}.table>thead{vertical-align:bottom}.table-group-divider{border-top:calc(var(--bs-border-width) * 2) solid currentcolor}.caption-top{caption-side:top}.table-sm>:not(caption)>*>*{padding:.25rem .25rem}.table-bordered>:not(caption)>*{border-width:var(--bs-border-width) 0}.table-bordered>:not(caption)>*>*{border-width:0 var(--bs-border-width)}.table-borderless>:not(caption)>*>*{border-bottom-width:0}.table-borderless>:not(:first-child){border-top-width:0}.table-striped>tbody>tr:nth-of-type(odd)>*{--bs-table-color-type:var(--bs-table-striped-color);--bs-table-bg-type:var(--bs-table-striped-bg)}.table-striped-columns>:not(caption)>tr>:nth-child(2n){--bs-table-color-type:var(--bs-table-striped-color);--bs-table-bg-type:var(--bs-table-striped-bg)}.table-active{--bs-table-color-state:var(--bs-table-active-color);--bs-table-bg-state:var(--bs-table-active-bg)}.table-hover>tbody>tr:hover>*{--bs-table-color-state:var(--bs-table-hover-color);--bs-table-bg-state:var(--bs-table-hover-bg)}.table-primary{--bs-table-color:#000;--bs-table-bg:#cfe2ff;--bs-table-border-color:#a6b5cc;--bs-table-striped-bg:#c5d7f2;--bs-table-striped-color:#000;--bs-table-active-bg:#bacbe6;--bs-table-active-color:#000;--bs-table-hover-bg:#bfd1ec;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-secondary{--bs-table-color:#000;--bs-table-bg:#e2e3e5;--bs-table-border-color:#b5b6b7;--bs-table-striped-bg:#d7d8da;--bs-table-striped-color:#000;--bs-table-active-bg:#cbccce;--bs-table-active-color:#000;--bs-table-hover-bg:#d1d2d4;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-success{--bs-table-color:#000;--bs-table-bg:#d1e7dd;--bs-table-border-color:#a7b9b1;--bs-table-striped-bg:#c7dbd2;--bs-table-striped-color:#000;--bs-table-active-bg:#bcd0c7;--bs-table-active-color:#000;--bs-table-hover-bg:#c1d6cc;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-info{--bs-table-color:#000;--bs-table-bg:#cff4fc;--bs-table-border-color:#a6c3ca;--bs-table-striped-bg:#c5e8ef;--bs-table-striped-color:#000;--bs-table-active-bg:#badce3;--bs-table-active-color:#000;--bs-table-hover-bg:#bfe2e9;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-warning{--bs-table-color:#000;--bs-table-bg:#fff3cd;--bs-table-border-color:#ccc2a4;--bs-table-striped-bg:#f2e7c3;--bs-table-striped-color:#000;--bs-table-active-bg:#e6dbb9;--bs-table-active-color:#000;--bs-table-hover-bg:#ece1be;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-danger{--bs-table-color:#000;--bs-table-bg:#f8d7da;--bs-table-border-color:#c6acae;--bs-table-striped-bg:#eccccf;--bs-table-striped-color:#000;--bs-table-active-bg:#dfc2c4;--bs-table-active-color:#000;--bs-table-hover-bg:#e5c7ca;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-light{--bs-table-color:#000;--bs-table-bg:#f8f9fa;--bs-table-border-color:#c6c7c8;--bs-table-striped-bg:#ecedee;--bs-table-striped-color:#000;--bs-table-active-bg:#dfe0e1;--bs-table-active-color:#000;--bs-table-hover-bg:#e5e6e7;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-dark{--bs-table-color:#fff;--bs-table-bg:#212529;--bs-table-border-color:#4d5154;--bs-table-striped-bg:#2c3034;--bs-table-striped-color:#fff;--bs-table-active-bg:#373b3e;--bs-table-active-color:#fff;--bs-table-hover-bg:#323539;--bs-table-hover-color:#fff;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-responsive{overflow-x:auto;-webkit-overflow-scrolling:touch}@media (max-width:575.98px){.table-responsive-sm{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:767.98px){.table-responsive-md{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:991.98px){.table-responsive-lg{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:1199.98px){.table-responsive-xl{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:1399.98px){.table-responsive-xxl{overflow-x:auto;-webkit-overflow-scrolling:touch}}.form-label{margin-bottom:.5rem}.col-form-label{padding-top:calc(.375rem + var(--bs-border-width));padding-bottom:calc(.375rem + var(--bs-border-width));margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + var(--bs-border-width));padding-bottom:calc(.5rem + var(--bs-border-width));font-size:1.25rem}.col-form-label-sm{padding-top:calc(.25rem + var(--bs-border-width));padding-bottom:calc(.25rem + var(--bs-border-width));font-size:.875rem}.form-text{margin-top:.25rem;font-size:.875em;color:var(--bs-secondary-color)}.form-control{display:block;width:100%;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:var(--bs-body-bg);background-clip:padding-box;border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius);transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control[type=file]{overflow:hidden}.form-control[type=file]:not(:disabled):not([readonly]){cursor:pointer}.form-control:focus{color:var(--bs-body-color);background-color:var(--bs-body-bg);border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-control::-webkit-date-and-time-value{min-width:85px;height:1.5em;margin:0}.form-control::-webkit-datetime-edit{display:block;padding:0}.form-control::-moz-placeholder{color:var(--bs-secondary-color);opacity:1}.form-control::placeholder{color:var(--bs-secondary-color);opacity:1}.form-control:disabled{background-color:var(--bs-secondary-bg);opacity:1}.form-control::-webkit-file-upload-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:var(--bs-body-color);background-color:var(--bs-tertiary-bg);pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:var(--bs-border-width);border-radius:0;-webkit-transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}.form-control::file-selector-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:var(--bs-body-color);background-color:var(--bs-tertiary-bg);pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:var(--bs-border-width);border-radius:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control::-webkit-file-upload-button{-webkit-transition:none;transition:none}.form-control::file-selector-button{transition:none}}.form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button{background-color:var(--bs-secondary-bg)}.form-control:hover:not(:disabled):not([readonly])::file-selector-button{background-color:var(--bs-secondary-bg)}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;line-height:1.5;color:var(--bs-body-color);background-color:transparent;border:solid transparent;border-width:var(--bs-border-width) 0}.form-control-plaintext:focus{outline:0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{min-height:calc(1.5em + .5rem + calc(var(--bs-border-width) * 2));padding:.25rem .5rem;font-size:.875rem;border-radius:var(--bs-border-radius-sm)}.form-control-sm::-webkit-file-upload-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-sm::file-selector-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-lg{min-height:calc(1.5em + 1rem + calc(var(--bs-border-width) * 2));padding:.5rem 1rem;font-size:1.25rem;border-radius:var(--bs-border-radius-lg)}.form-control-lg::-webkit-file-upload-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}.form-control-lg::file-selector-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}textarea.form-control{min-height:calc(1.5em + .75rem + calc(var(--bs-border-width) * 2))}textarea.form-control-sm{min-height:calc(1.5em + .5rem + calc(var(--bs-border-width) * 2))}textarea.form-control-lg{min-height:calc(1.5em + 1rem + calc(var(--bs-border-width) * 2))}.form-control-color{width:3rem;height:calc(1.5em + .75rem + calc(var(--bs-border-width) * 2));padding:.375rem}.form-control-color:not(:disabled):not([readonly]){cursor:pointer}.form-control-color::-moz-color-swatch{border:0!important;border-radius:var(--bs-border-radius)}.form-control-color::-webkit-color-swatch{border:0!important;border-radius:var(--bs-border-radius)}.form-control-color.form-control-sm{height:calc(1.5em + .5rem + calc(var(--bs-border-width) * 2))}.form-control-color.form-control-lg{height:calc(1.5em + 1rem + calc(var(--bs-border-width) * 2))}.form-select{--bs-form-select-bg-img:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");display:block;width:100%;padding:.375rem 2.25rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:var(--bs-body-bg);background-image:var(--bs-form-select-bg-img),var(--bs-form-select-bg-icon,none);background-repeat:no-repeat;background-position:right .75rem center;background-size:16px 12px;border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius);transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-select{transition:none}}.form-select:focus{border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-select[multiple],.form-select[size]:not([size="1"]){padding-right:.75rem;background-image:none}.form-select:disabled{background-color:var(--bs-secondary-bg)}.form-select:-moz-focusring{color:transparent;text-shadow:0 0 0 var(--bs-body-color)}.form-select-sm{padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem;border-radius:var(--bs-border-radius-sm)}.form-select-lg{padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem;border-radius:var(--bs-border-radius-lg)}[data-bs-theme=dark] .form-select{--bs-form-select-bg-img:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23dee2e6' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e")}.form-check{display:block;min-height:1.5rem;padding-left:1.5em;margin-bottom:.125rem}.form-check .form-check-input{float:left;margin-left:-1.5em}.form-check-reverse{padding-right:1.5em;padding-left:0;text-align:right}.form-check-reverse .form-check-input{float:right;margin-right:-1.5em;margin-left:0}.form-check-input{--bs-form-check-bg:var(--bs-body-bg);flex-shrink:0;width:1em;height:1em;margin-top:.25em;vertical-align:top;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:var(--bs-form-check-bg);background-image:var(--bs-form-check-bg-image);background-repeat:no-repeat;background-position:center;background-size:contain;border:var(--bs-border-width) solid var(--bs-border-color);-webkit-print-color-adjust:exact;color-adjust:exact;print-color-adjust:exact}.form-check-input[type=checkbox]{border-radius:.25em}.form-check-input[type=radio]{border-radius:50%}.form-check-input:active{filter:brightness(90%)}.form-check-input:focus{border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-check-input:checked{background-color:#0d6efd;border-color:#0d6efd}.form-check-input:checked[type=checkbox]{--bs-form-check-bg-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e")}.form-check-input:checked[type=radio]{--bs-form-check-bg-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e")}.form-check-input[type=checkbox]:indeterminate{background-color:#0d6efd;border-color:#0d6efd;--bs-form-check-bg-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e")}.form-check-input:disabled{pointer-events:none;filter:none;opacity:.5}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{cursor:default;opacity:.5}.form-switch{padding-left:2.5em}.form-switch .form-check-input{--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e");width:2em;margin-left:-2.5em;background-image:var(--bs-form-switch-bg);background-position:left center;border-radius:2em;transition:background-position .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-switch .form-check-input{transition:none}}.form-switch .form-check-input:focus{--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%2386b7fe'/%3e%3c/svg%3e")}.form-switch .form-check-input:checked{background-position:right center;--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.form-switch.form-check-reverse{padding-right:2.5em;padding-left:0}.form-switch.form-check-reverse .form-check-input{margin-right:-2.5em;margin-left:0}.form-check-inline{display:inline-block;margin-right:1rem}.btn-check{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.btn-check:disabled+.btn,.btn-check[disabled]+.btn{pointer-events:none;filter:none;opacity:.65}[data-bs-theme=dark] .form-switch .form-check-input:not(:checked):not(:focus){--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%28255, 255, 255, 0.25%29'/%3e%3c/svg%3e")}.form-range{width:100%;height:1.5rem;padding:0;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:transparent}.form-range:focus{outline:0}.form-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range::-moz-focus-outer{border:0}.form-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;-webkit-appearance:none;appearance:none;background-color:#0d6efd;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.form-range::-webkit-slider-thumb:active{background-color:#b6d4fe}.form-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:var(--bs-secondary-bg);border-color:transparent;border-radius:1rem}.form-range::-moz-range-thumb{width:1rem;height:1rem;-moz-appearance:none;appearance:none;background-color:#0d6efd;border:0;border-radius:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-range::-moz-range-thumb{-moz-transition:none;transition:none}}.form-range::-moz-range-thumb:active{background-color:#b6d4fe}.form-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:var(--bs-secondary-bg);border-color:transparent;border-radius:1rem}.form-range:disabled{pointer-events:none}.form-range:disabled::-webkit-slider-thumb{background-color:var(--bs-secondary-color)}.form-range:disabled::-moz-range-thumb{background-color:var(--bs-secondary-color)}.form-floating{position:relative}.form-floating>.form-control,.form-floating>.form-control-plaintext,.form-floating>.form-select{height:calc(3.5rem + calc(var(--bs-border-width) * 2));min-height:calc(3.5rem + calc(var(--bs-border-width) * 2));line-height:1.25}.form-floating>label{position:absolute;top:0;left:0;z-index:2;height:100%;padding:1rem .75rem;overflow:hidden;text-align:start;text-overflow:ellipsis;white-space:nowrap;pointer-events:none;border:var(--bs-border-width) solid transparent;transform-origin:0 0;transition:opacity .1s ease-in-out,transform .1s ease-in-out}@media (prefers-reduced-motion:reduce){.form-floating>label{transition:none}}.form-floating>.form-control,.form-floating>.form-control-plaintext{padding:1rem .75rem}.form-floating>.form-control-plaintext::-moz-placeholder,.form-floating>.form-control::-moz-placeholder{color:transparent}.form-floating>.form-control-plaintext::placeholder,.form-floating>.form-control::placeholder{color:transparent}.form-floating>.form-control-plaintext:not(:-moz-placeholder-shown),.form-floating>.form-control:not(:-moz-placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control-plaintext:focus,.form-floating>.form-control-plaintext:not(:placeholder-shown),.form-floating>.form-control:focus,.form-floating>.form-control:not(:placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control-plaintext:-webkit-autofill,.form-floating>.form-control:-webkit-autofill{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-select{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:not(:-moz-placeholder-shown)~label{color:rgba(var(--bs-body-color-rgb),.65);transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control-plaintext~label,.form-floating>.form-control:focus~label,.form-floating>.form-control:not(:placeholder-shown)~label,.form-floating>.form-select~label{color:rgba(var(--bs-body-color-rgb),.65);transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control:not(:-moz-placeholder-shown)~label::after{position:absolute;inset:1rem 0.375rem;z-index:-1;height:1.5em;content:"";background-color:var(--bs-body-bg);border-radius:var(--bs-border-radius)}.form-floating>.form-control-plaintext~label::after,.form-floating>.form-control:focus~label::after,.form-floating>.form-control:not(:placeholder-shown)~label::after,.form-floating>.form-select~label::after{position:absolute;inset:1rem 0.375rem;z-index:-1;height:1.5em;content:"";background-color:var(--bs-body-bg);border-radius:var(--bs-border-radius)}.form-floating>.form-control:-webkit-autofill~label{color:rgba(var(--bs-body-color-rgb),.65);transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control-plaintext~label{border-width:var(--bs-border-width) 0}.form-floating>.form-control:disabled~label,.form-floating>:disabled~label{color:#6c757d}.form-floating>.form-control:disabled~label::after,.form-floating>:disabled~label::after{background-color:var(--bs-secondary-bg)}.input-group{position:relative;display:flex;flex-wrap:wrap;align-items:stretch;width:100%}.input-group>.form-control,.input-group>.form-floating,.input-group>.form-select{position:relative;flex:1 1 auto;width:1%;min-width:0}.input-group>.form-control:focus,.input-group>.form-floating:focus-within,.input-group>.form-select:focus{z-index:5}.input-group .btn{position:relative;z-index:2}.input-group .btn:focus{z-index:5}.input-group-text{display:flex;align-items:center;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);text-align:center;white-space:nowrap;background-color:var(--bs-tertiary-bg);border:var(--bs-border-width) solid var(--bs-border-color);border-radius:var(--bs-border-radius)}.input-group-lg>.btn,.input-group-lg>.form-control,.input-group-lg>.form-select,.input-group-lg>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;border-radius:var(--bs-border-radius-lg)}.input-group-sm>.btn,.input-group-sm>.form-control,.input-group-sm>.form-select,.input-group-sm>.input-group-text{padding:.25rem .5rem;font-size:.875rem;border-radius:var(--bs-border-radius-sm)}.input-group-lg>.form-select,.input-group-sm>.form-select{padding-right:3rem}.input-group:not(.has-validation)>.dropdown-toggle:nth-last-child(n+3),.input-group:not(.has-validation)>.form-floating:not(:last-child)>.form-control,.input-group:not(.has-validation)>.form-floating:not(:last-child)>.form-select,.input-group:not(.has-validation)>:not(:last-child):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating){border-top-right-radius:0;border-bottom-right-radius:0}.input-group.has-validation>.dropdown-toggle:nth-last-child(n+4),.input-group.has-validation>.form-floating:nth-last-child(n+3)>.form-control,.input-group.has-validation>.form-floating:nth-last-child(n+3)>.form-select,.input-group.has-validation>:nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>:not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback){margin-left:calc(var(--bs-border-width) * -1);border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.form-floating:not(:first-child)>.form-control,.input-group>.form-floating:not(:first-child)>.form-select{border-top-left-radius:0;border-bottom-left-radius:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:var(--bs-form-valid-color)}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:var(--bs-success);border-radius:var(--bs-border-radius)}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:var(--bs-form-valid-border-color);padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:var(--bs-form-valid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-success-rgb),.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.form-select.is-valid,.was-validated .form-select:valid{border-color:var(--bs-form-valid-border-color)}.form-select.is-valid:not([multiple]):not([size]),.form-select.is-valid:not([multiple])[size="1"],.was-validated .form-select:valid:not([multiple]):not([size]),.was-validated .form-select:valid:not([multiple])[size="1"]{--bs-form-select-bg-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");padding-right:4.125rem;background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem)}.form-select.is-valid:focus,.was-validated .form-select:valid:focus{border-color:var(--bs-form-valid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-success-rgb),.25)}.form-control-color.is-valid,.was-validated .form-control-color:valid{width:calc(3rem + calc(1.5em + .75rem))}.form-check-input.is-valid,.was-validated .form-check-input:valid{border-color:var(--bs-form-valid-border-color)}.form-check-input.is-valid:checked,.was-validated .form-check-input:valid:checked{background-color:var(--bs-form-valid-color)}.form-check-input.is-valid:focus,.was-validated .form-check-input:valid:focus{box-shadow:0 0 0 .25rem rgba(var(--bs-success-rgb),.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:var(--bs-form-valid-color)}.form-check-inline .form-check-input~.valid-feedback{margin-left:.5em}.input-group>.form-control:not(:focus).is-valid,.input-group>.form-floating:not(:focus-within).is-valid,.input-group>.form-select:not(:focus).is-valid,.was-validated .input-group>.form-control:not(:focus):valid,.was-validated .input-group>.form-floating:not(:focus-within):valid,.was-validated .input-group>.form-select:not(:focus):valid{z-index:3}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:var(--bs-form-invalid-color)}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:var(--bs-danger);border-radius:var(--bs-border-radius)}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:var(--bs-form-invalid-border-color);padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:var(--bs-form-invalid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-danger-rgb),.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.form-select.is-invalid,.was-validated .form-select:invalid{border-color:var(--bs-form-invalid-border-color)}.form-select.is-invalid:not([multiple]):not([size]),.form-select.is-invalid:not([multiple])[size="1"],.was-validated .form-select:invalid:not([multiple]):not([size]),.was-validated .form-select:invalid:not([multiple])[size="1"]{--bs-form-select-bg-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");padding-right:4.125rem;background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem)}.form-select.is-invalid:focus,.was-validated .form-select:invalid:focus{border-color:var(--bs-form-invalid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-danger-rgb),.25)}.form-control-color.is-invalid,.was-validated .form-control-color:invalid{width:calc(3rem + calc(1.5em + .75rem))}.form-check-input.is-invalid,.was-validated .form-check-input:invalid{border-color:var(--bs-form-invalid-border-color)}.form-check-input.is-invalid:checked,.was-validated .form-check-input:invalid:checked{background-color:var(--bs-form-invalid-color)}.form-check-input.is-invalid:focus,.was-validated .form-check-input:invalid:focus{box-shadow:0 0 0 .25rem rgba(var(--bs-danger-rgb),.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:var(--bs-form-invalid-color)}.form-check-inline .form-check-input~.invalid-feedback{margin-left:.5em}.input-group>.form-control:not(:focus).is-invalid,.input-group>.form-floating:not(:focus-within).is-invalid,.input-group>.form-select:not(:focus).is-invalid,.was-validated .input-group>.form-control:not(:focus):invalid,.was-validated .input-group>.form-floating:not(:focus-within):invalid,.was-validated .input-group>.form-select:not(:focus):invalid{z-index:4}.btn{--bs-btn-padding-x:0.75rem;--bs-btn-padding-y:0.375rem;--bs-btn-font-family: ;--bs-btn-font-size:1rem;--bs-btn-font-weight:400;--bs-btn-line-height:1.5;--bs-btn-color:var(--bs-body-color);--bs-btn-bg:transparent;--bs-btn-border-width:var(--bs-border-width);--bs-btn-border-color:transparent;--bs-btn-border-radius:var(--bs-border-radius);--bs-btn-hover-border-color:transparent;--bs-btn-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.15),0 1px 1px rgba(0, 0, 0, 0.075);--bs-btn-disabled-opacity:0.65;--bs-btn-focus-box-shadow:0 0 0 0.25rem rgba(var(--bs-btn-focus-shadow-rgb), .5);display:inline-block;padding:var(--bs-btn-padding-y) var(--bs-btn-padding-x);font-family:var(--bs-btn-font-family);font-size:var(--bs-btn-font-size);font-weight:var(--bs-btn-font-weight);line-height:var(--bs-btn-line-height);color:var(--bs-btn-color);text-align:center;text-decoration:none;vertical-align:middle;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;border:var(--bs-btn-border-width) solid var(--bs-btn-border-color);border-radius:var(--bs-btn-border-radius);background-color:var(--bs-btn-bg);transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:var(--bs-btn-hover-color);background-color:var(--bs-btn-hover-bg);border-color:var(--bs-btn-hover-border-color)}.btn-check+.btn:hover{color:var(--bs-btn-color);background-color:var(--bs-btn-bg);border-color:var(--bs-btn-border-color)}.btn:focus-visible{color:var(--bs-btn-hover-color);background-color:var(--bs-btn-hover-bg);border-color:var(--bs-btn-hover-border-color);outline:0;box-shadow:var(--bs-btn-focus-box-shadow)}.btn-check:focus-visible+.btn{border-color:var(--bs-btn-hover-border-color);outline:0;box-shadow:var(--bs-btn-focus-box-shadow)}.btn-check:checked+.btn,.btn.active,.btn.show,.btn:first-child:active,:not(.btn-check)+.btn:active{color:var(--bs-btn-active-color);background-color:var(--bs-btn-active-bg);border-color:var(--bs-btn-active-border-color)}.btn-check:checked+.btn:focus-visible,.btn.active:focus-visible,.btn.show:focus-visible,.btn:first-child:active:focus-visible,:not(.btn-check)+.btn:active:focus-visible{box-shadow:var(--bs-btn-focus-box-shadow)}.btn.disabled,.btn:disabled,fieldset:disabled .btn{color:var(--bs-btn-disabled-color);pointer-events:none;background-color:var(--bs-btn-disabled-bg);border-color:var(--bs-btn-disabled-border-color);opacity:var(--bs-btn-disabled-opacity)}.btn-primary{--bs-btn-color:#fff;--bs-btn-bg:#0d6efd;--bs-btn-border-color:#0d6efd;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#0b5ed7;--bs-btn-hover-border-color:#0a58ca;--bs-btn-focus-shadow-rgb:49,132,253;--bs-btn-active-color:#fff;--bs-btn-active-bg:#0a58ca;--bs-btn-active-border-color:#0a53be;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#0d6efd;--bs-btn-disabled-border-color:#0d6efd}.btn-secondary{--bs-btn-color:#fff;--bs-btn-bg:#6c757d;--bs-btn-border-color:#6c757d;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#5c636a;--bs-btn-hover-border-color:#565e64;--bs-btn-focus-shadow-rgb:130,138,145;--bs-btn-active-color:#fff;--bs-btn-active-bg:#565e64;--bs-btn-active-border-color:#51585e;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#6c757d;--bs-btn-disabled-border-color:#6c757d}.btn-success{--bs-btn-color:#fff;--bs-btn-bg:#198754;--bs-btn-border-color:#198754;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#157347;--bs-btn-hover-border-color:#146c43;--bs-btn-focus-shadow-rgb:60,153,110;--bs-btn-active-color:#fff;--bs-btn-active-bg:#146c43;--bs-btn-active-border-color:#13653f;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#198754;--bs-btn-disabled-border-color:#198754}.btn-info{--bs-btn-color:#000;--bs-btn-bg:#0dcaf0;--bs-btn-border-color:#0dcaf0;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#31d2f2;--bs-btn-hover-border-color:#25cff2;--bs-btn-focus-shadow-rgb:11,172,204;--bs-btn-active-color:#000;--bs-btn-active-bg:#3dd5f3;--bs-btn-active-border-color:#25cff2;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#0dcaf0;--bs-btn-disabled-border-color:#0dcaf0}.btn-warning{--bs-btn-color:#000;--bs-btn-bg:#ffc107;--bs-btn-border-color:#ffc107;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#ffca2c;--bs-btn-hover-border-color:#ffc720;--bs-btn-focus-shadow-rgb:217,164,6;--bs-btn-active-color:#000;--bs-btn-active-bg:#ffcd39;--bs-btn-active-border-color:#ffc720;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#ffc107;--bs-btn-disabled-border-color:#ffc107}.btn-danger{--bs-btn-color:#fff;--bs-btn-bg:#dc3545;--bs-btn-border-color:#dc3545;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#bb2d3b;--bs-btn-hover-border-color:#b02a37;--bs-btn-focus-shadow-rgb:225,83,97;--bs-btn-active-color:#fff;--bs-btn-active-bg:#b02a37;--bs-btn-active-border-color:#a52834;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#dc3545;--bs-btn-disabled-border-color:#dc3545}.btn-light{--bs-btn-color:#000;--bs-btn-bg:#f8f9fa;--bs-btn-border-color:#f8f9fa;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#d3d4d5;--bs-btn-hover-border-color:#c6c7c8;--bs-btn-focus-shadow-rgb:211,212,213;--bs-btn-active-color:#000;--bs-btn-active-bg:#c6c7c8;--bs-btn-active-border-color:#babbbc;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#f8f9fa;--bs-btn-disabled-border-color:#f8f9fa}.btn-dark{--bs-btn-color:#fff;--bs-btn-bg:#212529;--bs-btn-border-color:#212529;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#424649;--bs-btn-hover-border-color:#373b3e;--bs-btn-focus-shadow-rgb:66,70,73;--bs-btn-active-color:#fff;--bs-btn-active-bg:#4d5154;--bs-btn-active-border-color:#373b3e;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#212529;--bs-btn-disabled-border-color:#212529}.btn-outline-primary{--bs-btn-color:#0d6efd;--bs-btn-border-color:#0d6efd;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#0d6efd;--bs-btn-hover-border-color:#0d6efd;--bs-btn-focus-shadow-rgb:13,110,253;--bs-btn-active-color:#fff;--bs-btn-active-bg:#0d6efd;--bs-btn-active-border-color:#0d6efd;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#0d6efd;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#0d6efd;--bs-gradient:none}.btn-outline-secondary{--bs-btn-color:#6c757d;--bs-btn-border-color:#6c757d;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#6c757d;--bs-btn-hover-border-color:#6c757d;--bs-btn-focus-shadow-rgb:108,117,125;--bs-btn-active-color:#fff;--bs-btn-active-bg:#6c757d;--bs-btn-active-border-color:#6c757d;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#6c757d;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#6c757d;--bs-gradient:none}.btn-outline-success{--bs-btn-color:#198754;--bs-btn-border-color:#198754;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#198754;--bs-btn-hover-border-color:#198754;--bs-btn-focus-shadow-rgb:25,135,84;--bs-btn-active-color:#fff;--bs-btn-active-bg:#198754;--bs-btn-active-border-color:#198754;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#198754;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#198754;--bs-gradient:none}.btn-outline-info{--bs-btn-color:#0dcaf0;--bs-btn-border-color:#0dcaf0;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#0dcaf0;--bs-btn-hover-border-color:#0dcaf0;--bs-btn-focus-shadow-rgb:13,202,240;--bs-btn-active-color:#000;--bs-btn-active-bg:#0dcaf0;--bs-btn-active-border-color:#0dcaf0;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#0dcaf0;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#0dcaf0;--bs-gradient:none}.btn-outline-warning{--bs-btn-color:#ffc107;--bs-btn-border-color:#ffc107;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#ffc107;--bs-btn-hover-border-color:#ffc107;--bs-btn-focus-shadow-rgb:255,193,7;--bs-btn-active-color:#000;--bs-btn-active-bg:#ffc107;--bs-btn-active-border-color:#ffc107;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#ffc107;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#ffc107;--bs-gradient:none}.btn-outline-danger{--bs-btn-color:#dc3545;--bs-btn-border-color:#dc3545;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#dc3545;--bs-btn-hover-border-color:#dc3545;--bs-btn-focus-shadow-rgb:220,53,69;--bs-btn-active-color:#fff;--bs-btn-active-bg:#dc3545;--bs-btn-active-border-color:#dc3545;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#dc3545;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#dc3545;--bs-gradient:none}.btn-outline-light{--bs-btn-color:#f8f9fa;--bs-btn-border-color:#f8f9fa;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#f8f9fa;--bs-btn-hover-border-color:#f8f9fa;--bs-btn-focus-shadow-rgb:248,249,250;--bs-btn-active-color:#000;--bs-btn-active-bg:#f8f9fa;--bs-btn-active-border-color:#f8f9fa;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#f8f9fa;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#f8f9fa;--bs-gradient:none}.btn-outline-dark{--bs-btn-color:#212529;--bs-btn-border-color:#212529;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#212529;--bs-btn-hover-border-color:#212529;--bs-btn-focus-shadow-rgb:33,37,41;--bs-btn-active-color:#fff;--bs-btn-active-bg:#212529;--bs-btn-active-border-color:#212529;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#212529;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#212529;--bs-gradient:none}.btn-link{--bs-btn-font-weight:400;--bs-btn-color:var(--bs-link-color);--bs-btn-bg:transparent;--bs-btn-border-color:transparent;--bs-btn-hover-color:var(--bs-link-hover-color);--bs-btn-hover-border-color:transparent;--bs-btn-active-color:var(--bs-link-hover-color);--bs-btn-active-border-color:transparent;--bs-btn-disabled-color:#6c757d;--bs-btn-disabled-border-color:transparent;--bs-btn-box-shadow:0 0 0 #000;--bs-btn-focus-shadow-rgb:49,132,253;text-decoration:underline}.btn-link:focus-visible{color:var(--bs-btn-color)}.btn-link:hover{color:var(--bs-btn-hover-color)}.btn-group-lg>.btn,.btn-lg{--bs-btn-padding-y:0.5rem;--bs-btn-padding-x:1rem;--bs-btn-font-size:1.25rem;--bs-btn-border-radius:var(--bs-border-radius-lg)}.btn-group-sm>.btn,.btn-sm{--bs-btn-padding-y:0.25rem;--bs-btn-padding-x:0.5rem;--bs-btn-font-size:0.875rem;--bs-btn-border-radius:var(--bs-border-radius-sm)}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.collapsing.collapse-horizontal{width:0;height:auto;transition:width .35s ease}@media (prefers-reduced-motion:reduce){.collapsing.collapse-horizontal{transition:none}}.dropdown,.dropdown-center,.dropend,.dropstart,.dropup,.dropup-center{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{--bs-dropdown-zindex:1000;--bs-dropdown-min-width:10rem;--bs-dropdown-padding-x:0;--bs-dropdown-padding-y:0.5rem;--bs-dropdown-spacer:0.125rem;--bs-dropdown-font-size:1rem;--bs-dropdown-color:var(--bs-body-color);--bs-dropdown-bg:var(--bs-body-bg);--bs-dropdown-border-color:var(--bs-border-color-translucent);--bs-dropdown-border-radius:var(--bs-border-radius);--bs-dropdown-border-width:var(--bs-border-width);--bs-dropdown-inner-border-radius:calc(var(--bs-border-radius) - var(--bs-border-width));--bs-dropdown-divider-bg:var(--bs-border-color-translucent);--bs-dropdown-divider-margin-y:0.5rem;--bs-dropdown-box-shadow:var(--bs-box-shadow);--bs-dropdown-link-color:var(--bs-body-color);--bs-dropdown-link-hover-color:var(--bs-body-color);--bs-dropdown-link-hover-bg:var(--bs-tertiary-bg);--bs-dropdown-link-active-color:#fff;--bs-dropdown-link-active-bg:#0d6efd;--bs-dropdown-link-disabled-color:var(--bs-tertiary-color);--bs-dropdown-item-padding-x:1rem;--bs-dropdown-item-padding-y:0.25rem;--bs-dropdown-header-color:#6c757d;--bs-dropdown-header-padding-x:1rem;--bs-dropdown-header-padding-y:0.5rem;position:absolute;z-index:var(--bs-dropdown-zindex);display:none;min-width:var(--bs-dropdown-min-width);padding:var(--bs-dropdown-padding-y) var(--bs-dropdown-padding-x);margin:0;font-size:var(--bs-dropdown-font-size);color:var(--bs-dropdown-color);text-align:left;list-style:none;background-color:var(--bs-dropdown-bg);background-clip:padding-box;border:var(--bs-dropdown-border-width) solid var(--bs-dropdown-border-color);border-radius:var(--bs-dropdown-border-radius)}.dropdown-menu[data-bs-popper]{top:100%;left:0;margin-top:var(--bs-dropdown-spacer)}.dropdown-menu-start{--bs-position:start}.dropdown-menu-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-end{--bs-position:end}.dropdown-menu-end[data-bs-popper]{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-start{--bs-position:start}.dropdown-menu-sm-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-sm-end{--bs-position:end}.dropdown-menu-sm-end[data-bs-popper]{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-start{--bs-position:start}.dropdown-menu-md-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-md-end{--bs-position:end}.dropdown-menu-md-end[data-bs-popper]{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-start{--bs-position:start}.dropdown-menu-lg-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-lg-end{--bs-position:end}.dropdown-menu-lg-end[data-bs-popper]{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-start{--bs-position:start}.dropdown-menu-xl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xl-end{--bs-position:end}.dropdown-menu-xl-end[data-bs-popper]{right:0;left:auto}}@media (min-width:1400px){.dropdown-menu-xxl-start{--bs-position:start}.dropdown-menu-xxl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xxl-end{--bs-position:end}.dropdown-menu-xxl-end[data-bs-popper]{right:0;left:auto}}.dropup .dropdown-menu[data-bs-popper]{top:auto;bottom:100%;margin-top:0;margin-bottom:var(--bs-dropdown-spacer)}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-menu[data-bs-popper]{top:0;right:auto;left:100%;margin-top:0;margin-left:var(--bs-dropdown-spacer)}.dropend .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropend .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-toggle::after{vertical-align:0}.dropstart .dropdown-menu[data-bs-popper]{top:0;right:100%;left:auto;margin-top:0;margin-right:var(--bs-dropdown-spacer)}.dropstart .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropstart .dropdown-toggle::after{display:none}.dropstart .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropstart .dropdown-toggle:empty::after{margin-left:0}.dropstart .dropdown-toggle::before{vertical-align:0}.dropdown-divider{height:0;margin:var(--bs-dropdown-divider-margin-y) 0;overflow:hidden;border-top:1px solid var(--bs-dropdown-divider-bg);opacity:1}.dropdown-item{display:block;width:100%;padding:var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);clear:both;font-weight:400;color:var(--bs-dropdown-link-color);text-align:inherit;text-decoration:none;white-space:nowrap;background-color:transparent;border:0;border-radius:var(--bs-dropdown-item-border-radius,0)}.dropdown-item:focus,.dropdown-item:hover{color:var(--bs-dropdown-link-hover-color);background-color:var(--bs-dropdown-link-hover-bg)}.dropdown-item.active,.dropdown-item:active{color:var(--bs-dropdown-link-active-color);text-decoration:none;background-color:var(--bs-dropdown-link-active-bg)}.dropdown-item.disabled,.dropdown-item:disabled{color:var(--bs-dropdown-link-disabled-color);pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:var(--bs-dropdown-header-padding-y) var(--bs-dropdown-header-padding-x);margin-bottom:0;font-size:.875rem;color:var(--bs-dropdown-header-color);white-space:nowrap}.dropdown-item-text{display:block;padding:var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);color:var(--bs-dropdown-link-color)}.dropdown-menu-dark{--bs-dropdown-color:#dee2e6;--bs-dropdown-bg:#343a40;--bs-dropdown-border-color:var(--bs-border-color-translucent);--bs-dropdown-box-shadow: ;--bs-dropdown-link-color:#dee2e6;--bs-dropdown-link-hover-color:#fff;--bs-dropdown-divider-bg:var(--bs-border-color-translucent);--bs-dropdown-link-hover-bg:rgba(255, 255, 255, 0.15);--bs-dropdown-link-active-color:#fff;--bs-dropdown-link-active-bg:#0d6efd;--bs-dropdown-link-disabled-color:#adb5bd;--bs-dropdown-header-color:#adb5bd}.btn-group,.btn-group-vertical{position:relative;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;flex:1 1 auto}.btn-group-vertical>.btn-check:checked+.btn,.btn-group-vertical>.btn-check:focus+.btn,.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn-check:checked+.btn,.btn-group>.btn-check:focus+.btn,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:1}.btn-toolbar{display:flex;flex-wrap:wrap;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group{border-radius:var(--bs-border-radius)}.btn-group>.btn-group:not(:first-child),.btn-group>:not(.btn-check:first-child)+.btn{margin-left:calc(var(--bs-border-width) * -1)}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn.dropdown-toggle-split:first-child,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:nth-child(n+3),.btn-group>:not(.btn-check)+.btn{border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropend .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropstart .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{flex-direction:column;align-items:flex-start;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:calc(var(--bs-border-width) * -1)}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn~.btn{border-top-left-radius:0;border-top-right-radius:0}.nav{--bs-nav-link-padding-x:1rem;--bs-nav-link-padding-y:0.5rem;--bs-nav-link-font-weight: ;--bs-nav-link-color:var(--bs-link-color);--bs-nav-link-hover-color:var(--bs-link-hover-color);--bs-nav-link-disabled-color:var(--bs-secondary-color);display:flex;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:var(--bs-nav-link-padding-y) var(--bs-nav-link-padding-x);font-size:var(--bs-nav-link-font-size);font-weight:var(--bs-nav-link-font-weight);color:var(--bs-nav-link-color);text-decoration:none;background:0 0;border:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out}@media (prefers-reduced-motion:reduce){.nav-link{transition:none}}.nav-link:focus,.nav-link:hover{color:var(--bs-nav-link-hover-color)}.nav-link:focus-visible{outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.nav-link.disabled,.nav-link:disabled{color:var(--bs-nav-link-disabled-color);pointer-events:none;cursor:default}.nav-tabs{--bs-nav-tabs-border-width:var(--bs-border-width);--bs-nav-tabs-border-color:var(--bs-border-color);--bs-nav-tabs-border-radius:var(--bs-border-radius);--bs-nav-tabs-link-hover-border-color:var(--bs-secondary-bg) var(--bs-secondary-bg) var(--bs-border-color);--bs-nav-tabs-link-active-color:var(--bs-emphasis-color);--bs-nav-tabs-link-active-bg:var(--bs-body-bg);--bs-nav-tabs-link-active-border-color:var(--bs-border-color) var(--bs-border-color) var(--bs-body-bg);border-bottom:var(--bs-nav-tabs-border-width) solid var(--bs-nav-tabs-border-color)}.nav-tabs .nav-link{margin-bottom:calc(-1 * var(--bs-nav-tabs-border-width));border:var(--bs-nav-tabs-border-width) solid transparent;border-top-left-radius:var(--bs-nav-tabs-border-radius);border-top-right-radius:var(--bs-nav-tabs-border-radius)}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{isolation:isolate;border-color:var(--bs-nav-tabs-link-hover-border-color)}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:var(--bs-nav-tabs-link-active-color);background-color:var(--bs-nav-tabs-link-active-bg);border-color:var(--bs-nav-tabs-link-active-border-color)}.nav-tabs .dropdown-menu{margin-top:calc(-1 * var(--bs-nav-tabs-border-width));border-top-left-radius:0;border-top-right-radius:0}.nav-pills{--bs-nav-pills-border-radius:var(--bs-border-radius);--bs-nav-pills-link-active-color:#fff;--bs-nav-pills-link-active-bg:#0d6efd}.nav-pills .nav-link{border-radius:var(--bs-nav-pills-border-radius)}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:var(--bs-nav-pills-link-active-color);background-color:var(--bs-nav-pills-link-active-bg)}.nav-underline{--bs-nav-underline-gap:1rem;--bs-nav-underline-border-width:0.125rem;--bs-nav-underline-link-active-color:var(--bs-emphasis-color);gap:var(--bs-nav-underline-gap)}.nav-underline .nav-link{padding-right:0;padding-left:0;border-bottom:var(--bs-nav-underline-border-width) solid transparent}.nav-underline .nav-link:focus,.nav-underline .nav-link:hover{border-bottom-color:currentcolor}.nav-underline .nav-link.active,.nav-underline .show>.nav-link{font-weight:700;color:var(--bs-nav-underline-link-active-color);border-bottom-color:currentcolor}.nav-fill .nav-item,.nav-fill>.nav-link{flex:1 1 auto;text-align:center}.nav-justified .nav-item,.nav-justified>.nav-link{flex-basis:0;flex-grow:1;text-align:center}.nav-fill .nav-item .nav-link,.nav-justified .nav-item .nav-link{width:100%}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{--bs-navbar-padding-x:0;--bs-navbar-padding-y:0.5rem;--bs-navbar-color:rgba(var(--bs-emphasis-color-rgb), 0.65);--bs-navbar-hover-color:rgba(var(--bs-emphasis-color-rgb), 0.8);--bs-navbar-disabled-color:rgba(var(--bs-emphasis-color-rgb), 0.3);--bs-navbar-active-color:rgba(var(--bs-emphasis-color-rgb), 1);--bs-navbar-brand-padding-y:0.3125rem;--bs-navbar-brand-margin-end:1rem;--bs-navbar-brand-font-size:1.25rem;--bs-navbar-brand-color:rgba(var(--bs-emphasis-color-rgb), 1);--bs-navbar-brand-hover-color:rgba(var(--bs-emphasis-color-rgb), 1);--bs-navbar-nav-link-padding-x:0.5rem;--bs-navbar-toggler-padding-y:0.25rem;--bs-navbar-toggler-padding-x:0.75rem;--bs-navbar-toggler-font-size:1.25rem;--bs-navbar-toggler-icon-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%2833, 37, 41, 0.75%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");--bs-navbar-toggler-border-color:rgba(var(--bs-emphasis-color-rgb), 0.15);--bs-navbar-toggler-border-radius:var(--bs-border-radius);--bs-navbar-toggler-focus-width:0.25rem;--bs-navbar-toggler-transition:box-shadow 0.15s ease-in-out;position:relative;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;padding:var(--bs-navbar-padding-y) var(--bs-navbar-padding-x)}.navbar>.container,.navbar>.container-fluid,.navbar>.container-lg,.navbar>.container-md,.navbar>.container-sm,.navbar>.container-xl,.navbar>.container-xxl{display:flex;flex-wrap:inherit;align-items:center;justify-content:space-between}.navbar-brand{padding-top:var(--bs-navbar-brand-padding-y);padding-bottom:var(--bs-navbar-brand-padding-y);margin-right:var(--bs-navbar-brand-margin-end);font-size:var(--bs-navbar-brand-font-size);color:var(--bs-navbar-brand-color);text-decoration:none;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{color:var(--bs-navbar-brand-hover-color)}.navbar-nav{--bs-nav-link-padding-x:0;--bs-nav-link-padding-y:0.5rem;--bs-nav-link-font-weight: ;--bs-nav-link-color:var(--bs-navbar-color);--bs-nav-link-hover-color:var(--bs-navbar-hover-color);--bs-nav-link-disabled-color:var(--bs-navbar-disabled-color);display:flex;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link.active,.navbar-nav .nav-link.show{color:var(--bs-navbar-active-color)}.navbar-nav .dropdown-menu{position:static}.navbar-text{padding-top:.5rem;padding-bottom:.5rem;color:var(--bs-navbar-color)}.navbar-text a,.navbar-text a:focus,.navbar-text a:hover{color:var(--bs-navbar-active-color)}.navbar-collapse{flex-basis:100%;flex-grow:1;align-items:center}.navbar-toggler{padding:var(--bs-navbar-toggler-padding-y) var(--bs-navbar-toggler-padding-x);font-size:var(--bs-navbar-toggler-font-size);line-height:1;color:var(--bs-navbar-color);background-color:transparent;border:var(--bs-border-width) solid var(--bs-navbar-toggler-border-color);border-radius:var(--bs-navbar-toggler-border-radius);transition:var(--bs-navbar-toggler-transition)}@media (prefers-reduced-motion:reduce){.navbar-toggler{transition:none}}.navbar-toggler:hover{text-decoration:none}.navbar-toggler:focus{text-decoration:none;outline:0;box-shadow:0 0 0 var(--bs-navbar-toggler-focus-width)}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;background-image:var(--bs-navbar-toggler-icon-bg);background-repeat:no-repeat;background-position:center;background-size:100%}.navbar-nav-scroll{max-height:var(--bs-scroll-height,75vh);overflow-y:auto}@media (min-width:576px){.navbar-expand-sm{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-sm .navbar-nav{flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-sm .navbar-nav-scroll{overflow:visible}.navbar-expand-sm .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}.navbar-expand-sm .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-sm .offcanvas .offcanvas-header{display:none}.navbar-expand-sm .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:768px){.navbar-expand-md{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-md .navbar-nav{flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-md .navbar-nav-scroll{overflow:visible}.navbar-expand-md .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}.navbar-expand-md .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-md .offcanvas .offcanvas-header{display:none}.navbar-expand-md .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:992px){.navbar-expand-lg{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-lg .navbar-nav{flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-lg .navbar-nav-scroll{overflow:visible}.navbar-expand-lg .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}.navbar-expand-lg .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-lg .offcanvas .offcanvas-header{display:none}.navbar-expand-lg .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:1200px){.navbar-expand-xl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xl .navbar-nav{flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-xl .navbar-nav-scroll{overflow:visible}.navbar-expand-xl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}.navbar-expand-xl .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-xl .offcanvas .offcanvas-header{display:none}.navbar-expand-xl .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:1400px){.navbar-expand-xxl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xxl .navbar-nav{flex-direction:row}.navbar-expand-xxl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xxl .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-xxl .navbar-nav-scroll{overflow:visible}.navbar-expand-xxl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xxl .navbar-toggler{display:none}.navbar-expand-xxl .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-xxl .offcanvas .offcanvas-header{display:none}.navbar-expand-xxl .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}.navbar-expand{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand .navbar-nav{flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand .navbar-nav-scroll{overflow:visible}.navbar-expand .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-expand .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand .offcanvas .offcanvas-header{display:none}.navbar-expand .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}.navbar-dark,.navbar[data-bs-theme=dark]{--bs-navbar-color:rgba(255, 255, 255, 0.55);--bs-navbar-hover-color:rgba(255, 255, 255, 0.75);--bs-navbar-disabled-color:rgba(255, 255, 255, 0.25);--bs-navbar-active-color:#fff;--bs-navbar-brand-color:#fff;--bs-navbar-brand-hover-color:#fff;--bs-navbar-toggler-border-color:rgba(255, 255, 255, 0.1);--bs-navbar-toggler-icon-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}[data-bs-theme=dark] .navbar-toggler-icon{--bs-navbar-toggler-icon-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.card{--bs-card-spacer-y:1rem;--bs-card-spacer-x:1rem;--bs-card-title-spacer-y:0.5rem;--bs-card-title-color: ;--bs-card-subtitle-color: ;--bs-card-border-width:var(--bs-border-width);--bs-card-border-color:var(--bs-border-color-translucent);--bs-card-border-radius:var(--bs-border-radius);--bs-card-box-shadow: ;--bs-card-inner-border-radius:calc(var(--bs-border-radius) - (var(--bs-border-width)));--bs-card-cap-padding-y:0.5rem;--bs-card-cap-padding-x:1rem;--bs-card-cap-bg:rgba(var(--bs-body-color-rgb), 0.03);--bs-card-cap-color: ;--bs-card-height: ;--bs-card-color: ;--bs-card-bg:var(--bs-body-bg);--bs-card-img-overlay-padding:1rem;--bs-card-group-margin:0.75rem;position:relative;display:flex;flex-direction:column;min-width:0;height:var(--bs-card-height);color:var(--bs-body-color);word-wrap:break-word;background-color:var(--bs-card-bg);background-clip:border-box;border:var(--bs-card-border-width) solid var(--bs-card-border-color);border-radius:var(--bs-card-border-radius)}.card>hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:var(--bs-card-inner-border-radius);border-top-right-radius:var(--bs-card-inner-border-radius)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:var(--bs-card-inner-border-radius);border-bottom-left-radius:var(--bs-card-inner-border-radius)}.card>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0}.card-body{flex:1 1 auto;padding:var(--bs-card-spacer-y) var(--bs-card-spacer-x);color:var(--bs-card-color)}.card-title{margin-bottom:var(--bs-card-title-spacer-y);color:var(--bs-card-title-color)}.card-subtitle{margin-top:calc(-.5 * var(--bs-card-title-spacer-y));margin-bottom:0;color:var(--bs-card-subtitle-color)}.card-text:last-child{margin-bottom:0}.card-link+.card-link{margin-left:var(--bs-card-spacer-x)}.card-header{padding:var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x);margin-bottom:0;color:var(--bs-card-cap-color);background-color:var(--bs-card-cap-bg);border-bottom:var(--bs-card-border-width) solid var(--bs-card-border-color)}.card-header:first-child{border-radius:var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius) 0 0}.card-footer{padding:var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x);color:var(--bs-card-cap-color);background-color:var(--bs-card-cap-bg);border-top:var(--bs-card-border-width) solid var(--bs-card-border-color)}.card-footer:last-child{border-radius:0 0 var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius)}.card-header-tabs{margin-right:calc(-.5 * var(--bs-card-cap-padding-x));margin-bottom:calc(-1 * var(--bs-card-cap-padding-y));margin-left:calc(-.5 * var(--bs-card-cap-padding-x));border-bottom:0}.card-header-tabs .nav-link.active{background-color:var(--bs-card-bg);border-bottom-color:var(--bs-card-bg)}.card-header-pills{margin-right:calc(-.5 * var(--bs-card-cap-padding-x));margin-left:calc(-.5 * var(--bs-card-cap-padding-x))}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:var(--bs-card-img-overlay-padding);border-radius:var(--bs-card-inner-border-radius)}.card-img,.card-img-bottom,.card-img-top{width:100%}.card-img,.card-img-top{border-top-left-radius:var(--bs-card-inner-border-radius);border-top-right-radius:var(--bs-card-inner-border-radius)}.card-img,.card-img-bottom{border-bottom-right-radius:var(--bs-card-inner-border-radius);border-bottom-left-radius:var(--bs-card-inner-border-radius)}.card-group>.card{margin-bottom:var(--bs-card-group-margin)}@media (min-width:576px){.card-group{display:flex;flex-flow:row wrap}.card-group>.card{flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.accordion{--bs-accordion-color:var(--bs-body-color);--bs-accordion-bg:var(--bs-body-bg);--bs-accordion-transition:color 0.15s ease-in-out,background-color 0.15s ease-in-out,border-color 0.15s ease-in-out,box-shadow 0.15s ease-in-out,border-radius 0.15s ease;--bs-accordion-border-color:var(--bs-border-color);--bs-accordion-border-width:var(--bs-border-width);--bs-accordion-border-radius:var(--bs-border-radius);--bs-accordion-inner-border-radius:calc(var(--bs-border-radius) - (var(--bs-border-width)));--bs-accordion-btn-padding-x:1.25rem;--bs-accordion-btn-padding-y:1rem;--bs-accordion-btn-color:var(--bs-body-color);--bs-accordion-btn-bg:var(--bs-accordion-bg);--bs-accordion-btn-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23212529'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");--bs-accordion-btn-icon-width:1.25rem;--bs-accordion-btn-icon-transform:rotate(-180deg);--bs-accordion-btn-icon-transition:transform 0.2s ease-in-out;--bs-accordion-btn-active-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23052c65'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");--bs-accordion-btn-focus-border-color:#86b7fe;--bs-accordion-btn-focus-box-shadow:0 0 0 0.25rem rgba(13, 110, 253, 0.25);--bs-accordion-body-padding-x:1.25rem;--bs-accordion-body-padding-y:1rem;--bs-accordion-active-color:var(--bs-primary-text-emphasis);--bs-accordion-active-bg:var(--bs-primary-bg-subtle)}.accordion-button{position:relative;display:flex;align-items:center;width:100%;padding:var(--bs-accordion-btn-padding-y) var(--bs-accordion-btn-padding-x);font-size:1rem;color:var(--bs-accordion-btn-color);text-align:left;background-color:var(--bs-accordion-btn-bg);border:0;border-radius:0;overflow-anchor:none;transition:var(--bs-accordion-transition)}@media (prefers-reduced-motion:reduce){.accordion-button{transition:none}}.accordion-button:not(.collapsed){color:var(--bs-accordion-active-color);background-color:var(--bs-accordion-active-bg);box-shadow:inset 0 calc(-1 * var(--bs-accordion-border-width)) 0 var(--bs-accordion-border-color)}.accordion-button:not(.collapsed)::after{background-image:var(--bs-accordion-btn-active-icon);transform:var(--bs-accordion-btn-icon-transform)}.accordion-button::after{flex-shrink:0;width:var(--bs-accordion-btn-icon-width);height:var(--bs-accordion-btn-icon-width);margin-left:auto;content:"";background-image:var(--bs-accordion-btn-icon);background-repeat:no-repeat;background-size:var(--bs-accordion-btn-icon-width);transition:var(--bs-accordion-btn-icon-transition)}@media (prefers-reduced-motion:reduce){.accordion-button::after{transition:none}}.accordion-button:hover{z-index:2}.accordion-button:focus{z-index:3;border-color:var(--bs-accordion-btn-focus-border-color);outline:0;box-shadow:var(--bs-accordion-btn-focus-box-shadow)}.accordion-header{margin-bottom:0}.accordion-item{color:var(--bs-accordion-color);background-color:var(--bs-accordion-bg);border:var(--bs-accordion-border-width) solid var(--bs-accordion-border-color)}.accordion-item:first-of-type{border-top-left-radius:var(--bs-accordion-border-radius);border-top-right-radius:var(--bs-accordion-border-radius)}.accordion-item:first-of-type .accordion-button{border-top-left-radius:var(--bs-accordion-inner-border-radius);border-top-right-radius:var(--bs-accordion-inner-border-radius)}.accordion-item:not(:first-of-type){border-top:0}.accordion-item:last-of-type{border-bottom-right-radius:var(--bs-accordion-border-radius);border-bottom-left-radius:var(--bs-accordion-border-radius)}.accordion-item:last-of-type .accordion-button.collapsed{border-bottom-right-radius:var(--bs-accordion-inner-border-radius);border-bottom-left-radius:var(--bs-accordion-inner-border-radius)}.accordion-item:last-of-type .accordion-collapse{border-bottom-right-radius:var(--bs-accordion-border-radius);border-bottom-left-radius:var(--bs-accordion-border-radius)}.accordion-body{padding:var(--bs-accordion-body-padding-y) var(--bs-accordion-body-padding-x)}.accordion-flush .accordion-collapse{border-width:0}.accordion-flush .accordion-item{border-right:0;border-left:0;border-radius:0}.accordion-flush .accordion-item:first-child{border-top:0}.accordion-flush .accordion-item:last-child{border-bottom:0}.accordion-flush .accordion-item .accordion-button,.accordion-flush .accordion-item .accordion-button.collapsed{border-radius:0}[data-bs-theme=dark] .accordion-button::after{--bs-accordion-btn-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236ea8fe'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");--bs-accordion-btn-active-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236ea8fe'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e")}.breadcrumb{--bs-breadcrumb-padding-x:0;--bs-breadcrumb-padding-y:0;--bs-breadcrumb-margin-bottom:1rem;--bs-breadcrumb-bg: ;--bs-breadcrumb-border-radius: ;--bs-breadcrumb-divider-color:var(--bs-secondary-color);--bs-breadcrumb-item-padding-x:0.5rem;--bs-breadcrumb-item-active-color:var(--bs-secondary-color);display:flex;flex-wrap:wrap;padding:var(--bs-breadcrumb-padding-y) var(--bs-breadcrumb-padding-x);margin-bottom:var(--bs-breadcrumb-margin-bottom);font-size:var(--bs-breadcrumb-font-size);list-style:none;background-color:var(--bs-breadcrumb-bg);border-radius:var(--bs-breadcrumb-border-radius)}.breadcrumb-item+.breadcrumb-item{padding-left:var(--bs-breadcrumb-item-padding-x)}.breadcrumb-item+.breadcrumb-item::before{float:left;padding-right:var(--bs-breadcrumb-item-padding-x);color:var(--bs-breadcrumb-divider-color);content:var(--bs-breadcrumb-divider, "/")}.breadcrumb-item.active{color:var(--bs-breadcrumb-item-active-color)}.pagination{--bs-pagination-padding-x:0.75rem;--bs-pagination-padding-y:0.375rem;--bs-pagination-font-size:1rem;--bs-pagination-color:var(--bs-link-color);--bs-pagination-bg:var(--bs-body-bg);--bs-pagination-border-width:var(--bs-border-width);--bs-pagination-border-color:var(--bs-border-color);--bs-pagination-border-radius:var(--bs-border-radius);--bs-pagination-hover-color:var(--bs-link-hover-color);--bs-pagination-hover-bg:var(--bs-tertiary-bg);--bs-pagination-hover-border-color:var(--bs-border-color);--bs-pagination-focus-color:var(--bs-link-hover-color);--bs-pagination-focus-bg:var(--bs-secondary-bg);--bs-pagination-focus-box-shadow:0 0 0 0.25rem rgba(13, 110, 253, 0.25);--bs-pagination-active-color:#fff;--bs-pagination-active-bg:#0d6efd;--bs-pagination-active-border-color:#0d6efd;--bs-pagination-disabled-color:var(--bs-secondary-color);--bs-pagination-disabled-bg:var(--bs-secondary-bg);--bs-pagination-disabled-border-color:var(--bs-border-color);display:flex;padding-left:0;list-style:none}.page-link{position:relative;display:block;padding:var(--bs-pagination-padding-y) var(--bs-pagination-padding-x);font-size:var(--bs-pagination-font-size);color:var(--bs-pagination-color);text-decoration:none;background-color:var(--bs-pagination-bg);border:var(--bs-pagination-border-width) solid var(--bs-pagination-border-color);transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.page-link{transition:none}}.page-link:hover{z-index:2;color:var(--bs-pagination-hover-color);background-color:var(--bs-pagination-hover-bg);border-color:var(--bs-pagination-hover-border-color)}.page-link:focus{z-index:3;color:var(--bs-pagination-focus-color);background-color:var(--bs-pagination-focus-bg);outline:0;box-shadow:var(--bs-pagination-focus-box-shadow)}.active>.page-link,.page-link.active{z-index:3;color:var(--bs-pagination-active-color);background-color:var(--bs-pagination-active-bg);border-color:var(--bs-pagination-active-border-color)}.disabled>.page-link,.page-link.disabled{color:var(--bs-pagination-disabled-color);pointer-events:none;background-color:var(--bs-pagination-disabled-bg);border-color:var(--bs-pagination-disabled-border-color)}.page-item:not(:first-child) .page-link{margin-left:calc(var(--bs-border-width) * -1)}.page-item:first-child .page-link{border-top-left-radius:var(--bs-pagination-border-radius);border-bottom-left-radius:var(--bs-pagination-border-radius)}.page-item:last-child .page-link{border-top-right-radius:var(--bs-pagination-border-radius);border-bottom-right-radius:var(--bs-pagination-border-radius)}.pagination-lg{--bs-pagination-padding-x:1.5rem;--bs-pagination-padding-y:0.75rem;--bs-pagination-font-size:1.25rem;--bs-pagination-border-radius:var(--bs-border-radius-lg)}.pagination-sm{--bs-pagination-padding-x:0.5rem;--bs-pagination-padding-y:0.25rem;--bs-pagination-font-size:0.875rem;--bs-pagination-border-radius:var(--bs-border-radius-sm)}.badge{--bs-badge-padding-x:0.65em;--bs-badge-padding-y:0.35em;--bs-badge-font-size:0.75em;--bs-badge-font-weight:700;--bs-badge-color:#fff;--bs-badge-border-radius:var(--bs-border-radius);display:inline-block;padding:var(--bs-badge-padding-y) var(--bs-badge-padding-x);font-size:var(--bs-badge-font-size);font-weight:var(--bs-badge-font-weight);line-height:1;color:var(--bs-badge-color);text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:var(--bs-badge-border-radius)}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.alert{--bs-alert-bg:transparent;--bs-alert-padding-x:1rem;--bs-alert-padding-y:1rem;--bs-alert-margin-bottom:1rem;--bs-alert-color:inherit;--bs-alert-border-color:transparent;--bs-alert-border:var(--bs-border-width) solid var(--bs-alert-border-color);--bs-alert-border-radius:var(--bs-border-radius);--bs-alert-link-color:inherit;position:relative;padding:var(--bs-alert-padding-y) var(--bs-alert-padding-x);margin-bottom:var(--bs-alert-margin-bottom);color:var(--bs-alert-color);background-color:var(--bs-alert-bg);border:var(--bs-alert-border);border-radius:var(--bs-alert-border-radius)}.alert-heading{color:inherit}.alert-link{font-weight:700;color:var(--bs-alert-link-color)}.alert-dismissible{padding-right:3rem}.alert-dismissible .btn-close{position:absolute;top:0;right:0;z-index:2;padding:1.25rem 1rem}.alert-primary{--bs-alert-color:var(--bs-primary-text-emphasis);--bs-alert-bg:var(--bs-primary-bg-subtle);--bs-alert-border-color:var(--bs-primary-border-subtle);--bs-alert-link-color:var(--bs-primary-text-emphasis)}.alert-secondary{--bs-alert-color:var(--bs-secondary-text-emphasis);--bs-alert-bg:var(--bs-secondary-bg-subtle);--bs-alert-border-color:var(--bs-secondary-border-subtle);--bs-alert-link-color:var(--bs-secondary-text-emphasis)}.alert-success{--bs-alert-color:var(--bs-success-text-emphasis);--bs-alert-bg:var(--bs-success-bg-subtle);--bs-alert-border-color:var(--bs-success-border-subtle);--bs-alert-link-color:var(--bs-success-text-emphasis)}.alert-info{--bs-alert-color:var(--bs-info-text-emphasis);--bs-alert-bg:var(--bs-info-bg-subtle);--bs-alert-border-color:var(--bs-info-border-subtle);--bs-alert-link-color:var(--bs-info-text-emphasis)}.alert-warning{--bs-alert-color:var(--bs-warning-text-emphasis);--bs-alert-bg:var(--bs-warning-bg-subtle);--bs-alert-border-color:var(--bs-warning-border-subtle);--bs-alert-link-color:var(--bs-warning-text-emphasis)}.alert-danger{--bs-alert-color:var(--bs-danger-text-emphasis);--bs-alert-bg:var(--bs-danger-bg-subtle);--bs-alert-border-color:var(--bs-danger-border-subtle);--bs-alert-link-color:var(--bs-danger-text-emphasis)}.alert-light{--bs-alert-color:var(--bs-light-text-emphasis);--bs-alert-bg:var(--bs-light-bg-subtle);--bs-alert-border-color:var(--bs-light-border-subtle);--bs-alert-link-color:var(--bs-light-text-emphasis)}.alert-dark{--bs-alert-color:var(--bs-dark-text-emphasis);--bs-alert-bg:var(--bs-dark-bg-subtle);--bs-alert-border-color:var(--bs-dark-border-subtle);--bs-alert-link-color:var(--bs-dark-text-emphasis)}@keyframes progress-bar-stripes{0%{background-position-x:1rem}}.progress,.progress-stacked{--bs-progress-height:1rem;--bs-progress-font-size:0.75rem;--bs-progress-bg:var(--bs-secondary-bg);--bs-progress-border-radius:var(--bs-border-radius);--bs-progress-box-shadow:var(--bs-box-shadow-inset);--bs-progress-bar-color:#fff;--bs-progress-bar-bg:#0d6efd;--bs-progress-bar-transition:width 0.6s ease;display:flex;height:var(--bs-progress-height);overflow:hidden;font-size:var(--bs-progress-font-size);background-color:var(--bs-progress-bg);border-radius:var(--bs-progress-border-radius)}.progress-bar{display:flex;flex-direction:column;justify-content:center;overflow:hidden;color:var(--bs-progress-bar-color);text-align:center;white-space:nowrap;background-color:var(--bs-progress-bar-bg);transition:var(--bs-progress-bar-transition)}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:var(--bs-progress-height) var(--bs-progress-height)}.progress-stacked>.progress{overflow:visible}.progress-stacked>.progress>.progress-bar{width:100%}.progress-bar-animated{animation:1s linear infinite progress-bar-stripes}@media (prefers-reduced-motion:reduce){.progress-bar-animated{animation:none}}.list-group{--bs-list-group-color:var(--bs-body-color);--bs-list-group-bg:var(--bs-body-bg);--bs-list-group-border-color:var(--bs-border-color);--bs-list-group-border-width:var(--bs-border-width);--bs-list-group-border-radius:var(--bs-border-radius);--bs-list-group-item-padding-x:1rem;--bs-list-group-item-padding-y:0.5rem;--bs-list-group-action-color:var(--bs-secondary-color);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-tertiary-bg);--bs-list-group-action-active-color:var(--bs-body-color);--bs-list-group-action-active-bg:var(--bs-secondary-bg);--bs-list-group-disabled-color:var(--bs-secondary-color);--bs-list-group-disabled-bg:var(--bs-body-bg);--bs-list-group-active-color:#fff;--bs-list-group-active-bg:#0d6efd;--bs-list-group-active-border-color:#0d6efd;display:flex;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:var(--bs-list-group-border-radius)}.list-group-numbered{list-style-type:none;counter-reset:section}.list-group-numbered>.list-group-item::before{content:counters(section, ".") ". ";counter-increment:section}.list-group-item-action{width:100%;color:var(--bs-list-group-action-color);text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:var(--bs-list-group-action-hover-color);text-decoration:none;background-color:var(--bs-list-group-action-hover-bg)}.list-group-item-action:active{color:var(--bs-list-group-action-active-color);background-color:var(--bs-list-group-action-active-bg)}.list-group-item{position:relative;display:block;padding:var(--bs-list-group-item-padding-y) var(--bs-list-group-item-padding-x);color:var(--bs-list-group-color);text-decoration:none;background-color:var(--bs-list-group-bg);border:var(--bs-list-group-border-width) solid var(--bs-list-group-border-color)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:var(--bs-list-group-disabled-color);pointer-events:none;background-color:var(--bs-list-group-disabled-bg)}.list-group-item.active{z-index:2;color:var(--bs-list-group-active-color);background-color:var(--bs-list-group-active-bg);border-color:var(--bs-list-group-active-border-color)}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:calc(-1 * var(--bs-list-group-border-width));border-top-width:var(--bs-list-group-border-width)}.list-group-horizontal{flex-direction:row}.list-group-horizontal>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}@media (min-width:576px){.list-group-horizontal-sm{flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media (min-width:768px){.list-group-horizontal-md{flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media (min-width:992px){.list-group-horizontal-lg{flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media (min-width:1200px){.list-group-horizontal-xl{flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media (min-width:1400px){.list-group-horizontal-xxl{flex-direction:row}.list-group-horizontal-xxl>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-xxl>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-xxl>.list-group-item.active{margin-top:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 var(--bs-list-group-border-width)}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{--bs-list-group-color:var(--bs-primary-text-emphasis);--bs-list-group-bg:var(--bs-primary-bg-subtle);--bs-list-group-border-color:var(--bs-primary-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-primary-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-primary-border-subtle);--bs-list-group-active-color:var(--bs-primary-bg-subtle);--bs-list-group-active-bg:var(--bs-primary-text-emphasis);--bs-list-group-active-border-color:var(--bs-primary-text-emphasis)}.list-group-item-secondary{--bs-list-group-color:var(--bs-secondary-text-emphasis);--bs-list-group-bg:var(--bs-secondary-bg-subtle);--bs-list-group-border-color:var(--bs-secondary-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-secondary-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-secondary-border-subtle);--bs-list-group-active-color:var(--bs-secondary-bg-subtle);--bs-list-group-active-bg:var(--bs-secondary-text-emphasis);--bs-list-group-active-border-color:var(--bs-secondary-text-emphasis)}.list-group-item-success{--bs-list-group-color:var(--bs-success-text-emphasis);--bs-list-group-bg:var(--bs-success-bg-subtle);--bs-list-group-border-color:var(--bs-success-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-success-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-success-border-subtle);--bs-list-group-active-color:var(--bs-success-bg-subtle);--bs-list-group-active-bg:var(--bs-success-text-emphasis);--bs-list-group-active-border-color:var(--bs-success-text-emphasis)}.list-group-item-info{--bs-list-group-color:var(--bs-info-text-emphasis);--bs-list-group-bg:var(--bs-info-bg-subtle);--bs-list-group-border-color:var(--bs-info-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-info-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-info-border-subtle);--bs-list-group-active-color:var(--bs-info-bg-subtle);--bs-list-group-active-bg:var(--bs-info-text-emphasis);--bs-list-group-active-border-color:var(--bs-info-text-emphasis)}.list-group-item-warning{--bs-list-group-color:var(--bs-warning-text-emphasis);--bs-list-group-bg:var(--bs-warning-bg-subtle);--bs-list-group-border-color:var(--bs-warning-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-warning-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-warning-border-subtle);--bs-list-group-active-color:var(--bs-warning-bg-subtle);--bs-list-group-active-bg:var(--bs-warning-text-emphasis);--bs-list-group-active-border-color:var(--bs-warning-text-emphasis)}.list-group-item-danger{--bs-list-group-color:var(--bs-danger-text-emphasis);--bs-list-group-bg:var(--bs-danger-bg-subtle);--bs-list-group-border-color:var(--bs-danger-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-danger-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-danger-border-subtle);--bs-list-group-active-color:var(--bs-danger-bg-subtle);--bs-list-group-active-bg:var(--bs-danger-text-emphasis);--bs-list-group-active-border-color:var(--bs-danger-text-emphasis)}.list-group-item-light{--bs-list-group-color:var(--bs-light-text-emphasis);--bs-list-group-bg:var(--bs-light-bg-subtle);--bs-list-group-border-color:var(--bs-light-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-light-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-light-border-subtle);--bs-list-group-active-color:var(--bs-light-bg-subtle);--bs-list-group-active-bg:var(--bs-light-text-emphasis);--bs-list-group-active-border-color:var(--bs-light-text-emphasis)}.list-group-item-dark{--bs-list-group-color:var(--bs-dark-text-emphasis);--bs-list-group-bg:var(--bs-dark-bg-subtle);--bs-list-group-border-color:var(--bs-dark-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-dark-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-dark-border-subtle);--bs-list-group-active-color:var(--bs-dark-bg-subtle);--bs-list-group-active-bg:var(--bs-dark-text-emphasis);--bs-list-group-active-border-color:var(--bs-dark-text-emphasis)}.btn-close{--bs-btn-close-color:#000;--bs-btn-close-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/%3e%3c/svg%3e");--bs-btn-close-opacity:0.5;--bs-btn-close-hover-opacity:0.75;--bs-btn-close-focus-shadow:0 0 0 0.25rem rgba(13, 110, 253, 0.25);--bs-btn-close-focus-opacity:1;--bs-btn-close-disabled-opacity:0.25;--bs-btn-close-white-filter:invert(1) grayscale(100%) brightness(200%);box-sizing:content-box;width:1em;height:1em;padding:.25em .25em;color:var(--bs-btn-close-color);background:transparent var(--bs-btn-close-bg) center/1em auto no-repeat;border:0;border-radius:.375rem;opacity:var(--bs-btn-close-opacity)}.btn-close:hover{color:var(--bs-btn-close-color);text-decoration:none;opacity:var(--bs-btn-close-hover-opacity)}.btn-close:focus{outline:0;box-shadow:var(--bs-btn-close-focus-shadow);opacity:var(--bs-btn-close-focus-opacity)}.btn-close.disabled,.btn-close:disabled{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;opacity:var(--bs-btn-close-disabled-opacity)}.btn-close-white{filter:var(--bs-btn-close-white-filter)}[data-bs-theme=dark] .btn-close{filter:var(--bs-btn-close-white-filter)}.toast{--bs-toast-zindex:1090;--bs-toast-padding-x:0.75rem;--bs-toast-padding-y:0.5rem;--bs-toast-spacing:1.5rem;--bs-toast-max-width:350px;--bs-toast-font-size:0.875rem;--bs-toast-color: ;--bs-toast-bg:rgba(var(--bs-body-bg-rgb), 0.85);--bs-toast-border-width:var(--bs-border-width);--bs-toast-border-color:var(--bs-border-color-translucent);--bs-toast-border-radius:var(--bs-border-radius);--bs-toast-box-shadow:var(--bs-box-shadow);--bs-toast-header-color:var(--bs-secondary-color);--bs-toast-header-bg:rgba(var(--bs-body-bg-rgb), 0.85);--bs-toast-header-border-color:var(--bs-border-color-translucent);width:var(--bs-toast-max-width);max-width:100%;font-size:var(--bs-toast-font-size);color:var(--bs-toast-color);pointer-events:auto;background-color:var(--bs-toast-bg);background-clip:padding-box;border:var(--bs-toast-border-width) solid var(--bs-toast-border-color);box-shadow:var(--bs-toast-box-shadow);border-radius:var(--bs-toast-border-radius)}.toast.showing{opacity:0}.toast:not(.show){display:none}.toast-container{--bs-toast-zindex:1090;position:absolute;z-index:var(--bs-toast-zindex);width:-webkit-max-content;width:-moz-max-content;width:max-content;max-width:100%;pointer-events:none}.toast-container>:not(:last-child){margin-bottom:var(--bs-toast-spacing)}.toast-header{display:flex;align-items:center;padding:var(--bs-toast-padding-y) var(--bs-toast-padding-x);color:var(--bs-toast-header-color);background-color:var(--bs-toast-header-bg);background-clip:padding-box;border-bottom:var(--bs-toast-border-width) solid var(--bs-toast-header-border-color);border-top-left-radius:calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width));border-top-right-radius:calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width))}.toast-header .btn-close{margin-right:calc(-.5 * var(--bs-toast-padding-x));margin-left:var(--bs-toast-padding-x)}.toast-body{padding:var(--bs-toast-padding-x);word-wrap:break-word}.modal{--bs-modal-zindex:1055;--bs-modal-width:500px;--bs-modal-padding:1rem;--bs-modal-margin:0.5rem;--bs-modal-color: ;--bs-modal-bg:var(--bs-body-bg);--bs-modal-border-color:var(--bs-border-color-translucent);--bs-modal-border-width:var(--bs-border-width);--bs-modal-border-radius:var(--bs-border-radius-lg);--bs-modal-box-shadow:var(--bs-box-shadow-sm);--bs-modal-inner-border-radius:calc(var(--bs-border-radius-lg) - (var(--bs-border-width)));--bs-modal-header-padding-x:1rem;--bs-modal-header-padding-y:1rem;--bs-modal-header-padding:1rem 1rem;--bs-modal-header-border-color:var(--bs-border-color);--bs-modal-header-border-width:var(--bs-border-width);--bs-modal-title-line-height:1.5;--bs-modal-footer-gap:0.5rem;--bs-modal-footer-bg: ;--bs-modal-footer-border-color:var(--bs-border-color);--bs-modal-footer-border-width:var(--bs-border-width);position:fixed;top:0;left:0;z-index:var(--bs-modal-zindex);display:none;width:100%;height:100%;overflow-x:hidden;overflow-y:auto;outline:0}.modal-dialog{position:relative;width:auto;margin:var(--bs-modal-margin);pointer-events:none}.modal.fade .modal-dialog{transition:transform .3s ease-out;transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{transform:none}.modal.modal-static .modal-dialog{transform:scale(1.02)}.modal-dialog-scrollable{height:calc(100% - var(--bs-modal-margin) * 2)}.modal-dialog-scrollable .modal-content{max-height:100%;overflow:hidden}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:flex;align-items:center;min-height:calc(100% - var(--bs-modal-margin) * 2)}.modal-content{position:relative;display:flex;flex-direction:column;width:100%;color:var(--bs-modal-color);pointer-events:auto;background-color:var(--bs-modal-bg);background-clip:padding-box;border:var(--bs-modal-border-width) solid var(--bs-modal-border-color);border-radius:var(--bs-modal-border-radius);outline:0}.modal-backdrop{--bs-backdrop-zindex:1050;--bs-backdrop-bg:#000;--bs-backdrop-opacity:0.5;position:fixed;top:0;left:0;z-index:var(--bs-backdrop-zindex);width:100vw;height:100vh;background-color:var(--bs-backdrop-bg)}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:var(--bs-backdrop-opacity)}.modal-header{display:flex;flex-shrink:0;align-items:center;justify-content:space-between;padding:var(--bs-modal-header-padding);border-bottom:var(--bs-modal-header-border-width) solid var(--bs-modal-header-border-color);border-top-left-radius:var(--bs-modal-inner-border-radius);border-top-right-radius:var(--bs-modal-inner-border-radius)}.modal-header .btn-close{padding:calc(var(--bs-modal-header-padding-y) * .5) calc(var(--bs-modal-header-padding-x) * .5);margin:calc(-.5 * var(--bs-modal-header-padding-y)) calc(-.5 * var(--bs-modal-header-padding-x)) calc(-.5 * var(--bs-modal-header-padding-y)) auto}.modal-title{margin-bottom:0;line-height:var(--bs-modal-title-line-height)}.modal-body{position:relative;flex:1 1 auto;padding:var(--bs-modal-padding)}.modal-footer{display:flex;flex-shrink:0;flex-wrap:wrap;align-items:center;justify-content:flex-end;padding:calc(var(--bs-modal-padding) - var(--bs-modal-footer-gap) * .5);background-color:var(--bs-modal-footer-bg);border-top:var(--bs-modal-footer-border-width) solid var(--bs-modal-footer-border-color);border-bottom-right-radius:var(--bs-modal-inner-border-radius);border-bottom-left-radius:var(--bs-modal-inner-border-radius)}.modal-footer>*{margin:calc(var(--bs-modal-footer-gap) * .5)}@media (min-width:576px){.modal{--bs-modal-margin:1.75rem;--bs-modal-box-shadow:var(--bs-box-shadow)}.modal-dialog{max-width:var(--bs-modal-width);margin-right:auto;margin-left:auto}.modal-sm{--bs-modal-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{--bs-modal-width:800px}}@media (min-width:1200px){.modal-xl{--bs-modal-width:1140px}}.modal-fullscreen{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen .modal-footer,.modal-fullscreen .modal-header{border-radius:0}.modal-fullscreen .modal-body{overflow-y:auto}@media (max-width:575.98px){.modal-fullscreen-sm-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-sm-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-sm-down .modal-footer,.modal-fullscreen-sm-down .modal-header{border-radius:0}.modal-fullscreen-sm-down .modal-body{overflow-y:auto}}@media (max-width:767.98px){.modal-fullscreen-md-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-md-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-md-down .modal-footer,.modal-fullscreen-md-down .modal-header{border-radius:0}.modal-fullscreen-md-down .modal-body{overflow-y:auto}}@media (max-width:991.98px){.modal-fullscreen-lg-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-lg-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-lg-down .modal-footer,.modal-fullscreen-lg-down .modal-header{border-radius:0}.modal-fullscreen-lg-down .modal-body{overflow-y:auto}}@media (max-width:1199.98px){.modal-fullscreen-xl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xl-down .modal-footer,.modal-fullscreen-xl-down .modal-header{border-radius:0}.modal-fullscreen-xl-down .modal-body{overflow-y:auto}}@media (max-width:1399.98px){.modal-fullscreen-xxl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xxl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xxl-down .modal-footer,.modal-fullscreen-xxl-down .modal-header{border-radius:0}.modal-fullscreen-xxl-down .modal-body{overflow-y:auto}}.tooltip{--bs-tooltip-zindex:1080;--bs-tooltip-max-width:200px;--bs-tooltip-padding-x:0.5rem;--bs-tooltip-padding-y:0.25rem;--bs-tooltip-margin: ;--bs-tooltip-font-size:0.875rem;--bs-tooltip-color:var(--bs-body-bg);--bs-tooltip-bg:var(--bs-emphasis-color);--bs-tooltip-border-radius:var(--bs-border-radius);--bs-tooltip-opacity:0.9;--bs-tooltip-arrow-width:0.8rem;--bs-tooltip-arrow-height:0.4rem;z-index:var(--bs-tooltip-zindex);display:block;margin:var(--bs-tooltip-margin);font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:var(--bs-tooltip-font-size);word-wrap:break-word;opacity:0}.tooltip.show{opacity:var(--bs-tooltip-opacity)}.tooltip .tooltip-arrow{display:block;width:var(--bs-tooltip-arrow-width);height:var(--bs-tooltip-arrow-height)}.tooltip .tooltip-arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow,.bs-tooltip-top .tooltip-arrow{bottom:calc(-1 * var(--bs-tooltip-arrow-height))}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before,.bs-tooltip-top .tooltip-arrow::before{top:-1px;border-width:var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * .5) 0;border-top-color:var(--bs-tooltip-bg)}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow,.bs-tooltip-end .tooltip-arrow{left:calc(-1 * var(--bs-tooltip-arrow-height));width:var(--bs-tooltip-arrow-height);height:var(--bs-tooltip-arrow-width)}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before,.bs-tooltip-end .tooltip-arrow::before{right:-1px;border-width:calc(var(--bs-tooltip-arrow-width) * .5) var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * .5) 0;border-right-color:var(--bs-tooltip-bg)}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow,.bs-tooltip-bottom .tooltip-arrow{top:calc(-1 * var(--bs-tooltip-arrow-height))}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before,.bs-tooltip-bottom .tooltip-arrow::before{bottom:-1px;border-width:0 calc(var(--bs-tooltip-arrow-width) * .5) var(--bs-tooltip-arrow-height);border-bottom-color:var(--bs-tooltip-bg)}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow,.bs-tooltip-start .tooltip-arrow{right:calc(-1 * var(--bs-tooltip-arrow-height));width:var(--bs-tooltip-arrow-height);height:var(--bs-tooltip-arrow-width)}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before,.bs-tooltip-start .tooltip-arrow::before{left:-1px;border-width:calc(var(--bs-tooltip-arrow-width) * .5) 0 calc(var(--bs-tooltip-arrow-width) * .5) var(--bs-tooltip-arrow-height);border-left-color:var(--bs-tooltip-bg)}.tooltip-inner{max-width:var(--bs-tooltip-max-width);padding:var(--bs-tooltip-padding-y) var(--bs-tooltip-padding-x);color:var(--bs-tooltip-color);text-align:center;background-color:var(--bs-tooltip-bg);border-radius:var(--bs-tooltip-border-radius)}.popover{--bs-popover-zindex:1070;--bs-popover-max-width:276px;--bs-popover-font-size:0.875rem;--bs-popover-bg:var(--bs-body-bg);--bs-popover-border-width:var(--bs-border-width);--bs-popover-border-color:var(--bs-border-color-translucent);--bs-popover-border-radius:var(--bs-border-radius-lg);--bs-popover-inner-border-radius:calc(var(--bs-border-radius-lg) - var(--bs-border-width));--bs-popover-box-shadow:var(--bs-box-shadow);--bs-popover-header-padding-x:1rem;--bs-popover-header-padding-y:0.5rem;--bs-popover-header-font-size:1rem;--bs-popover-header-color:inherit;--bs-popover-header-bg:var(--bs-secondary-bg);--bs-popover-body-padding-x:1rem;--bs-popover-body-padding-y:1rem;--bs-popover-body-color:var(--bs-body-color);--bs-popover-arrow-width:1rem;--bs-popover-arrow-height:0.5rem;--bs-popover-arrow-border:var(--bs-popover-border-color);z-index:var(--bs-popover-zindex);display:block;max-width:var(--bs-popover-max-width);font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:var(--bs-popover-font-size);word-wrap:break-word;background-color:var(--bs-popover-bg);background-clip:padding-box;border:var(--bs-popover-border-width) solid var(--bs-popover-border-color);border-radius:var(--bs-popover-border-radius)}.popover .popover-arrow{display:block;width:var(--bs-popover-arrow-width);height:var(--bs-popover-arrow-height)}.popover .popover-arrow::after,.popover .popover-arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid;border-width:0}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow,.bs-popover-top>.popover-arrow{bottom:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width))}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before,.bs-popover-top>.popover-arrow::after,.bs-popover-top>.popover-arrow::before{border-width:var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * .5) 0}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before,.bs-popover-top>.popover-arrow::before{bottom:0;border-top-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after,.bs-popover-top>.popover-arrow::after{bottom:var(--bs-popover-border-width);border-top-color:var(--bs-popover-bg)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow,.bs-popover-end>.popover-arrow{left:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));width:var(--bs-popover-arrow-height);height:var(--bs-popover-arrow-width)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before,.bs-popover-end>.popover-arrow::after,.bs-popover-end>.popover-arrow::before{border-width:calc(var(--bs-popover-arrow-width) * .5) var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * .5) 0}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before,.bs-popover-end>.popover-arrow::before{left:0;border-right-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after,.bs-popover-end>.popover-arrow::after{left:var(--bs-popover-border-width);border-right-color:var(--bs-popover-bg)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow,.bs-popover-bottom>.popover-arrow{top:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width))}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before,.bs-popover-bottom>.popover-arrow::after,.bs-popover-bottom>.popover-arrow::before{border-width:0 calc(var(--bs-popover-arrow-width) * .5) var(--bs-popover-arrow-height)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before,.bs-popover-bottom>.popover-arrow::before{top:0;border-bottom-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after,.bs-popover-bottom>.popover-arrow::after{top:var(--bs-popover-border-width);border-bottom-color:var(--bs-popover-bg)}.bs-popover-auto[data-popper-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:var(--bs-popover-arrow-width);margin-left:calc(-.5 * var(--bs-popover-arrow-width));content:"";border-bottom:var(--bs-popover-border-width) solid var(--bs-popover-header-bg)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow,.bs-popover-start>.popover-arrow{right:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));width:var(--bs-popover-arrow-height);height:var(--bs-popover-arrow-width)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before,.bs-popover-start>.popover-arrow::after,.bs-popover-start>.popover-arrow::before{border-width:calc(var(--bs-popover-arrow-width) * .5) 0 calc(var(--bs-popover-arrow-width) * .5) var(--bs-popover-arrow-height)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before,.bs-popover-start>.popover-arrow::before{right:0;border-left-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after,.bs-popover-start>.popover-arrow::after{right:var(--bs-popover-border-width);border-left-color:var(--bs-popover-bg)}.popover-header{padding:var(--bs-popover-header-padding-y) var(--bs-popover-header-padding-x);margin-bottom:0;font-size:var(--bs-popover-header-font-size);color:var(--bs-popover-header-color);background-color:var(--bs-popover-header-bg);border-bottom:var(--bs-popover-border-width) solid var(--bs-popover-border-color);border-top-left-radius:var(--bs-popover-inner-border-radius);border-top-right-radius:var(--bs-popover-inner-border-radius)}.popover-header:empty{display:none}.popover-body{padding:var(--bs-popover-body-padding-y) var(--bs-popover-body-padding-x);color:var(--bs-popover-body-color)}.carousel{position:relative}.carousel.pointer-event{touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-end,.carousel-item-next:not(.carousel-item-start){transform:translateX(100%)}.active.carousel-item-start,.carousel-item-prev:not(.carousel-item-end){transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;transform:none}.carousel-fade .carousel-item-next.carousel-item-start,.carousel-fade .carousel-item-prev.carousel-item-end,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{z-index:0;opacity:0;transition:opacity 0s .6s}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:flex;align-items:center;justify-content:center;width:15%;padding:0;color:#fff;text-align:center;background:0 0;border:0;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:2rem;height:2rem;background-repeat:no-repeat;background-position:50%;background-size:100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:2;display:flex;justify-content:center;padding:0;margin-right:15%;margin-bottom:1rem;margin-left:15%}.carousel-indicators [data-bs-target]{box-sizing:content-box;flex:0 1 auto;width:30px;height:3px;padding:0;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border:0;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators [data-bs-target]{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:1.25rem;left:15%;padding-top:1.25rem;padding-bottom:1.25rem;color:#fff;text-align:center}.carousel-dark .carousel-control-next-icon,.carousel-dark .carousel-control-prev-icon{filter:invert(1) grayscale(100)}.carousel-dark .carousel-indicators [data-bs-target]{background-color:#000}.carousel-dark .carousel-caption{color:#000}[data-bs-theme=dark] .carousel .carousel-control-next-icon,[data-bs-theme=dark] .carousel .carousel-control-prev-icon,[data-bs-theme=dark].carousel .carousel-control-next-icon,[data-bs-theme=dark].carousel .carousel-control-prev-icon{filter:invert(1) grayscale(100)}[data-bs-theme=dark] .carousel .carousel-indicators [data-bs-target],[data-bs-theme=dark].carousel .carousel-indicators [data-bs-target]{background-color:#000}[data-bs-theme=dark] .carousel .carousel-caption,[data-bs-theme=dark].carousel .carousel-caption{color:#000}.spinner-border,.spinner-grow{display:inline-block;width:var(--bs-spinner-width);height:var(--bs-spinner-height);vertical-align:var(--bs-spinner-vertical-align);border-radius:50%;animation:var(--bs-spinner-animation-speed) linear infinite var(--bs-spinner-animation-name)}@keyframes spinner-border{to{transform:rotate(360deg)}}.spinner-border{--bs-spinner-width:2rem;--bs-spinner-height:2rem;--bs-spinner-vertical-align:-0.125em;--bs-spinner-border-width:0.25em;--bs-spinner-animation-speed:0.75s;--bs-spinner-animation-name:spinner-border;border:var(--bs-spinner-border-width) solid currentcolor;border-right-color:transparent}.spinner-border-sm{--bs-spinner-width:1rem;--bs-spinner-height:1rem;--bs-spinner-border-width:0.2em}@keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}.spinner-grow{--bs-spinner-width:2rem;--bs-spinner-height:2rem;--bs-spinner-vertical-align:-0.125em;--bs-spinner-animation-speed:0.75s;--bs-spinner-animation-name:spinner-grow;background-color:currentcolor;opacity:0}.spinner-grow-sm{--bs-spinner-width:1rem;--bs-spinner-height:1rem}@media (prefers-reduced-motion:reduce){.spinner-border,.spinner-grow{--bs-spinner-animation-speed:1.5s}}.offcanvas,.offcanvas-lg,.offcanvas-md,.offcanvas-sm,.offcanvas-xl,.offcanvas-xxl{--bs-offcanvas-zindex:1045;--bs-offcanvas-width:400px;--bs-offcanvas-height:30vh;--bs-offcanvas-padding-x:1rem;--bs-offcanvas-padding-y:1rem;--bs-offcanvas-color:var(--bs-body-color);--bs-offcanvas-bg:var(--bs-body-bg);--bs-offcanvas-border-width:var(--bs-border-width);--bs-offcanvas-border-color:var(--bs-border-color-translucent);--bs-offcanvas-box-shadow:var(--bs-box-shadow-sm);--bs-offcanvas-transition:transform 0.3s ease-in-out;--bs-offcanvas-title-line-height:1.5}@media (max-width:575.98px){.offcanvas-sm{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:575.98px) and (prefers-reduced-motion:reduce){.offcanvas-sm{transition:none}}@media (max-width:575.98px){.offcanvas-sm.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-sm.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-sm.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-sm.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-sm.show:not(.hiding),.offcanvas-sm.showing{transform:none}.offcanvas-sm.hiding,.offcanvas-sm.show,.offcanvas-sm.showing{visibility:visible}}@media (min-width:576px){.offcanvas-sm{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-sm .offcanvas-header{display:none}.offcanvas-sm .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media (max-width:767.98px){.offcanvas-md{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:767.98px) and (prefers-reduced-motion:reduce){.offcanvas-md{transition:none}}@media (max-width:767.98px){.offcanvas-md.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-md.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-md.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-md.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-md.show:not(.hiding),.offcanvas-md.showing{transform:none}.offcanvas-md.hiding,.offcanvas-md.show,.offcanvas-md.showing{visibility:visible}}@media (min-width:768px){.offcanvas-md{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-md .offcanvas-header{display:none}.offcanvas-md .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media (max-width:991.98px){.offcanvas-lg{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:991.98px) and (prefers-reduced-motion:reduce){.offcanvas-lg{transition:none}}@media (max-width:991.98px){.offcanvas-lg.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-lg.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-lg.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-lg.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-lg.show:not(.hiding),.offcanvas-lg.showing{transform:none}.offcanvas-lg.hiding,.offcanvas-lg.show,.offcanvas-lg.showing{visibility:visible}}@media (min-width:992px){.offcanvas-lg{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-lg .offcanvas-header{display:none}.offcanvas-lg .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media (max-width:1199.98px){.offcanvas-xl{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:1199.98px) and (prefers-reduced-motion:reduce){.offcanvas-xl{transition:none}}@media (max-width:1199.98px){.offcanvas-xl.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-xl.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-xl.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-xl.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-xl.show:not(.hiding),.offcanvas-xl.showing{transform:none}.offcanvas-xl.hiding,.offcanvas-xl.show,.offcanvas-xl.showing{visibility:visible}}@media (min-width:1200px){.offcanvas-xl{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-xl .offcanvas-header{display:none}.offcanvas-xl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media (max-width:1399.98px){.offcanvas-xxl{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media (max-width:1399.98px) and (prefers-reduced-motion:reduce){.offcanvas-xxl{transition:none}}@media (max-width:1399.98px){.offcanvas-xxl.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-xxl.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-xxl.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-xxl.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-xxl.show:not(.hiding),.offcanvas-xxl.showing{transform:none}.offcanvas-xxl.hiding,.offcanvas-xxl.show,.offcanvas-xxl.showing{visibility:visible}}@media (min-width:1400px){.offcanvas-xxl{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-xxl .offcanvas-header{display:none}.offcanvas-xxl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}.offcanvas{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}@media (prefers-reduced-motion:reduce){.offcanvas{transition:none}}.offcanvas.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas.show:not(.hiding),.offcanvas.showing{transform:none}.offcanvas.hiding,.offcanvas.show,.offcanvas.showing{visibility:visible}.offcanvas-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.offcanvas-backdrop.fade{opacity:0}.offcanvas-backdrop.show{opacity:.5}.offcanvas-header{display:flex;align-items:center;justify-content:space-between;padding:var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x)}.offcanvas-header .btn-close{padding:calc(var(--bs-offcanvas-padding-y) * .5) calc(var(--bs-offcanvas-padding-x) * .5);margin-top:calc(-.5 * var(--bs-offcanvas-padding-y));margin-right:calc(-.5 * var(--bs-offcanvas-padding-x));margin-bottom:calc(-.5 * var(--bs-offcanvas-padding-y))}.offcanvas-title{margin-bottom:0;line-height:var(--bs-offcanvas-title-line-height)}.offcanvas-body{flex-grow:1;padding:var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x);overflow-y:auto}.placeholder{display:inline-block;min-height:1em;vertical-align:middle;cursor:wait;background-color:currentcolor;opacity:.5}.placeholder.btn::before{display:inline-block;content:""}.placeholder-xs{min-height:.6em}.placeholder-sm{min-height:.8em}.placeholder-lg{min-height:1.2em}.placeholder-glow .placeholder{animation:placeholder-glow 2s ease-in-out infinite}@keyframes placeholder-glow{50%{opacity:.2}}.placeholder-wave{-webkit-mask-image:linear-gradient(130deg,#000 55%,rgba(0,0,0,0.8) 75%,#000 95%);mask-image:linear-gradient(130deg,#000 55%,rgba(0,0,0,0.8) 75%,#000 95%);-webkit-mask-size:200% 100%;mask-size:200% 100%;animation:placeholder-wave 2s linear infinite}@keyframes placeholder-wave{100%{-webkit-mask-position:-200% 0%;mask-position:-200% 0%}}.clearfix::after{display:block;clear:both;content:""}.text-bg-primary{color:#fff!important;background-color:RGBA(var(--bs-primary-rgb),var(--bs-bg-opacity,1))!important}.text-bg-secondary{color:#fff!important;background-color:RGBA(var(--bs-secondary-rgb),var(--bs-bg-opacity,1))!important}.text-bg-success{color:#fff!important;background-color:RGBA(var(--bs-success-rgb),var(--bs-bg-opacity,1))!important}.text-bg-info{color:#000!important;background-color:RGBA(var(--bs-info-rgb),var(--bs-bg-opacity,1))!important}.text-bg-warning{color:#000!important;background-color:RGBA(var(--bs-warning-rgb),var(--bs-bg-opacity,1))!important}.text-bg-danger{color:#fff!important;background-color:RGBA(var(--bs-danger-rgb),var(--bs-bg-opacity,1))!important}.text-bg-light{color:#000!important;background-color:RGBA(var(--bs-light-rgb),var(--bs-bg-opacity,1))!important}.text-bg-dark{color:#fff!important;background-color:RGBA(var(--bs-dark-rgb),var(--bs-bg-opacity,1))!important}.link-primary{color:RGBA(var(--bs-primary-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-primary-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-primary-rgb),var(--bs-link-underline-opacity,1))!important}.link-primary:focus,.link-primary:hover{color:RGBA(10,88,202,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(10,88,202,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(10,88,202,var(--bs-link-underline-opacity,1))!important}.link-secondary{color:RGBA(var(--bs-secondary-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-secondary-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-secondary-rgb),var(--bs-link-underline-opacity,1))!important}.link-secondary:focus,.link-secondary:hover{color:RGBA(86,94,100,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(86,94,100,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(86,94,100,var(--bs-link-underline-opacity,1))!important}.link-success{color:RGBA(var(--bs-success-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-success-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-success-rgb),var(--bs-link-underline-opacity,1))!important}.link-success:focus,.link-success:hover{color:RGBA(20,108,67,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(20,108,67,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(20,108,67,var(--bs-link-underline-opacity,1))!important}.link-info{color:RGBA(var(--bs-info-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-info-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-info-rgb),var(--bs-link-underline-opacity,1))!important}.link-info:focus,.link-info:hover{color:RGBA(61,213,243,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(61,213,243,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(61,213,243,var(--bs-link-underline-opacity,1))!important}.link-warning{color:RGBA(var(--bs-warning-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-warning-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-warning-rgb),var(--bs-link-underline-opacity,1))!important}.link-warning:focus,.link-warning:hover{color:RGBA(255,205,57,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(255,205,57,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(255,205,57,var(--bs-link-underline-opacity,1))!important}.link-danger{color:RGBA(var(--bs-danger-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-danger-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-danger-rgb),var(--bs-link-underline-opacity,1))!important}.link-danger:focus,.link-danger:hover{color:RGBA(176,42,55,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(176,42,55,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(176,42,55,var(--bs-link-underline-opacity,1))!important}.link-light{color:RGBA(var(--bs-light-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-light-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-light-rgb),var(--bs-link-underline-opacity,1))!important}.link-light:focus,.link-light:hover{color:RGBA(249,250,251,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(249,250,251,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(249,250,251,var(--bs-link-underline-opacity,1))!important}.link-dark{color:RGBA(var(--bs-dark-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-dark-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-dark-rgb),var(--bs-link-underline-opacity,1))!important}.link-dark:focus,.link-dark:hover{color:RGBA(26,30,33,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(26,30,33,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(26,30,33,var(--bs-link-underline-opacity,1))!important}.link-body-emphasis{color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,1))!important}.link-body-emphasis:focus,.link-body-emphasis:hover{color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-opacity,.75))!important;-webkit-text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,0.75))!important;text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,0.75))!important}.focus-ring:focus{outline:0;box-shadow:var(--bs-focus-ring-x,0) var(--bs-focus-ring-y,0) var(--bs-focus-ring-blur,0) var(--bs-focus-ring-width) var(--bs-focus-ring-color)}.icon-link{display:inline-flex;gap:.375rem;align-items:center;-webkit-text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-opacity,0.5));text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-opacity,0.5));text-underline-offset:0.25em;-webkit-backface-visibility:hidden;backface-visibility:hidden}.icon-link>.bi{flex-shrink:0;width:1em;height:1em;fill:currentcolor;transition:.2s ease-in-out transform}@media (prefers-reduced-motion:reduce){.icon-link>.bi{transition:none}}.icon-link-hover:focus-visible>.bi,.icon-link-hover:hover>.bi{transform:var(--bs-icon-link-transform,translate3d(.25em,0,0))}.ratio{position:relative;width:100%}.ratio::before{display:block;padding-top:var(--bs-aspect-ratio);content:""}.ratio>*{position:absolute;top:0;left:0;width:100%;height:100%}.ratio-1x1{--bs-aspect-ratio:100%}.ratio-4x3{--bs-aspect-ratio:75%}.ratio-16x9{--bs-aspect-ratio:56.25%}.ratio-21x9{--bs-aspect-ratio:42.8571428571%}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}@media (min-width:576px){.sticky-sm-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-sm-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media (min-width:768px){.sticky-md-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-md-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media (min-width:992px){.sticky-lg-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-lg-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media (min-width:1200px){.sticky-xl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-xl-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media (min-width:1400px){.sticky-xxl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-xxl-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}.hstack{display:flex;flex-direction:row;align-items:center;align-self:stretch}.vstack{display:flex;flex:1 1 auto;flex-direction:column;align-self:stretch}.visually-hidden,.visually-hidden-focusable:not(:focus):not(:focus-within){width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}.visually-hidden-focusable:not(:focus):not(:focus-within):not(caption),.visually-hidden:not(caption){position:absolute!important}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;content:""}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.vr{display:inline-block;align-self:stretch;width:var(--bs-border-width);min-height:1em;background-color:currentcolor;opacity:.25}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.float-start{float:left!important}.float-end{float:right!important}.float-none{float:none!important}.object-fit-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-none{-o-object-fit:none!important;object-fit:none!important}.opacity-0{opacity:0!important}.opacity-25{opacity:.25!important}.opacity-50{opacity:.5!important}.opacity-75{opacity:.75!important}.opacity-100{opacity:1!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.overflow-visible{overflow:visible!important}.overflow-scroll{overflow:scroll!important}.overflow-x-auto{overflow-x:auto!important}.overflow-x-hidden{overflow-x:hidden!important}.overflow-x-visible{overflow-x:visible!important}.overflow-x-scroll{overflow-x:scroll!important}.overflow-y-auto{overflow-y:auto!important}.overflow-y-hidden{overflow-y:hidden!important}.overflow-y-visible{overflow-y:visible!important}.overflow-y-scroll{overflow-y:scroll!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-grid{display:grid!important}.d-inline-grid{display:inline-grid!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:flex!important}.d-inline-flex{display:inline-flex!important}.d-none{display:none!important}.shadow{box-shadow:var(--bs-box-shadow)!important}.shadow-sm{box-shadow:var(--bs-box-shadow-sm)!important}.shadow-lg{box-shadow:var(--bs-box-shadow-lg)!important}.shadow-none{box-shadow:none!important}.focus-ring-primary{--bs-focus-ring-color:rgba(var(--bs-primary-rgb), var(--bs-focus-ring-opacity))}.focus-ring-secondary{--bs-focus-ring-color:rgba(var(--bs-secondary-rgb), var(--bs-focus-ring-opacity))}.focus-ring-success{--bs-focus-ring-color:rgba(var(--bs-success-rgb), var(--bs-focus-ring-opacity))}.focus-ring-info{--bs-focus-ring-color:rgba(var(--bs-info-rgb), var(--bs-focus-ring-opacity))}.focus-ring-warning{--bs-focus-ring-color:rgba(var(--bs-warning-rgb), var(--bs-focus-ring-opacity))}.focus-ring-danger{--bs-focus-ring-color:rgba(var(--bs-danger-rgb), var(--bs-focus-ring-opacity))}.focus-ring-light{--bs-focus-ring-color:rgba(var(--bs-light-rgb), var(--bs-focus-ring-opacity))}.focus-ring-dark{--bs-focus-ring-color:rgba(var(--bs-dark-rgb), var(--bs-focus-ring-opacity))}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.top-0{top:0!important}.top-50{top:50%!important}.top-100{top:100%!important}.bottom-0{bottom:0!important}.bottom-50{bottom:50%!important}.bottom-100{bottom:100%!important}.start-0{left:0!important}.start-50{left:50%!important}.start-100{left:100%!important}.end-0{right:0!important}.end-50{right:50%!important}.end-100{right:100%!important}.translate-middle{transform:translate(-50%,-50%)!important}.translate-middle-x{transform:translateX(-50%)!important}.translate-middle-y{transform:translateY(-50%)!important}.border{border:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-0{border:0!important}.border-top{border-top:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-top-0{border-top:0!important}.border-end{border-right:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-end-0{border-right:0!important}.border-bottom{border-bottom:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-bottom-0{border-bottom:0!important}.border-start{border-left:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-start-0{border-left:0!important}.border-primary{--bs-border-opacity:1;border-color:rgba(var(--bs-primary-rgb),var(--bs-border-opacity))!important}.border-secondary{--bs-border-opacity:1;border-color:rgba(var(--bs-secondary-rgb),var(--bs-border-opacity))!important}.border-success{--bs-border-opacity:1;border-color:rgba(var(--bs-success-rgb),var(--bs-border-opacity))!important}.border-info{--bs-border-opacity:1;border-color:rgba(var(--bs-info-rgb),var(--bs-border-opacity))!important}.border-warning{--bs-border-opacity:1;border-color:rgba(var(--bs-warning-rgb),var(--bs-border-opacity))!important}.border-danger{--bs-border-opacity:1;border-color:rgba(var(--bs-danger-rgb),var(--bs-border-opacity))!important}.border-light{--bs-border-opacity:1;border-color:rgba(var(--bs-light-rgb),var(--bs-border-opacity))!important}.border-dark{--bs-border-opacity:1;border-color:rgba(var(--bs-dark-rgb),var(--bs-border-opacity))!important}.border-black{--bs-border-opacity:1;border-color:rgba(var(--bs-black-rgb),var(--bs-border-opacity))!important}.border-white{--bs-border-opacity:1;border-color:rgba(var(--bs-white-rgb),var(--bs-border-opacity))!important}.border-primary-subtle{border-color:var(--bs-primary-border-subtle)!important}.border-secondary-subtle{border-color:var(--bs-secondary-border-subtle)!important}.border-success-subtle{border-color:var(--bs-success-border-subtle)!important}.border-info-subtle{border-color:var(--bs-info-border-subtle)!important}.border-warning-subtle{border-color:var(--bs-warning-border-subtle)!important}.border-danger-subtle{border-color:var(--bs-danger-border-subtle)!important}.border-light-subtle{border-color:var(--bs-light-border-subtle)!important}.border-dark-subtle{border-color:var(--bs-dark-border-subtle)!important}.border-1{border-width:1px!important}.border-2{border-width:2px!important}.border-3{border-width:3px!important}.border-4{border-width:4px!important}.border-5{border-width:5px!important}.border-opacity-10{--bs-border-opacity:0.1}.border-opacity-25{--bs-border-opacity:0.25}.border-opacity-50{--bs-border-opacity:0.5}.border-opacity-75{--bs-border-opacity:0.75}.border-opacity-100{--bs-border-opacity:1}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.mw-100{max-width:100%!important}.vw-100{width:100vw!important}.min-vw-100{min-width:100vw!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mh-100{max-height:100%!important}.vh-100{height:100vh!important}.min-vh-100{min-height:100vh!important}.flex-fill{flex:1 1 auto!important}.flex-row{flex-direction:row!important}.flex-column{flex-direction:column!important}.flex-row-reverse{flex-direction:row-reverse!important}.flex-column-reverse{flex-direction:column-reverse!important}.flex-grow-0{flex-grow:0!important}.flex-grow-1{flex-grow:1!important}.flex-shrink-0{flex-shrink:0!important}.flex-shrink-1{flex-shrink:1!important}.flex-wrap{flex-wrap:wrap!important}.flex-nowrap{flex-wrap:nowrap!important}.flex-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-start{justify-content:flex-start!important}.justify-content-end{justify-content:flex-end!important}.justify-content-center{justify-content:center!important}.justify-content-between{justify-content:space-between!important}.justify-content-around{justify-content:space-around!important}.justify-content-evenly{justify-content:space-evenly!important}.align-items-start{align-items:flex-start!important}.align-items-end{align-items:flex-end!important}.align-items-center{align-items:center!important}.align-items-baseline{align-items:baseline!important}.align-items-stretch{align-items:stretch!important}.align-content-start{align-content:flex-start!important}.align-content-end{align-content:flex-end!important}.align-content-center{align-content:center!important}.align-content-between{align-content:space-between!important}.align-content-around{align-content:space-around!important}.align-content-stretch{align-content:stretch!important}.align-self-auto{align-self:auto!important}.align-self-start{align-self:flex-start!important}.align-self-end{align-self:flex-end!important}.align-self-center{align-self:center!important}.align-self-baseline{align-self:baseline!important}.align-self-stretch{align-self:stretch!important}.order-first{order:-1!important}.order-0{order:0!important}.order-1{order:1!important}.order-2{order:2!important}.order-3{order:3!important}.order-4{order:4!important}.order-5{order:5!important}.order-last{order:6!important}.m-0{margin:0!important}.m-1{margin:.25rem!important}.m-2{margin:.5rem!important}.m-3{margin:1rem!important}.m-4{margin:1.5rem!important}.m-5{margin:3rem!important}.m-auto{margin:auto!important}.mx-0{margin-right:0!important;margin-left:0!important}.mx-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-3{margin-right:1rem!important;margin-left:1rem!important}.mx-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-5{margin-right:3rem!important;margin-left:3rem!important}.mx-auto{margin-right:auto!important;margin-left:auto!important}.my-0{margin-top:0!important;margin-bottom:0!important}.my-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-0{margin-top:0!important}.mt-1{margin-top:.25rem!important}.mt-2{margin-top:.5rem!important}.mt-3{margin-top:1rem!important}.mt-4{margin-top:1.5rem!important}.mt-5{margin-top:3rem!important}.mt-auto{margin-top:auto!important}.me-0{margin-right:0!important}.me-1{margin-right:.25rem!important}.me-2{margin-right:.5rem!important}.me-3{margin-right:1rem!important}.me-4{margin-right:1.5rem!important}.me-5{margin-right:3rem!important}.me-auto{margin-right:auto!important}.mb-0{margin-bottom:0!important}.mb-1{margin-bottom:.25rem!important}.mb-2{margin-bottom:.5rem!important}.mb-3{margin-bottom:1rem!important}.mb-4{margin-bottom:1.5rem!important}.mb-5{margin-bottom:3rem!important}.mb-auto{margin-bottom:auto!important}.ms-0{margin-left:0!important}.ms-1{margin-left:.25rem!important}.ms-2{margin-left:.5rem!important}.ms-3{margin-left:1rem!important}.ms-4{margin-left:1.5rem!important}.ms-5{margin-left:3rem!important}.ms-auto{margin-left:auto!important}.p-0{padding:0!important}.p-1{padding:.25rem!important}.p-2{padding:.5rem!important}.p-3{padding:1rem!important}.p-4{padding:1.5rem!important}.p-5{padding:3rem!important}.px-0{padding-right:0!important;padding-left:0!important}.px-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-3{padding-right:1rem!important;padding-left:1rem!important}.px-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-5{padding-right:3rem!important;padding-left:3rem!important}.py-0{padding-top:0!important;padding-bottom:0!important}.py-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-0{padding-top:0!important}.pt-1{padding-top:.25rem!important}.pt-2{padding-top:.5rem!important}.pt-3{padding-top:1rem!important}.pt-4{padding-top:1.5rem!important}.pt-5{padding-top:3rem!important}.pe-0{padding-right:0!important}.pe-1{padding-right:.25rem!important}.pe-2{padding-right:.5rem!important}.pe-3{padding-right:1rem!important}.pe-4{padding-right:1.5rem!important}.pe-5{padding-right:3rem!important}.pb-0{padding-bottom:0!important}.pb-1{padding-bottom:.25rem!important}.pb-2{padding-bottom:.5rem!important}.pb-3{padding-bottom:1rem!important}.pb-4{padding-bottom:1.5rem!important}.pb-5{padding-bottom:3rem!important}.ps-0{padding-left:0!important}.ps-1{padding-left:.25rem!important}.ps-2{padding-left:.5rem!important}.ps-3{padding-left:1rem!important}.ps-4{padding-left:1.5rem!important}.ps-5{padding-left:3rem!important}.gap-0{gap:0!important}.gap-1{gap:.25rem!important}.gap-2{gap:.5rem!important}.gap-3{gap:1rem!important}.gap-4{gap:1.5rem!important}.gap-5{gap:3rem!important}.row-gap-0{row-gap:0!important}.row-gap-1{row-gap:.25rem!important}.row-gap-2{row-gap:.5rem!important}.row-gap-3{row-gap:1rem!important}.row-gap-4{row-gap:1.5rem!important}.row-gap-5{row-gap:3rem!important}.column-gap-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.font-monospace{font-family:var(--bs-font-monospace)!important}.fs-1{font-size:calc(1.375rem + 1.5vw)!important}.fs-2{font-size:calc(1.325rem + .9vw)!important}.fs-3{font-size:calc(1.3rem + .6vw)!important}.fs-4{font-size:calc(1.275rem + .3vw)!important}.fs-5{font-size:1.25rem!important}.fs-6{font-size:1rem!important}.fst-italic{font-style:italic!important}.fst-normal{font-style:normal!important}.fw-lighter{font-weight:lighter!important}.fw-light{font-weight:300!important}.fw-normal{font-weight:400!important}.fw-medium{font-weight:500!important}.fw-semibold{font-weight:600!important}.fw-bold{font-weight:700!important}.fw-bolder{font-weight:bolder!important}.lh-1{line-height:1!important}.lh-sm{line-height:1.25!important}.lh-base{line-height:1.5!important}.lh-lg{line-height:2!important}.text-start{text-align:left!important}.text-end{text-align:right!important}.text-center{text-align:center!important}.text-decoration-none{text-decoration:none!important}.text-decoration-underline{text-decoration:underline!important}.text-decoration-line-through{text-decoration:line-through!important}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-break{word-wrap:break-word!important;word-break:break-word!important}.text-primary{--bs-text-opacity:1;color:rgba(var(--bs-primary-rgb),var(--bs-text-opacity))!important}.text-secondary{--bs-text-opacity:1;color:rgba(var(--bs-secondary-rgb),var(--bs-text-opacity))!important}.text-success{--bs-text-opacity:1;color:rgba(var(--bs-success-rgb),var(--bs-text-opacity))!important}.text-info{--bs-text-opacity:1;color:rgba(var(--bs-info-rgb),var(--bs-text-opacity))!important}.text-warning{--bs-text-opacity:1;color:rgba(var(--bs-warning-rgb),var(--bs-text-opacity))!important}.text-danger{--bs-text-opacity:1;color:rgba(var(--bs-danger-rgb),var(--bs-text-opacity))!important}.text-light{--bs-text-opacity:1;color:rgba(var(--bs-light-rgb),var(--bs-text-opacity))!important}.text-dark{--bs-text-opacity:1;color:rgba(var(--bs-dark-rgb),var(--bs-text-opacity))!important}.text-black{--bs-text-opacity:1;color:rgba(var(--bs-black-rgb),var(--bs-text-opacity))!important}.text-white{--bs-text-opacity:1;color:rgba(var(--bs-white-rgb),var(--bs-text-opacity))!important}.text-body{--bs-text-opacity:1;color:rgba(var(--bs-body-color-rgb),var(--bs-text-opacity))!important}.text-muted{--bs-text-opacity:1;color:var(--bs-secondary-color)!important}.text-black-50{--bs-text-opacity:1;color:rgba(0,0,0,.5)!important}.text-white-50{--bs-text-opacity:1;color:rgba(255,255,255,.5)!important}.text-body-secondary{--bs-text-opacity:1;color:var(--bs-secondary-color)!important}.text-body-tertiary{--bs-text-opacity:1;color:var(--bs-tertiary-color)!important}.text-body-emphasis{--bs-text-opacity:1;color:var(--bs-emphasis-color)!important}.text-reset{--bs-text-opacity:1;color:inherit!important}.text-opacity-25{--bs-text-opacity:0.25}.text-opacity-50{--bs-text-opacity:0.5}.text-opacity-75{--bs-text-opacity:0.75}.text-opacity-100{--bs-text-opacity:1}.text-primary-emphasis{color:var(--bs-primary-text-emphasis)!important}.text-secondary-emphasis{color:var(--bs-secondary-text-emphasis)!important}.text-success-emphasis{color:var(--bs-success-text-emphasis)!important}.text-info-emphasis{color:var(--bs-info-text-emphasis)!important}.text-warning-emphasis{color:var(--bs-warning-text-emphasis)!important}.text-danger-emphasis{color:var(--bs-danger-text-emphasis)!important}.text-light-emphasis{color:var(--bs-light-text-emphasis)!important}.text-dark-emphasis{color:var(--bs-dark-text-emphasis)!important}.link-opacity-10{--bs-link-opacity:0.1}.link-opacity-10-hover:hover{--bs-link-opacity:0.1}.link-opacity-25{--bs-link-opacity:0.25}.link-opacity-25-hover:hover{--bs-link-opacity:0.25}.link-opacity-50{--bs-link-opacity:0.5}.link-opacity-50-hover:hover{--bs-link-opacity:0.5}.link-opacity-75{--bs-link-opacity:0.75}.link-opacity-75-hover:hover{--bs-link-opacity:0.75}.link-opacity-100{--bs-link-opacity:1}.link-opacity-100-hover:hover{--bs-link-opacity:1}.link-offset-1{text-underline-offset:0.125em!important}.link-offset-1-hover:hover{text-underline-offset:0.125em!important}.link-offset-2{text-underline-offset:0.25em!important}.link-offset-2-hover:hover{text-underline-offset:0.25em!important}.link-offset-3{text-underline-offset:0.375em!important}.link-offset-3-hover:hover{text-underline-offset:0.375em!important}.link-underline-primary{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-primary-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-primary-rgb),var(--bs-link-underline-opacity))!important}.link-underline-secondary{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-secondary-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-secondary-rgb),var(--bs-link-underline-opacity))!important}.link-underline-success{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-success-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-success-rgb),var(--bs-link-underline-opacity))!important}.link-underline-info{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-info-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-info-rgb),var(--bs-link-underline-opacity))!important}.link-underline-warning{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-warning-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-warning-rgb),var(--bs-link-underline-opacity))!important}.link-underline-danger{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-danger-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-danger-rgb),var(--bs-link-underline-opacity))!important}.link-underline-light{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-light-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-light-rgb),var(--bs-link-underline-opacity))!important}.link-underline-dark{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-dark-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-dark-rgb),var(--bs-link-underline-opacity))!important}.link-underline{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-underline-opacity,1))!important}.link-underline-opacity-0{--bs-link-underline-opacity:0}.link-underline-opacity-0-hover:hover{--bs-link-underline-opacity:0}.link-underline-opacity-10{--bs-link-underline-opacity:0.1}.link-underline-opacity-10-hover:hover{--bs-link-underline-opacity:0.1}.link-underline-opacity-25{--bs-link-underline-opacity:0.25}.link-underline-opacity-25-hover:hover{--bs-link-underline-opacity:0.25}.link-underline-opacity-50{--bs-link-underline-opacity:0.5}.link-underline-opacity-50-hover:hover{--bs-link-underline-opacity:0.5}.link-underline-opacity-75{--bs-link-underline-opacity:0.75}.link-underline-opacity-75-hover:hover{--bs-link-underline-opacity:0.75}.link-underline-opacity-100{--bs-link-underline-opacity:1}.link-underline-opacity-100-hover:hover{--bs-link-underline-opacity:1}.bg-primary{--bs-bg-opacity:1;background-color:rgba(var(--bs-primary-rgb),var(--bs-bg-opacity))!important}.bg-secondary{--bs-bg-opacity:1;background-color:rgba(var(--bs-secondary-rgb),var(--bs-bg-opacity))!important}.bg-success{--bs-bg-opacity:1;background-color:rgba(var(--bs-success-rgb),var(--bs-bg-opacity))!important}.bg-info{--bs-bg-opacity:1;background-color:rgba(var(--bs-info-rgb),var(--bs-bg-opacity))!important}.bg-warning{--bs-bg-opacity:1;background-color:rgba(var(--bs-warning-rgb),var(--bs-bg-opacity))!important}.bg-danger{--bs-bg-opacity:1;background-color:rgba(var(--bs-danger-rgb),var(--bs-bg-opacity))!important}.bg-light{--bs-bg-opacity:1;background-color:rgba(var(--bs-light-rgb),var(--bs-bg-opacity))!important}.bg-dark{--bs-bg-opacity:1;background-color:rgba(var(--bs-dark-rgb),var(--bs-bg-opacity))!important}.bg-black{--bs-bg-opacity:1;background-color:rgba(var(--bs-black-rgb),var(--bs-bg-opacity))!important}.bg-white{--bs-bg-opacity:1;background-color:rgba(var(--bs-white-rgb),var(--bs-bg-opacity))!important}.bg-body{--bs-bg-opacity:1;background-color:rgba(var(--bs-body-bg-rgb),var(--bs-bg-opacity))!important}.bg-transparent{--bs-bg-opacity:1;background-color:transparent!important}.bg-body-secondary{--bs-bg-opacity:1;background-color:rgba(var(--bs-secondary-bg-rgb),var(--bs-bg-opacity))!important}.bg-body-tertiary{--bs-bg-opacity:1;background-color:rgba(var(--bs-tertiary-bg-rgb),var(--bs-bg-opacity))!important}.bg-opacity-10{--bs-bg-opacity:0.1}.bg-opacity-25{--bs-bg-opacity:0.25}.bg-opacity-50{--bs-bg-opacity:0.5}.bg-opacity-75{--bs-bg-opacity:0.75}.bg-opacity-100{--bs-bg-opacity:1}.bg-primary-subtle{background-color:var(--bs-primary-bg-subtle)!important}.bg-secondary-subtle{background-color:var(--bs-secondary-bg-subtle)!important}.bg-success-subtle{background-color:var(--bs-success-bg-subtle)!important}.bg-info-subtle{background-color:var(--bs-info-bg-subtle)!important}.bg-warning-subtle{background-color:var(--bs-warning-bg-subtle)!important}.bg-danger-subtle{background-color:var(--bs-danger-bg-subtle)!important}.bg-light-subtle{background-color:var(--bs-light-bg-subtle)!important}.bg-dark-subtle{background-color:var(--bs-dark-bg-subtle)!important}.bg-gradient{background-image:var(--bs-gradient)!important}.user-select-all{-webkit-user-select:all!important;-moz-user-select:all!important;user-select:all!important}.user-select-auto{-webkit-user-select:auto!important;-moz-user-select:auto!important;user-select:auto!important}.user-select-none{-webkit-user-select:none!important;-moz-user-select:none!important;user-select:none!important}.pe-none{pointer-events:none!important}.pe-auto{pointer-events:auto!important}.rounded{border-radius:var(--bs-border-radius)!important}.rounded-0{border-radius:0!important}.rounded-1{border-radius:var(--bs-border-radius-sm)!important}.rounded-2{border-radius:var(--bs-border-radius)!important}.rounded-3{border-radius:var(--bs-border-radius-lg)!important}.rounded-4{border-radius:var(--bs-border-radius-xl)!important}.rounded-5{border-radius:var(--bs-border-radius-xxl)!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:var(--bs-border-radius-pill)!important}.rounded-top{border-top-left-radius:var(--bs-border-radius)!important;border-top-right-radius:var(--bs-border-radius)!important}.rounded-top-0{border-top-left-radius:0!important;border-top-right-radius:0!important}.rounded-top-1{border-top-left-radius:var(--bs-border-radius-sm)!important;border-top-right-radius:var(--bs-border-radius-sm)!important}.rounded-top-2{border-top-left-radius:var(--bs-border-radius)!important;border-top-right-radius:var(--bs-border-radius)!important}.rounded-top-3{border-top-left-radius:var(--bs-border-radius-lg)!important;border-top-right-radius:var(--bs-border-radius-lg)!important}.rounded-top-4{border-top-left-radius:var(--bs-border-radius-xl)!important;border-top-right-radius:var(--bs-border-radius-xl)!important}.rounded-top-5{border-top-left-radius:var(--bs-border-radius-xxl)!important;border-top-right-radius:var(--bs-border-radius-xxl)!important}.rounded-top-circle{border-top-left-radius:50%!important;border-top-right-radius:50%!important}.rounded-top-pill{border-top-left-radius:var(--bs-border-radius-pill)!important;border-top-right-radius:var(--bs-border-radius-pill)!important}.rounded-end{border-top-right-radius:var(--bs-border-radius)!important;border-bottom-right-radius:var(--bs-border-radius)!important}.rounded-end-0{border-top-right-radius:0!important;border-bottom-right-radius:0!important}.rounded-end-1{border-top-right-radius:var(--bs-border-radius-sm)!important;border-bottom-right-radius:var(--bs-border-radius-sm)!important}.rounded-end-2{border-top-right-radius:var(--bs-border-radius)!important;border-bottom-right-radius:var(--bs-border-radius)!important}.rounded-end-3{border-top-right-radius:var(--bs-border-radius-lg)!important;border-bottom-right-radius:var(--bs-border-radius-lg)!important}.rounded-end-4{border-top-right-radius:var(--bs-border-radius-xl)!important;border-bottom-right-radius:var(--bs-border-radius-xl)!important}.rounded-end-5{border-top-right-radius:var(--bs-border-radius-xxl)!important;border-bottom-right-radius:var(--bs-border-radius-xxl)!important}.rounded-end-circle{border-top-right-radius:50%!important;border-bottom-right-radius:50%!important}.rounded-end-pill{border-top-right-radius:var(--bs-border-radius-pill)!important;border-bottom-right-radius:var(--bs-border-radius-pill)!important}.rounded-bottom{border-bottom-right-radius:var(--bs-border-radius)!important;border-bottom-left-radius:var(--bs-border-radius)!important}.rounded-bottom-0{border-bottom-right-radius:0!important;border-bottom-left-radius:0!important}.rounded-bottom-1{border-bottom-right-radius:var(--bs-border-radius-sm)!important;border-bottom-left-radius:var(--bs-border-radius-sm)!important}.rounded-bottom-2{border-bottom-right-radius:var(--bs-border-radius)!important;border-bottom-left-radius:var(--bs-border-radius)!important}.rounded-bottom-3{border-bottom-right-radius:var(--bs-border-radius-lg)!important;border-bottom-left-radius:var(--bs-border-radius-lg)!important}.rounded-bottom-4{border-bottom-right-radius:var(--bs-border-radius-xl)!important;border-bottom-left-radius:var(--bs-border-radius-xl)!important}.rounded-bottom-5{border-bottom-right-radius:var(--bs-border-radius-xxl)!important;border-bottom-left-radius:var(--bs-border-radius-xxl)!important}.rounded-bottom-circle{border-bottom-right-radius:50%!important;border-bottom-left-radius:50%!important}.rounded-bottom-pill{border-bottom-right-radius:var(--bs-border-radius-pill)!important;border-bottom-left-radius:var(--bs-border-radius-pill)!important}.rounded-start{border-bottom-left-radius:var(--bs-border-radius)!important;border-top-left-radius:var(--bs-border-radius)!important}.rounded-start-0{border-bottom-left-radius:0!important;border-top-left-radius:0!important}.rounded-start-1{border-bottom-left-radius:var(--bs-border-radius-sm)!important;border-top-left-radius:var(--bs-border-radius-sm)!important}.rounded-start-2{border-bottom-left-radius:var(--bs-border-radius)!important;border-top-left-radius:var(--bs-border-radius)!important}.rounded-start-3{border-bottom-left-radius:var(--bs-border-radius-lg)!important;border-top-left-radius:var(--bs-border-radius-lg)!important}.rounded-start-4{border-bottom-left-radius:var(--bs-border-radius-xl)!important;border-top-left-radius:var(--bs-border-radius-xl)!important}.rounded-start-5{border-bottom-left-radius:var(--bs-border-radius-xxl)!important;border-top-left-radius:var(--bs-border-radius-xxl)!important}.rounded-start-circle{border-bottom-left-radius:50%!important;border-top-left-radius:50%!important}.rounded-start-pill{border-bottom-left-radius:var(--bs-border-radius-pill)!important;border-top-left-radius:var(--bs-border-radius-pill)!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}.z-n1{z-index:-1!important}.z-0{z-index:0!important}.z-1{z-index:1!important}.z-2{z-index:2!important}.z-3{z-index:3!important}@media (min-width:576px){.float-sm-start{float:left!important}.float-sm-end{float:right!important}.float-sm-none{float:none!important}.object-fit-sm-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-sm-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-sm-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-sm-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-sm-none{-o-object-fit:none!important;object-fit:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-grid{display:grid!important}.d-sm-inline-grid{display:inline-grid!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:flex!important}.d-sm-inline-flex{display:inline-flex!important}.d-sm-none{display:none!important}.flex-sm-fill{flex:1 1 auto!important}.flex-sm-row{flex-direction:row!important}.flex-sm-column{flex-direction:column!important}.flex-sm-row-reverse{flex-direction:row-reverse!important}.flex-sm-column-reverse{flex-direction:column-reverse!important}.flex-sm-grow-0{flex-grow:0!important}.flex-sm-grow-1{flex-grow:1!important}.flex-sm-shrink-0{flex-shrink:0!important}.flex-sm-shrink-1{flex-shrink:1!important}.flex-sm-wrap{flex-wrap:wrap!important}.flex-sm-nowrap{flex-wrap:nowrap!important}.flex-sm-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-sm-start{justify-content:flex-start!important}.justify-content-sm-end{justify-content:flex-end!important}.justify-content-sm-center{justify-content:center!important}.justify-content-sm-between{justify-content:space-between!important}.justify-content-sm-around{justify-content:space-around!important}.justify-content-sm-evenly{justify-content:space-evenly!important}.align-items-sm-start{align-items:flex-start!important}.align-items-sm-end{align-items:flex-end!important}.align-items-sm-center{align-items:center!important}.align-items-sm-baseline{align-items:baseline!important}.align-items-sm-stretch{align-items:stretch!important}.align-content-sm-start{align-content:flex-start!important}.align-content-sm-end{align-content:flex-end!important}.align-content-sm-center{align-content:center!important}.align-content-sm-between{align-content:space-between!important}.align-content-sm-around{align-content:space-around!important}.align-content-sm-stretch{align-content:stretch!important}.align-self-sm-auto{align-self:auto!important}.align-self-sm-start{align-self:flex-start!important}.align-self-sm-end{align-self:flex-end!important}.align-self-sm-center{align-self:center!important}.align-self-sm-baseline{align-self:baseline!important}.align-self-sm-stretch{align-self:stretch!important}.order-sm-first{order:-1!important}.order-sm-0{order:0!important}.order-sm-1{order:1!important}.order-sm-2{order:2!important}.order-sm-3{order:3!important}.order-sm-4{order:4!important}.order-sm-5{order:5!important}.order-sm-last{order:6!important}.m-sm-0{margin:0!important}.m-sm-1{margin:.25rem!important}.m-sm-2{margin:.5rem!important}.m-sm-3{margin:1rem!important}.m-sm-4{margin:1.5rem!important}.m-sm-5{margin:3rem!important}.m-sm-auto{margin:auto!important}.mx-sm-0{margin-right:0!important;margin-left:0!important}.mx-sm-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-sm-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-sm-3{margin-right:1rem!important;margin-left:1rem!important}.mx-sm-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-sm-5{margin-right:3rem!important;margin-left:3rem!important}.mx-sm-auto{margin-right:auto!important;margin-left:auto!important}.my-sm-0{margin-top:0!important;margin-bottom:0!important}.my-sm-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-sm-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-sm-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-sm-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-sm-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-sm-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-sm-0{margin-top:0!important}.mt-sm-1{margin-top:.25rem!important}.mt-sm-2{margin-top:.5rem!important}.mt-sm-3{margin-top:1rem!important}.mt-sm-4{margin-top:1.5rem!important}.mt-sm-5{margin-top:3rem!important}.mt-sm-auto{margin-top:auto!important}.me-sm-0{margin-right:0!important}.me-sm-1{margin-right:.25rem!important}.me-sm-2{margin-right:.5rem!important}.me-sm-3{margin-right:1rem!important}.me-sm-4{margin-right:1.5rem!important}.me-sm-5{margin-right:3rem!important}.me-sm-auto{margin-right:auto!important}.mb-sm-0{margin-bottom:0!important}.mb-sm-1{margin-bottom:.25rem!important}.mb-sm-2{margin-bottom:.5rem!important}.mb-sm-3{margin-bottom:1rem!important}.mb-sm-4{margin-bottom:1.5rem!important}.mb-sm-5{margin-bottom:3rem!important}.mb-sm-auto{margin-bottom:auto!important}.ms-sm-0{margin-left:0!important}.ms-sm-1{margin-left:.25rem!important}.ms-sm-2{margin-left:.5rem!important}.ms-sm-3{margin-left:1rem!important}.ms-sm-4{margin-left:1.5rem!important}.ms-sm-5{margin-left:3rem!important}.ms-sm-auto{margin-left:auto!important}.p-sm-0{padding:0!important}.p-sm-1{padding:.25rem!important}.p-sm-2{padding:.5rem!important}.p-sm-3{padding:1rem!important}.p-sm-4{padding:1.5rem!important}.p-sm-5{padding:3rem!important}.px-sm-0{padding-right:0!important;padding-left:0!important}.px-sm-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-sm-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-sm-3{padding-right:1rem!important;padding-left:1rem!important}.px-sm-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-sm-5{padding-right:3rem!important;padding-left:3rem!important}.py-sm-0{padding-top:0!important;padding-bottom:0!important}.py-sm-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-sm-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-sm-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-sm-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-sm-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-sm-0{padding-top:0!important}.pt-sm-1{padding-top:.25rem!important}.pt-sm-2{padding-top:.5rem!important}.pt-sm-3{padding-top:1rem!important}.pt-sm-4{padding-top:1.5rem!important}.pt-sm-5{padding-top:3rem!important}.pe-sm-0{padding-right:0!important}.pe-sm-1{padding-right:.25rem!important}.pe-sm-2{padding-right:.5rem!important}.pe-sm-3{padding-right:1rem!important}.pe-sm-4{padding-right:1.5rem!important}.pe-sm-5{padding-right:3rem!important}.pb-sm-0{padding-bottom:0!important}.pb-sm-1{padding-bottom:.25rem!important}.pb-sm-2{padding-bottom:.5rem!important}.pb-sm-3{padding-bottom:1rem!important}.pb-sm-4{padding-bottom:1.5rem!important}.pb-sm-5{padding-bottom:3rem!important}.ps-sm-0{padding-left:0!important}.ps-sm-1{padding-left:.25rem!important}.ps-sm-2{padding-left:.5rem!important}.ps-sm-3{padding-left:1rem!important}.ps-sm-4{padding-left:1.5rem!important}.ps-sm-5{padding-left:3rem!important}.gap-sm-0{gap:0!important}.gap-sm-1{gap:.25rem!important}.gap-sm-2{gap:.5rem!important}.gap-sm-3{gap:1rem!important}.gap-sm-4{gap:1.5rem!important}.gap-sm-5{gap:3rem!important}.row-gap-sm-0{row-gap:0!important}.row-gap-sm-1{row-gap:.25rem!important}.row-gap-sm-2{row-gap:.5rem!important}.row-gap-sm-3{row-gap:1rem!important}.row-gap-sm-4{row-gap:1.5rem!important}.row-gap-sm-5{row-gap:3rem!important}.column-gap-sm-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-sm-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-sm-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-sm-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-sm-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-sm-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-sm-start{text-align:left!important}.text-sm-end{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.float-md-start{float:left!important}.float-md-end{float:right!important}.float-md-none{float:none!important}.object-fit-md-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-md-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-md-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-md-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-md-none{-o-object-fit:none!important;object-fit:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-grid{display:grid!important}.d-md-inline-grid{display:inline-grid!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:flex!important}.d-md-inline-flex{display:inline-flex!important}.d-md-none{display:none!important}.flex-md-fill{flex:1 1 auto!important}.flex-md-row{flex-direction:row!important}.flex-md-column{flex-direction:column!important}.flex-md-row-reverse{flex-direction:row-reverse!important}.flex-md-column-reverse{flex-direction:column-reverse!important}.flex-md-grow-0{flex-grow:0!important}.flex-md-grow-1{flex-grow:1!important}.flex-md-shrink-0{flex-shrink:0!important}.flex-md-shrink-1{flex-shrink:1!important}.flex-md-wrap{flex-wrap:wrap!important}.flex-md-nowrap{flex-wrap:nowrap!important}.flex-md-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-md-start{justify-content:flex-start!important}.justify-content-md-end{justify-content:flex-end!important}.justify-content-md-center{justify-content:center!important}.justify-content-md-between{justify-content:space-between!important}.justify-content-md-around{justify-content:space-around!important}.justify-content-md-evenly{justify-content:space-evenly!important}.align-items-md-start{align-items:flex-start!important}.align-items-md-end{align-items:flex-end!important}.align-items-md-center{align-items:center!important}.align-items-md-baseline{align-items:baseline!important}.align-items-md-stretch{align-items:stretch!important}.align-content-md-start{align-content:flex-start!important}.align-content-md-end{align-content:flex-end!important}.align-content-md-center{align-content:center!important}.align-content-md-between{align-content:space-between!important}.align-content-md-around{align-content:space-around!important}.align-content-md-stretch{align-content:stretch!important}.align-self-md-auto{align-self:auto!important}.align-self-md-start{align-self:flex-start!important}.align-self-md-end{align-self:flex-end!important}.align-self-md-center{align-self:center!important}.align-self-md-baseline{align-self:baseline!important}.align-self-md-stretch{align-self:stretch!important}.order-md-first{order:-1!important}.order-md-0{order:0!important}.order-md-1{order:1!important}.order-md-2{order:2!important}.order-md-3{order:3!important}.order-md-4{order:4!important}.order-md-5{order:5!important}.order-md-last{order:6!important}.m-md-0{margin:0!important}.m-md-1{margin:.25rem!important}.m-md-2{margin:.5rem!important}.m-md-3{margin:1rem!important}.m-md-4{margin:1.5rem!important}.m-md-5{margin:3rem!important}.m-md-auto{margin:auto!important}.mx-md-0{margin-right:0!important;margin-left:0!important}.mx-md-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-md-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-md-3{margin-right:1rem!important;margin-left:1rem!important}.mx-md-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-md-5{margin-right:3rem!important;margin-left:3rem!important}.mx-md-auto{margin-right:auto!important;margin-left:auto!important}.my-md-0{margin-top:0!important;margin-bottom:0!important}.my-md-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-md-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-md-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-md-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-md-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-md-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-md-0{margin-top:0!important}.mt-md-1{margin-top:.25rem!important}.mt-md-2{margin-top:.5rem!important}.mt-md-3{margin-top:1rem!important}.mt-md-4{margin-top:1.5rem!important}.mt-md-5{margin-top:3rem!important}.mt-md-auto{margin-top:auto!important}.me-md-0{margin-right:0!important}.me-md-1{margin-right:.25rem!important}.me-md-2{margin-right:.5rem!important}.me-md-3{margin-right:1rem!important}.me-md-4{margin-right:1.5rem!important}.me-md-5{margin-right:3rem!important}.me-md-auto{margin-right:auto!important}.mb-md-0{margin-bottom:0!important}.mb-md-1{margin-bottom:.25rem!important}.mb-md-2{margin-bottom:.5rem!important}.mb-md-3{margin-bottom:1rem!important}.mb-md-4{margin-bottom:1.5rem!important}.mb-md-5{margin-bottom:3rem!important}.mb-md-auto{margin-bottom:auto!important}.ms-md-0{margin-left:0!important}.ms-md-1{margin-left:.25rem!important}.ms-md-2{margin-left:.5rem!important}.ms-md-3{margin-left:1rem!important}.ms-md-4{margin-left:1.5rem!important}.ms-md-5{margin-left:3rem!important}.ms-md-auto{margin-left:auto!important}.p-md-0{padding:0!important}.p-md-1{padding:.25rem!important}.p-md-2{padding:.5rem!important}.p-md-3{padding:1rem!important}.p-md-4{padding:1.5rem!important}.p-md-5{padding:3rem!important}.px-md-0{padding-right:0!important;padding-left:0!important}.px-md-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-md-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-md-3{padding-right:1rem!important;padding-left:1rem!important}.px-md-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-md-5{padding-right:3rem!important;padding-left:3rem!important}.py-md-0{padding-top:0!important;padding-bottom:0!important}.py-md-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-md-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-md-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-md-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-md-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-md-0{padding-top:0!important}.pt-md-1{padding-top:.25rem!important}.pt-md-2{padding-top:.5rem!important}.pt-md-3{padding-top:1rem!important}.pt-md-4{padding-top:1.5rem!important}.pt-md-5{padding-top:3rem!important}.pe-md-0{padding-right:0!important}.pe-md-1{padding-right:.25rem!important}.pe-md-2{padding-right:.5rem!important}.pe-md-3{padding-right:1rem!important}.pe-md-4{padding-right:1.5rem!important}.pe-md-5{padding-right:3rem!important}.pb-md-0{padding-bottom:0!important}.pb-md-1{padding-bottom:.25rem!important}.pb-md-2{padding-bottom:.5rem!important}.pb-md-3{padding-bottom:1rem!important}.pb-md-4{padding-bottom:1.5rem!important}.pb-md-5{padding-bottom:3rem!important}.ps-md-0{padding-left:0!important}.ps-md-1{padding-left:.25rem!important}.ps-md-2{padding-left:.5rem!important}.ps-md-3{padding-left:1rem!important}.ps-md-4{padding-left:1.5rem!important}.ps-md-5{padding-left:3rem!important}.gap-md-0{gap:0!important}.gap-md-1{gap:.25rem!important}.gap-md-2{gap:.5rem!important}.gap-md-3{gap:1rem!important}.gap-md-4{gap:1.5rem!important}.gap-md-5{gap:3rem!important}.row-gap-md-0{row-gap:0!important}.row-gap-md-1{row-gap:.25rem!important}.row-gap-md-2{row-gap:.5rem!important}.row-gap-md-3{row-gap:1rem!important}.row-gap-md-4{row-gap:1.5rem!important}.row-gap-md-5{row-gap:3rem!important}.column-gap-md-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-md-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-md-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-md-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-md-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-md-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-md-start{text-align:left!important}.text-md-end{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.float-lg-start{float:left!important}.float-lg-end{float:right!important}.float-lg-none{float:none!important}.object-fit-lg-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-lg-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-lg-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-lg-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-lg-none{-o-object-fit:none!important;object-fit:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-grid{display:grid!important}.d-lg-inline-grid{display:inline-grid!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:flex!important}.d-lg-inline-flex{display:inline-flex!important}.d-lg-none{display:none!important}.flex-lg-fill{flex:1 1 auto!important}.flex-lg-row{flex-direction:row!important}.flex-lg-column{flex-direction:column!important}.flex-lg-row-reverse{flex-direction:row-reverse!important}.flex-lg-column-reverse{flex-direction:column-reverse!important}.flex-lg-grow-0{flex-grow:0!important}.flex-lg-grow-1{flex-grow:1!important}.flex-lg-shrink-0{flex-shrink:0!important}.flex-lg-shrink-1{flex-shrink:1!important}.flex-lg-wrap{flex-wrap:wrap!important}.flex-lg-nowrap{flex-wrap:nowrap!important}.flex-lg-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-lg-start{justify-content:flex-start!important}.justify-content-lg-end{justify-content:flex-end!important}.justify-content-lg-center{justify-content:center!important}.justify-content-lg-between{justify-content:space-between!important}.justify-content-lg-around{justify-content:space-around!important}.justify-content-lg-evenly{justify-content:space-evenly!important}.align-items-lg-start{align-items:flex-start!important}.align-items-lg-end{align-items:flex-end!important}.align-items-lg-center{align-items:center!important}.align-items-lg-baseline{align-items:baseline!important}.align-items-lg-stretch{align-items:stretch!important}.align-content-lg-start{align-content:flex-start!important}.align-content-lg-end{align-content:flex-end!important}.align-content-lg-center{align-content:center!important}.align-content-lg-between{align-content:space-between!important}.align-content-lg-around{align-content:space-around!important}.align-content-lg-stretch{align-content:stretch!important}.align-self-lg-auto{align-self:auto!important}.align-self-lg-start{align-self:flex-start!important}.align-self-lg-end{align-self:flex-end!important}.align-self-lg-center{align-self:center!important}.align-self-lg-baseline{align-self:baseline!important}.align-self-lg-stretch{align-self:stretch!important}.order-lg-first{order:-1!important}.order-lg-0{order:0!important}.order-lg-1{order:1!important}.order-lg-2{order:2!important}.order-lg-3{order:3!important}.order-lg-4{order:4!important}.order-lg-5{order:5!important}.order-lg-last{order:6!important}.m-lg-0{margin:0!important}.m-lg-1{margin:.25rem!important}.m-lg-2{margin:.5rem!important}.m-lg-3{margin:1rem!important}.m-lg-4{margin:1.5rem!important}.m-lg-5{margin:3rem!important}.m-lg-auto{margin:auto!important}.mx-lg-0{margin-right:0!important;margin-left:0!important}.mx-lg-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-lg-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-lg-3{margin-right:1rem!important;margin-left:1rem!important}.mx-lg-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-lg-5{margin-right:3rem!important;margin-left:3rem!important}.mx-lg-auto{margin-right:auto!important;margin-left:auto!important}.my-lg-0{margin-top:0!important;margin-bottom:0!important}.my-lg-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-lg-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-lg-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-lg-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-lg-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-lg-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-lg-0{margin-top:0!important}.mt-lg-1{margin-top:.25rem!important}.mt-lg-2{margin-top:.5rem!important}.mt-lg-3{margin-top:1rem!important}.mt-lg-4{margin-top:1.5rem!important}.mt-lg-5{margin-top:3rem!important}.mt-lg-auto{margin-top:auto!important}.me-lg-0{margin-right:0!important}.me-lg-1{margin-right:.25rem!important}.me-lg-2{margin-right:.5rem!important}.me-lg-3{margin-right:1rem!important}.me-lg-4{margin-right:1.5rem!important}.me-lg-5{margin-right:3rem!important}.me-lg-auto{margin-right:auto!important}.mb-lg-0{margin-bottom:0!important}.mb-lg-1{margin-bottom:.25rem!important}.mb-lg-2{margin-bottom:.5rem!important}.mb-lg-3{margin-bottom:1rem!important}.mb-lg-4{margin-bottom:1.5rem!important}.mb-lg-5{margin-bottom:3rem!important}.mb-lg-auto{margin-bottom:auto!important}.ms-lg-0{margin-left:0!important}.ms-lg-1{margin-left:.25rem!important}.ms-lg-2{margin-left:.5rem!important}.ms-lg-3{margin-left:1rem!important}.ms-lg-4{margin-left:1.5rem!important}.ms-lg-5{margin-left:3rem!important}.ms-lg-auto{margin-left:auto!important}.p-lg-0{padding:0!important}.p-lg-1{padding:.25rem!important}.p-lg-2{padding:.5rem!important}.p-lg-3{padding:1rem!important}.p-lg-4{padding:1.5rem!important}.p-lg-5{padding:3rem!important}.px-lg-0{padding-right:0!important;padding-left:0!important}.px-lg-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-lg-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-lg-3{padding-right:1rem!important;padding-left:1rem!important}.px-lg-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-lg-5{padding-right:3rem!important;padding-left:3rem!important}.py-lg-0{padding-top:0!important;padding-bottom:0!important}.py-lg-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-lg-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-lg-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-lg-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-lg-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-lg-0{padding-top:0!important}.pt-lg-1{padding-top:.25rem!important}.pt-lg-2{padding-top:.5rem!important}.pt-lg-3{padding-top:1rem!important}.pt-lg-4{padding-top:1.5rem!important}.pt-lg-5{padding-top:3rem!important}.pe-lg-0{padding-right:0!important}.pe-lg-1{padding-right:.25rem!important}.pe-lg-2{padding-right:.5rem!important}.pe-lg-3{padding-right:1rem!important}.pe-lg-4{padding-right:1.5rem!important}.pe-lg-5{padding-right:3rem!important}.pb-lg-0{padding-bottom:0!important}.pb-lg-1{padding-bottom:.25rem!important}.pb-lg-2{padding-bottom:.5rem!important}.pb-lg-3{padding-bottom:1rem!important}.pb-lg-4{padding-bottom:1.5rem!important}.pb-lg-5{padding-bottom:3rem!important}.ps-lg-0{padding-left:0!important}.ps-lg-1{padding-left:.25rem!important}.ps-lg-2{padding-left:.5rem!important}.ps-lg-3{padding-left:1rem!important}.ps-lg-4{padding-left:1.5rem!important}.ps-lg-5{padding-left:3rem!important}.gap-lg-0{gap:0!important}.gap-lg-1{gap:.25rem!important}.gap-lg-2{gap:.5rem!important}.gap-lg-3{gap:1rem!important}.gap-lg-4{gap:1.5rem!important}.gap-lg-5{gap:3rem!important}.row-gap-lg-0{row-gap:0!important}.row-gap-lg-1{row-gap:.25rem!important}.row-gap-lg-2{row-gap:.5rem!important}.row-gap-lg-3{row-gap:1rem!important}.row-gap-lg-4{row-gap:1.5rem!important}.row-gap-lg-5{row-gap:3rem!important}.column-gap-lg-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-lg-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-lg-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-lg-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-lg-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-lg-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-lg-start{text-align:left!important}.text-lg-end{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.float-xl-start{float:left!important}.float-xl-end{float:right!important}.float-xl-none{float:none!important}.object-fit-xl-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-xl-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-xl-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-xl-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-xl-none{-o-object-fit:none!important;object-fit:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-grid{display:grid!important}.d-xl-inline-grid{display:inline-grid!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:flex!important}.d-xl-inline-flex{display:inline-flex!important}.d-xl-none{display:none!important}.flex-xl-fill{flex:1 1 auto!important}.flex-xl-row{flex-direction:row!important}.flex-xl-column{flex-direction:column!important}.flex-xl-row-reverse{flex-direction:row-reverse!important}.flex-xl-column-reverse{flex-direction:column-reverse!important}.flex-xl-grow-0{flex-grow:0!important}.flex-xl-grow-1{flex-grow:1!important}.flex-xl-shrink-0{flex-shrink:0!important}.flex-xl-shrink-1{flex-shrink:1!important}.flex-xl-wrap{flex-wrap:wrap!important}.flex-xl-nowrap{flex-wrap:nowrap!important}.flex-xl-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-xl-start{justify-content:flex-start!important}.justify-content-xl-end{justify-content:flex-end!important}.justify-content-xl-center{justify-content:center!important}.justify-content-xl-between{justify-content:space-between!important}.justify-content-xl-around{justify-content:space-around!important}.justify-content-xl-evenly{justify-content:space-evenly!important}.align-items-xl-start{align-items:flex-start!important}.align-items-xl-end{align-items:flex-end!important}.align-items-xl-center{align-items:center!important}.align-items-xl-baseline{align-items:baseline!important}.align-items-xl-stretch{align-items:stretch!important}.align-content-xl-start{align-content:flex-start!important}.align-content-xl-end{align-content:flex-end!important}.align-content-xl-center{align-content:center!important}.align-content-xl-between{align-content:space-between!important}.align-content-xl-around{align-content:space-around!important}.align-content-xl-stretch{align-content:stretch!important}.align-self-xl-auto{align-self:auto!important}.align-self-xl-start{align-self:flex-start!important}.align-self-xl-end{align-self:flex-end!important}.align-self-xl-center{align-self:center!important}.align-self-xl-baseline{align-self:baseline!important}.align-self-xl-stretch{align-self:stretch!important}.order-xl-first{order:-1!important}.order-xl-0{order:0!important}.order-xl-1{order:1!important}.order-xl-2{order:2!important}.order-xl-3{order:3!important}.order-xl-4{order:4!important}.order-xl-5{order:5!important}.order-xl-last{order:6!important}.m-xl-0{margin:0!important}.m-xl-1{margin:.25rem!important}.m-xl-2{margin:.5rem!important}.m-xl-3{margin:1rem!important}.m-xl-4{margin:1.5rem!important}.m-xl-5{margin:3rem!important}.m-xl-auto{margin:auto!important}.mx-xl-0{margin-right:0!important;margin-left:0!important}.mx-xl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xl-auto{margin-right:auto!important;margin-left:auto!important}.my-xl-0{margin-top:0!important;margin-bottom:0!important}.my-xl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xl-0{margin-top:0!important}.mt-xl-1{margin-top:.25rem!important}.mt-xl-2{margin-top:.5rem!important}.mt-xl-3{margin-top:1rem!important}.mt-xl-4{margin-top:1.5rem!important}.mt-xl-5{margin-top:3rem!important}.mt-xl-auto{margin-top:auto!important}.me-xl-0{margin-right:0!important}.me-xl-1{margin-right:.25rem!important}.me-xl-2{margin-right:.5rem!important}.me-xl-3{margin-right:1rem!important}.me-xl-4{margin-right:1.5rem!important}.me-xl-5{margin-right:3rem!important}.me-xl-auto{margin-right:auto!important}.mb-xl-0{margin-bottom:0!important}.mb-xl-1{margin-bottom:.25rem!important}.mb-xl-2{margin-bottom:.5rem!important}.mb-xl-3{margin-bottom:1rem!important}.mb-xl-4{margin-bottom:1.5rem!important}.mb-xl-5{margin-bottom:3rem!important}.mb-xl-auto{margin-bottom:auto!important}.ms-xl-0{margin-left:0!important}.ms-xl-1{margin-left:.25rem!important}.ms-xl-2{margin-left:.5rem!important}.ms-xl-3{margin-left:1rem!important}.ms-xl-4{margin-left:1.5rem!important}.ms-xl-5{margin-left:3rem!important}.ms-xl-auto{margin-left:auto!important}.p-xl-0{padding:0!important}.p-xl-1{padding:.25rem!important}.p-xl-2{padding:.5rem!important}.p-xl-3{padding:1rem!important}.p-xl-4{padding:1.5rem!important}.p-xl-5{padding:3rem!important}.px-xl-0{padding-right:0!important;padding-left:0!important}.px-xl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xl-0{padding-top:0!important;padding-bottom:0!important}.py-xl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xl-0{padding-top:0!important}.pt-xl-1{padding-top:.25rem!important}.pt-xl-2{padding-top:.5rem!important}.pt-xl-3{padding-top:1rem!important}.pt-xl-4{padding-top:1.5rem!important}.pt-xl-5{padding-top:3rem!important}.pe-xl-0{padding-right:0!important}.pe-xl-1{padding-right:.25rem!important}.pe-xl-2{padding-right:.5rem!important}.pe-xl-3{padding-right:1rem!important}.pe-xl-4{padding-right:1.5rem!important}.pe-xl-5{padding-right:3rem!important}.pb-xl-0{padding-bottom:0!important}.pb-xl-1{padding-bottom:.25rem!important}.pb-xl-2{padding-bottom:.5rem!important}.pb-xl-3{padding-bottom:1rem!important}.pb-xl-4{padding-bottom:1.5rem!important}.pb-xl-5{padding-bottom:3rem!important}.ps-xl-0{padding-left:0!important}.ps-xl-1{padding-left:.25rem!important}.ps-xl-2{padding-left:.5rem!important}.ps-xl-3{padding-left:1rem!important}.ps-xl-4{padding-left:1.5rem!important}.ps-xl-5{padding-left:3rem!important}.gap-xl-0{gap:0!important}.gap-xl-1{gap:.25rem!important}.gap-xl-2{gap:.5rem!important}.gap-xl-3{gap:1rem!important}.gap-xl-4{gap:1.5rem!important}.gap-xl-5{gap:3rem!important}.row-gap-xl-0{row-gap:0!important}.row-gap-xl-1{row-gap:.25rem!important}.row-gap-xl-2{row-gap:.5rem!important}.row-gap-xl-3{row-gap:1rem!important}.row-gap-xl-4{row-gap:1.5rem!important}.row-gap-xl-5{row-gap:3rem!important}.column-gap-xl-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-xl-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-xl-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-xl-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-xl-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-xl-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-xl-start{text-align:left!important}.text-xl-end{text-align:right!important}.text-xl-center{text-align:center!important}}@media (min-width:1400px){.float-xxl-start{float:left!important}.float-xxl-end{float:right!important}.float-xxl-none{float:none!important}.object-fit-xxl-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-xxl-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-xxl-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-xxl-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-xxl-none{-o-object-fit:none!important;object-fit:none!important}.d-xxl-inline{display:inline!important}.d-xxl-inline-block{display:inline-block!important}.d-xxl-block{display:block!important}.d-xxl-grid{display:grid!important}.d-xxl-inline-grid{display:inline-grid!important}.d-xxl-table{display:table!important}.d-xxl-table-row{display:table-row!important}.d-xxl-table-cell{display:table-cell!important}.d-xxl-flex{display:flex!important}.d-xxl-inline-flex{display:inline-flex!important}.d-xxl-none{display:none!important}.flex-xxl-fill{flex:1 1 auto!important}.flex-xxl-row{flex-direction:row!important}.flex-xxl-column{flex-direction:column!important}.flex-xxl-row-reverse{flex-direction:row-reverse!important}.flex-xxl-column-reverse{flex-direction:column-reverse!important}.flex-xxl-grow-0{flex-grow:0!important}.flex-xxl-grow-1{flex-grow:1!important}.flex-xxl-shrink-0{flex-shrink:0!important}.flex-xxl-shrink-1{flex-shrink:1!important}.flex-xxl-wrap{flex-wrap:wrap!important}.flex-xxl-nowrap{flex-wrap:nowrap!important}.flex-xxl-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-xxl-start{justify-content:flex-start!important}.justify-content-xxl-end{justify-content:flex-end!important}.justify-content-xxl-center{justify-content:center!important}.justify-content-xxl-between{justify-content:space-between!important}.justify-content-xxl-around{justify-content:space-around!important}.justify-content-xxl-evenly{justify-content:space-evenly!important}.align-items-xxl-start{align-items:flex-start!important}.align-items-xxl-end{align-items:flex-end!important}.align-items-xxl-center{align-items:center!important}.align-items-xxl-baseline{align-items:baseline!important}.align-items-xxl-stretch{align-items:stretch!important}.align-content-xxl-start{align-content:flex-start!important}.align-content-xxl-end{align-content:flex-end!important}.align-content-xxl-center{align-content:center!important}.align-content-xxl-between{align-content:space-between!important}.align-content-xxl-around{align-content:space-around!important}.align-content-xxl-stretch{align-content:stretch!important}.align-self-xxl-auto{align-self:auto!important}.align-self-xxl-start{align-self:flex-start!important}.align-self-xxl-end{align-self:flex-end!important}.align-self-xxl-center{align-self:center!important}.align-self-xxl-baseline{align-self:baseline!important}.align-self-xxl-stretch{align-self:stretch!important}.order-xxl-first{order:-1!important}.order-xxl-0{order:0!important}.order-xxl-1{order:1!important}.order-xxl-2{order:2!important}.order-xxl-3{order:3!important}.order-xxl-4{order:4!important}.order-xxl-5{order:5!important}.order-xxl-last{order:6!important}.m-xxl-0{margin:0!important}.m-xxl-1{margin:.25rem!important}.m-xxl-2{margin:.5rem!important}.m-xxl-3{margin:1rem!important}.m-xxl-4{margin:1.5rem!important}.m-xxl-5{margin:3rem!important}.m-xxl-auto{margin:auto!important}.mx-xxl-0{margin-right:0!important;margin-left:0!important}.mx-xxl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xxl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xxl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xxl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xxl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xxl-auto{margin-right:auto!important;margin-left:auto!important}.my-xxl-0{margin-top:0!important;margin-bottom:0!important}.my-xxl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xxl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xxl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xxl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xxl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xxl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xxl-0{margin-top:0!important}.mt-xxl-1{margin-top:.25rem!important}.mt-xxl-2{margin-top:.5rem!important}.mt-xxl-3{margin-top:1rem!important}.mt-xxl-4{margin-top:1.5rem!important}.mt-xxl-5{margin-top:3rem!important}.mt-xxl-auto{margin-top:auto!important}.me-xxl-0{margin-right:0!important}.me-xxl-1{margin-right:.25rem!important}.me-xxl-2{margin-right:.5rem!important}.me-xxl-3{margin-right:1rem!important}.me-xxl-4{margin-right:1.5rem!important}.me-xxl-5{margin-right:3rem!important}.me-xxl-auto{margin-right:auto!important}.mb-xxl-0{margin-bottom:0!important}.mb-xxl-1{margin-bottom:.25rem!important}.mb-xxl-2{margin-bottom:.5rem!important}.mb-xxl-3{margin-bottom:1rem!important}.mb-xxl-4{margin-bottom:1.5rem!important}.mb-xxl-5{margin-bottom:3rem!important}.mb-xxl-auto{margin-bottom:auto!important}.ms-xxl-0{margin-left:0!important}.ms-xxl-1{margin-left:.25rem!important}.ms-xxl-2{margin-left:.5rem!important}.ms-xxl-3{margin-left:1rem!important}.ms-xxl-4{margin-left:1.5rem!important}.ms-xxl-5{margin-left:3rem!important}.ms-xxl-auto{margin-left:auto!important}.p-xxl-0{padding:0!important}.p-xxl-1{padding:.25rem!important}.p-xxl-2{padding:.5rem!important}.p-xxl-3{padding:1rem!important}.p-xxl-4{padding:1.5rem!important}.p-xxl-5{padding:3rem!important}.px-xxl-0{padding-right:0!important;padding-left:0!important}.px-xxl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xxl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xxl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xxl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xxl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xxl-0{padding-top:0!important;padding-bottom:0!important}.py-xxl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xxl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xxl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xxl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xxl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xxl-0{padding-top:0!important}.pt-xxl-1{padding-top:.25rem!important}.pt-xxl-2{padding-top:.5rem!important}.pt-xxl-3{padding-top:1rem!important}.pt-xxl-4{padding-top:1.5rem!important}.pt-xxl-5{padding-top:3rem!important}.pe-xxl-0{padding-right:0!important}.pe-xxl-1{padding-right:.25rem!important}.pe-xxl-2{padding-right:.5rem!important}.pe-xxl-3{padding-right:1rem!important}.pe-xxl-4{padding-right:1.5rem!important}.pe-xxl-5{padding-right:3rem!important}.pb-xxl-0{padding-bottom:0!important}.pb-xxl-1{padding-bottom:.25rem!important}.pb-xxl-2{padding-bottom:.5rem!important}.pb-xxl-3{padding-bottom:1rem!important}.pb-xxl-4{padding-bottom:1.5rem!important}.pb-xxl-5{padding-bottom:3rem!important}.ps-xxl-0{padding-left:0!important}.ps-xxl-1{padding-left:.25rem!important}.ps-xxl-2{padding-left:.5rem!important}.ps-xxl-3{padding-left:1rem!important}.ps-xxl-4{padding-left:1.5rem!important}.ps-xxl-5{padding-left:3rem!important}.gap-xxl-0{gap:0!important}.gap-xxl-1{gap:.25rem!important}.gap-xxl-2{gap:.5rem!important}.gap-xxl-3{gap:1rem!important}.gap-xxl-4{gap:1.5rem!important}.gap-xxl-5{gap:3rem!important}.row-gap-xxl-0{row-gap:0!important}.row-gap-xxl-1{row-gap:.25rem!important}.row-gap-xxl-2{row-gap:.5rem!important}.row-gap-xxl-3{row-gap:1rem!important}.row-gap-xxl-4{row-gap:1.5rem!important}.row-gap-xxl-5{row-gap:3rem!important}.column-gap-xxl-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-xxl-1{-moz-column-gap:0.25rem!important;column-gap:.25rem!important}.column-gap-xxl-2{-moz-column-gap:0.5rem!important;column-gap:.5rem!important}.column-gap-xxl-3{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-xxl-4{-moz-column-gap:1.5rem!important;column-gap:1.5rem!important}.column-gap-xxl-5{-moz-column-gap:3rem!important;column-gap:3rem!important}.text-xxl-start{text-align:left!important}.text-xxl-end{text-align:right!important}.text-xxl-center{text-align:center!important}}@media (min-width:1200px){.fs-1{font-size:2.5rem!important}.fs-2{font-size:2rem!important}.fs-3{font-size:1.75rem!important}.fs-4{font-size:1.5rem!important}}@media print{.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-grid{display:grid!important}.d-print-inline-grid{display:inline-grid!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:flex!important}.d-print-inline-flex{display:inline-flex!important}.d-print-none{display:none!important}} +/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/dejacode/static/bootstrap-5.3.2/css/bootstrap.min.css.map b/dejacode/static/bootstrap-5.3.2/css/bootstrap.min.css.map new file mode 100644 index 00000000..8c582d2d --- /dev/null +++ b/dejacode/static/bootstrap-5.3.2/css/bootstrap.min.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../scss/mixins/_banner.scss","../../scss/_root.scss","dist/css/bootstrap.css","../../scss/vendor/_rfs.scss","../../scss/mixins/_color-mode.scss","../../scss/_reboot.scss","../../scss/mixins/_border-radius.scss","../../scss/_type.scss","../../scss/mixins/_lists.scss","../../scss/_images.scss","../../scss/mixins/_image.scss","../../scss/_containers.scss","../../scss/mixins/_container.scss","../../scss/mixins/_breakpoints.scss","../../scss/_grid.scss","../../scss/mixins/_grid.scss","../../scss/_tables.scss","../../scss/mixins/_table-variants.scss","../../scss/forms/_labels.scss","../../scss/forms/_form-text.scss","../../scss/forms/_form-control.scss","../../scss/mixins/_transition.scss","../../scss/mixins/_gradients.scss","../../scss/forms/_form-select.scss","../../scss/forms/_form-check.scss","../../scss/forms/_form-range.scss","../../scss/forms/_floating-labels.scss","../../scss/forms/_input-group.scss","../../scss/mixins/_forms.scss","../../scss/_buttons.scss","../../scss/mixins/_buttons.scss","../../scss/_transitions.scss","../../scss/_dropdown.scss","../../scss/mixins/_caret.scss","../../scss/_button-group.scss","../../scss/_nav.scss","../../scss/_navbar.scss","../../scss/_card.scss","../../scss/_accordion.scss","../../scss/_breadcrumb.scss","../../scss/_pagination.scss","../../scss/mixins/_pagination.scss","../../scss/_badge.scss","../../scss/_alert.scss","../../scss/_progress.scss","../../scss/_list-group.scss","../../scss/_close.scss","../../scss/_toasts.scss","../../scss/_modal.scss","../../scss/mixins/_backdrop.scss","../../scss/_tooltip.scss","../../scss/mixins/_reset-text.scss","../../scss/_popover.scss","../../scss/_carousel.scss","../../scss/mixins/_clearfix.scss","../../scss/_spinners.scss","../../scss/_offcanvas.scss","../../scss/_placeholders.scss","../../scss/helpers/_color-bg.scss","../../scss/helpers/_colored-links.scss","../../scss/helpers/_focus-ring.scss","../../scss/helpers/_icon-link.scss","../../scss/helpers/_ratio.scss","../../scss/helpers/_position.scss","../../scss/helpers/_stacks.scss","../../scss/helpers/_visually-hidden.scss","../../scss/mixins/_visually-hidden.scss","../../scss/helpers/_stretched-link.scss","../../scss/helpers/_text-truncation.scss","../../scss/mixins/_text-truncate.scss","../../scss/helpers/_vr.scss","../../scss/mixins/_utilities.scss","../../scss/utilities/_api.scss"],"names":[],"mappings":"iBACE;;;;ACDF,MCOA,sBDEI,UAAA,QAAA,YAAA,QAAA,YAAA,QAAA,UAAA,QAAA,SAAA,QAAA,YAAA,QAAA,YAAA,QAAA,WAAA,QAAA,UAAA,QAAA,UAAA,QAAA,WAAA,KAAA,WAAA,KAAA,UAAA,QAAA,eAAA,QAIA,cAAA,QAAA,cAAA,QAAA,cAAA,QAAA,cAAA,QAAA,cAAA,QAAA,cAAA,QAAA,cAAA,QAAA,cAAA,QAAA,cAAA,QAIA,aAAA,QAAA,eAAA,QAAA,aAAA,QAAA,UAAA,QAAA,aAAA,QAAA,YAAA,QAAA,WAAA,QAAA,UAAA,QAIA,iBAAA,EAAA,CAAA,GAAA,CAAA,IAAA,mBAAA,GAAA,CAAA,GAAA,CAAA,IAAA,iBAAA,EAAA,CAAA,GAAA,CAAA,GAAA,cAAA,EAAA,CAAA,GAAA,CAAA,IAAA,iBAAA,GAAA,CAAA,GAAA,CAAA,EAAA,gBAAA,GAAA,CAAA,EAAA,CAAA,GAAA,eAAA,GAAA,CAAA,GAAA,CAAA,IAAA,cAAA,EAAA,CAAA,EAAA,CAAA,GAIA,2BAAA,QAAA,6BAAA,QAAA,2BAAA,QAAA,wBAAA,QAAA,2BAAA,QAAA,0BAAA,QAAA,yBAAA,QAAA,wBAAA,QAIA,uBAAA,QAAA,yBAAA,QAAA,uBAAA,QAAA,oBAAA,QAAA,uBAAA,QAAA,sBAAA,QAAA,qBAAA,QAAA,oBAAA,QAIA,2BAAA,QAAA,6BAAA,QAAA,2BAAA,QAAA,wBAAA,QAAA,2BAAA,QAAA,0BAAA,QAAA,yBAAA,QAAA,wBAAA,QAGF,eAAA,GAAA,CAAA,GAAA,CAAA,IACA,eAAA,CAAA,CAAA,CAAA,CAAA,EAMA,qBAAA,SAAA,CAAA,aAAA,CAAA,UAAA,CAAA,MAAA,CAAA,gBAAA,CAAA,WAAA,CAAA,iBAAA,CAAA,KAAA,CAAA,UAAA,CAAA,mBAAA,CAAA,gBAAA,CAAA,iBAAA,CAAA,mBACA,oBAAA,cAAA,CAAA,KAAA,CAAA,MAAA,CAAA,QAAA,CAAA,iBAAA,CAAA,aAAA,CAAA,UACA,cAAA,2EAOA,sBAAA,0BE2OI,oBAAA,KFzOJ,sBAAA,IACA,sBAAA,IAKA,gBAAA,QACA,oBAAA,EAAA,CAAA,EAAA,CAAA,GACA,aAAA,KACA,iBAAA,GAAA,CAAA,GAAA,CAAA,IAEA,oBAAA,KACA,wBAAA,CAAA,CAAA,CAAA,CAAA,EAEA,qBAAA,uBACA,yBAAA,EAAA,CAAA,EAAA,CAAA,GACA,kBAAA,QACA,sBAAA,GAAA,CAAA,GAAA,CAAA,IAEA,oBAAA,sBACA,wBAAA,EAAA,CAAA,EAAA,CAAA,GACA,iBAAA,QACA,qBAAA,GAAA,CAAA,GAAA,CAAA,IAGA,mBAAA,QAEA,gBAAA,QACA,oBAAA,EAAA,CAAA,GAAA,CAAA,IACA,qBAAA,UAEA,sBAAA,QACA,0BAAA,EAAA,CAAA,EAAA,CAAA,IAMA,gBAAA,QACA,qBAAA,QACA,kBAAA,QAGA,kBAAA,IACA,kBAAA,MACA,kBAAA,QACA,8BAAA,qBAEA,mBAAA,SACA,sBAAA,QACA,sBAAA,OACA,sBAAA,KACA,uBAAA,KACA,uBAAA,4BACA,wBAAA,MAGA,gBAAA,EAAA,OAAA,KAAA,oBACA,mBAAA,EAAA,SAAA,QAAA,qBACA,mBAAA,EAAA,KAAA,KAAA,qBACA,sBAAA,MAAA,EAAA,IAAA,IAAA,qBAIA,sBAAA,QACA,wBAAA,KACA,sBAAA,yBAIA,sBAAA,QACA,6BAAA,QACA,wBAAA,QACA,+BAAA,QGhHE,qBHsHA,aAAA,KAGA,gBAAA,QACA,oBAAA,GAAA,CAAA,GAAA,CAAA,IACA,aAAA,QACA,iBAAA,EAAA,CAAA,EAAA,CAAA,GAEA,oBAAA,KACA,wBAAA,GAAA,CAAA,GAAA,CAAA,IAEA,qBAAA,0BACA,yBAAA,GAAA,CAAA,GAAA,CAAA,IACA,kBAAA,QACA,sBAAA,EAAA,CAAA,EAAA,CAAA,GAEA,oBAAA,yBACA,wBAAA,GAAA,CAAA,GAAA,CAAA,IACA,iBAAA,QACA,qBAAA,EAAA,CAAA,EAAA,CAAA,GAGE,2BAAA,QAAA,6BAAA,QAAA,2BAAA,QAAA,wBAAA,QAAA,2BAAA,QAAA,0BAAA,QAAA,yBAAA,QAAA,wBAAA,QAIA,uBAAA,QAAA,yBAAA,QAAA,uBAAA,QAAA,oBAAA,QAAA,uBAAA,QAAA,sBAAA,QAAA,qBAAA,QAAA,oBAAA,QAIA,2BAAA,QAAA,6BAAA,QAAA,2BAAA,QAAA,wBAAA,QAAA,2BAAA,QAAA,0BAAA,QAAA,yBAAA,QAAA,wBAAA,QAGF,mBAAA,QAEA,gBAAA,QACA,sBAAA,QACA,oBAAA,GAAA,CAAA,GAAA,CAAA,IACA,0BAAA,GAAA,CAAA,GAAA,CAAA,IAEA,gBAAA,QACA,qBAAA,QACA,kBAAA,QAEA,kBAAA,QACA,8BAAA,0BAEA,sBAAA,QACA,6BAAA,QACA,wBAAA,QACA,+BAAA,QIxKJ,EH0KA,QADA,SGtKE,WAAA,WAeE,8CANJ,MAOM,gBAAA,QAcN,KACE,OAAA,EACA,YAAA,2BF6OI,UAAA,yBE3OJ,YAAA,2BACA,YAAA,2BACA,MAAA,qBACA,WAAA,0BACA,iBAAA,kBACA,yBAAA,KACA,4BAAA,YASF,GACE,OAAA,KAAA,EACA,MAAA,QACA,OAAA,EACA,WAAA,uBAAA,MACA,QAAA,IAUF,IAAA,IAAA,IAAA,IAAA,IAAA,IAAA,GAAA,GAAA,GAAA,GAAA,GAAA,GACE,WAAA,EACA,cAAA,MAGA,YAAA,IACA,YAAA,IACA,MAAA,wBAGF,IAAA,GFuMQ,UAAA,uBA5JJ,0BE3CJ,IAAA,GF8MQ,UAAA,QEzMR,IAAA,GFkMQ,UAAA,sBA5JJ,0BEtCJ,IAAA,GFyMQ,UAAA,MEpMR,IAAA,GF6LQ,UAAA,oBA5JJ,0BEjCJ,IAAA,GFoMQ,UAAA,SE/LR,IAAA,GFwLQ,UAAA,sBA5JJ,0BE5BJ,IAAA,GF+LQ,UAAA,QE1LR,IAAA,GF+KM,UAAA,QE1KN,IAAA,GF0KM,UAAA,KE/JN,EACE,WAAA,EACA,cAAA,KAUF,YACE,wBAAA,UAAA,OAAA,gBAAA,UAAA,OACA,OAAA,KACA,iCAAA,KAAA,yBAAA,KAMF,QACE,cAAA,KACA,WAAA,OACA,YAAA,QAMF,GHkIA,GGhIE,aAAA,KHsIF,GGnIA,GHkIA,GG/HE,WAAA,EACA,cAAA,KAGF,MHmIA,MACA,MAFA,MG9HE,cAAA,EAGF,GACE,YAAA,IAKF,GACE,cAAA,MACA,YAAA,EAMF,WACE,OAAA,EAAA,EAAA,KAQF,EHwHA,OGtHE,YAAA,OAQF,OAAA,MF6EM,UAAA,OEtEN,MAAA,KACE,QAAA,QACA,MAAA,0BACA,iBAAA,uBASF,IH0GA,IGxGE,SAAA,SFwDI,UAAA,MEtDJ,YAAA,EACA,eAAA,SAGF,IAAM,OAAA,OACN,IAAM,IAAA,MAKN,EACE,MAAA,wDACA,gBAAA,UAEA,QACE,oBAAA,+BAWF,2BAAA,iCAEE,MAAA,QACA,gBAAA,KHsGJ,KACA,IGhGA,IHiGA,KG7FE,YAAA,yBFcI,UAAA,IENN,IACE,QAAA,MACA,WAAA,EACA,cAAA,KACA,SAAA,KFEI,UAAA,OEGJ,SFHI,UAAA,QEKF,MAAA,QACA,WAAA,OAIJ,KFVM,UAAA,OEYJ,MAAA,qBACA,UAAA,WAGA,OACE,MAAA,QAIJ,IACE,QAAA,SAAA,QFtBI,UAAA,OEwBJ,MAAA,kBACA,iBAAA,qBCrSE,cAAA,ODwSF,QACE,QAAA,EF7BE,UAAA,IEwCN,OACE,OAAA,EAAA,EAAA,KAMF,IH4EA,IG1EE,eAAA,OAQF,MACE,aAAA,OACA,gBAAA,SAGF,QACE,YAAA,MACA,eAAA,MACA,MAAA,0BACA,WAAA,KAOF,GAEE,WAAA,QACA,WAAA,qBHqEF,MAGA,GAFA,MAGA,GGtEA,MHoEA,GG9DE,aAAA,QACA,aAAA,MACA,aAAA,EAQF,MACE,QAAA,aAMF,OAEE,cAAA,EAQF,iCACE,QAAA,EHuDF,OGlDA,MHoDA,SADA,OAEA,SGhDE,OAAA,EACA,YAAA,QF5HI,UAAA,QE8HJ,YAAA,QAIF,OHiDA,OG/CE,eAAA,KAKF,cACE,OAAA,QAGF,OAGE,UAAA,OAGA,gBACE,QAAA,EAOJ,0IACE,QAAA,eH2CF,cACA,aACA,cGrCA,OAIE,mBAAA,OHqCF,6BACA,4BACA,6BGpCI,sBACE,OAAA,QAON,mBACE,QAAA,EACA,aAAA,KAKF,SACE,OAAA,SAUF,SACE,UAAA,EACA,QAAA,EACA,OAAA,EACA,OAAA,EAQF,OACE,MAAA,KACA,MAAA,KACA,QAAA,EACA,cAAA,MFjNM,UAAA,sBEoNN,YAAA,QFhXE,0BEyWJ,OFtMQ,UAAA,QE+MN,SACE,MAAA,KH6BJ,kCGtBA,uCHqBA,mCADA,+BAGA,oCAJA,6BAKA,mCGjBE,QAAA,EAGF,4BACE,OAAA,KASF,cACE,mBAAA,UACA,eAAA,KAmBF,4BACE,mBAAA,KAKF,+BACE,QAAA,EAOF,6BACE,KAAA,QACA,mBAAA,OAFF,uBACE,KAAA,QACA,mBAAA,OAKF,OACE,QAAA,aAKF,OACE,OAAA,EAOF,QACE,QAAA,UACA,OAAA,QAQF,SACE,eAAA,SAQF,SACE,QAAA,eErkBF,MJmQM,UAAA,QIjQJ,YAAA,IAKA,WJgQM,UAAA,uBI5PJ,YAAA,IACA,YAAA,IJ+FA,0BIpGF,WJuQM,UAAA,MIvQN,WJgQM,UAAA,uBI5PJ,YAAA,IACA,YAAA,IJ+FA,0BIpGF,WJuQM,UAAA,QIvQN,WJgQM,UAAA,uBI5PJ,YAAA,IACA,YAAA,IJ+FA,0BIpGF,WJuQM,UAAA,MIvQN,WJgQM,UAAA,uBI5PJ,YAAA,IACA,YAAA,IJ+FA,0BIpGF,WJuQM,UAAA,QIvQN,WJgQM,UAAA,uBI5PJ,YAAA,IACA,YAAA,IJ+FA,0BIpGF,WJuQM,UAAA,MIvQN,WJgQM,UAAA,uBI5PJ,YAAA,IACA,YAAA,IJ+FA,0BIpGF,WJuQM,UAAA,QI/OR,eCvDE,aAAA,EACA,WAAA,KD2DF,aC5DE,aAAA,EACA,WAAA,KD8DF,kBACE,QAAA,aAEA,mCACE,aAAA,MAUJ,YJ8MM,UAAA,OI5MJ,eAAA,UAIF,YACE,cAAA,KJuMI,UAAA,QIpMJ,wBACE,cAAA,EAIJ,mBACE,WAAA,MACA,cAAA,KJ6LI,UAAA,OI3LJ,MAAA,QAEA,2BACE,QAAA,KEhGJ,WCIE,UAAA,KAGA,OAAA,KDDF,eACE,QAAA,OACA,iBAAA,kBACA,OAAA,uBAAA,MAAA,uBHGE,cAAA,wBIRF,UAAA,KAGA,OAAA,KDcF,QAEE,QAAA,aAGF,YACE,cAAA,MACA,YAAA,EAGF,gBNyPM,UAAA,OMvPJ,MAAA,0BElCA,WT2tBF,iBAGA,cACA,cACA,cAHA,cADA,eU/tBE,cAAA,OACA,cAAA,EACA,MAAA,KACA,cAAA,8BACA,aAAA,8BACA,aAAA,KACA,YAAA,KCsDE,yBF5CE,WAAA,cACE,UAAA,OE2CJ,yBF5CE,WAAA,cAAA,cACE,UAAA,OE2CJ,yBF5CE,WAAA,cAAA,cAAA,cACE,UAAA,OE2CJ,0BF5CE,WAAA,cAAA,cAAA,cAAA,cACE,UAAA,QE2CJ,0BF5CE,WAAA,cAAA,cAAA,cAAA,cAAA,eACE,UAAA,QGhBR,MAEI,mBAAA,EAAA,mBAAA,MAAA,mBAAA,MAAA,mBAAA,MAAA,mBAAA,OAAA,oBAAA,OAKF,KCNA,cAAA,OACA,cAAA,EACA,QAAA,KACA,UAAA,KAEA,WAAA,8BACA,aAAA,+BACA,YAAA,+BDEE,OCOF,YAAA,EACA,MAAA,KACA,UAAA,KACA,cAAA,8BACA,aAAA,8BACA,WAAA,mBA+CI,KACE,KAAA,EAAA,EAAA,GAGF,iBApCJ,KAAA,EAAA,EAAA,KACA,MAAA,KAcA,cACE,KAAA,EAAA,EAAA,KACA,MAAA,KAFF,cACE,KAAA,EAAA,EAAA,KACA,MAAA,IAFF,cACE,KAAA,EAAA,EAAA,KACA,MAAA,aAFF,cACE,KAAA,EAAA,EAAA,KACA,MAAA,IAFF,cACE,KAAA,EAAA,EAAA,KACA,MAAA,IAFF,cACE,KAAA,EAAA,EAAA,KACA,MAAA,aA+BE,UAhDJ,KAAA,EAAA,EAAA,KACA,MAAA,KAqDQ,OAhEN,KAAA,EAAA,EAAA,KACA,MAAA,YA+DM,OAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,OAhEN,KAAA,EAAA,EAAA,KACA,MAAA,IA+DM,OAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,OAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,OAhEN,KAAA,EAAA,EAAA,KACA,MAAA,IA+DM,OAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,OAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,OAhEN,KAAA,EAAA,EAAA,KACA,MAAA,IA+DM,QAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,QAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,QAhEN,KAAA,EAAA,EAAA,KACA,MAAA,KAuEQ,UAxDV,YAAA,YAwDU,UAxDV,YAAA,aAwDU,UAxDV,YAAA,IAwDU,UAxDV,YAAA,aAwDU,UAxDV,YAAA,aAwDU,UAxDV,YAAA,IAwDU,UAxDV,YAAA,aAwDU,UAxDV,YAAA,aAwDU,UAxDV,YAAA,IAwDU,WAxDV,YAAA,aAwDU,WAxDV,YAAA,aAmEM,Kbu0BR,Mar0BU,cAAA,EAGF,Kbu0BR,Mar0BU,cAAA,EAPF,Kbi1BR,Ma/0BU,cAAA,QAGF,Kbi1BR,Ma/0BU,cAAA,QAPF,Kb21BR,Maz1BU,cAAA,OAGF,Kb21BR,Maz1BU,cAAA,OAPF,Kbq2BR,Man2BU,cAAA,KAGF,Kbq2BR,Man2BU,cAAA,KAPF,Kb+2BR,Ma72BU,cAAA,OAGF,Kb+2BR,Ma72BU,cAAA,OAPF,Kby3BR,Mav3BU,cAAA,KAGF,Kby3BR,Mav3BU,cAAA,KF1DN,yBEUE,QACE,KAAA,EAAA,EAAA,GAGF,oBApCJ,KAAA,EAAA,EAAA,KACA,MAAA,KAcA,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,KAFF,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,IAFF,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,aAFF,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,IAFF,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,IAFF,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,aA+BE,aAhDJ,KAAA,EAAA,EAAA,KACA,MAAA,KAqDQ,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,YA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,IA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,IA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,IA+DM,WAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,WAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,WAhEN,KAAA,EAAA,EAAA,KACA,MAAA,KAuEQ,aAxDV,YAAA,EAwDU,aAxDV,YAAA,YAwDU,aAxDV,YAAA,aAwDU,aAxDV,YAAA,IAwDU,aAxDV,YAAA,aAwDU,aAxDV,YAAA,aAwDU,aAxDV,YAAA,IAwDU,aAxDV,YAAA,aAwDU,aAxDV,YAAA,aAwDU,aAxDV,YAAA,IAwDU,cAxDV,YAAA,aAwDU,cAxDV,YAAA,aAmEM,Qb2/BN,Saz/BQ,cAAA,EAGF,Qb0/BN,Sax/BQ,cAAA,EAPF,QbmgCN,SajgCQ,cAAA,QAGF,QbkgCN,SahgCQ,cAAA,QAPF,Qb2gCN,SazgCQ,cAAA,OAGF,Qb0gCN,SaxgCQ,cAAA,OAPF,QbmhCN,SajhCQ,cAAA,KAGF,QbkhCN,SahhCQ,cAAA,KAPF,Qb2hCN,SazhCQ,cAAA,OAGF,Qb0hCN,SaxhCQ,cAAA,OAPF,QbmiCN,SajiCQ,cAAA,KAGF,QbkiCN,SahiCQ,cAAA,MF1DN,yBEUE,QACE,KAAA,EAAA,EAAA,GAGF,oBApCJ,KAAA,EAAA,EAAA,KACA,MAAA,KAcA,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,KAFF,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,IAFF,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,aAFF,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,IAFF,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,IAFF,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,aA+BE,aAhDJ,KAAA,EAAA,EAAA,KACA,MAAA,KAqDQ,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,YA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,IA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,IA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,IA+DM,WAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,WAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,WAhEN,KAAA,EAAA,EAAA,KACA,MAAA,KAuEQ,aAxDV,YAAA,EAwDU,aAxDV,YAAA,YAwDU,aAxDV,YAAA,aAwDU,aAxDV,YAAA,IAwDU,aAxDV,YAAA,aAwDU,aAxDV,YAAA,aAwDU,aAxDV,YAAA,IAwDU,aAxDV,YAAA,aAwDU,aAxDV,YAAA,aAwDU,aAxDV,YAAA,IAwDU,cAxDV,YAAA,aAwDU,cAxDV,YAAA,aAmEM,QboqCN,SalqCQ,cAAA,EAGF,QbmqCN,SajqCQ,cAAA,EAPF,Qb4qCN,Sa1qCQ,cAAA,QAGF,Qb2qCN,SazqCQ,cAAA,QAPF,QborCN,SalrCQ,cAAA,OAGF,QbmrCN,SajrCQ,cAAA,OAPF,Qb4rCN,Sa1rCQ,cAAA,KAGF,Qb2rCN,SazrCQ,cAAA,KAPF,QbosCN,SalsCQ,cAAA,OAGF,QbmsCN,SajsCQ,cAAA,OAPF,Qb4sCN,Sa1sCQ,cAAA,KAGF,Qb2sCN,SazsCQ,cAAA,MF1DN,yBEUE,QACE,KAAA,EAAA,EAAA,GAGF,oBApCJ,KAAA,EAAA,EAAA,KACA,MAAA,KAcA,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,KAFF,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,IAFF,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,aAFF,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,IAFF,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,IAFF,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,aA+BE,aAhDJ,KAAA,EAAA,EAAA,KACA,MAAA,KAqDQ,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,YA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,IA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,IA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,IA+DM,WAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,WAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,WAhEN,KAAA,EAAA,EAAA,KACA,MAAA,KAuEQ,aAxDV,YAAA,EAwDU,aAxDV,YAAA,YAwDU,aAxDV,YAAA,aAwDU,aAxDV,YAAA,IAwDU,aAxDV,YAAA,aAwDU,aAxDV,YAAA,aAwDU,aAxDV,YAAA,IAwDU,aAxDV,YAAA,aAwDU,aAxDV,YAAA,aAwDU,aAxDV,YAAA,IAwDU,cAxDV,YAAA,aAwDU,cAxDV,YAAA,aAmEM,Qb60CN,Sa30CQ,cAAA,EAGF,Qb40CN,Sa10CQ,cAAA,EAPF,Qbq1CN,San1CQ,cAAA,QAGF,Qbo1CN,Sal1CQ,cAAA,QAPF,Qb61CN,Sa31CQ,cAAA,OAGF,Qb41CN,Sa11CQ,cAAA,OAPF,Qbq2CN,San2CQ,cAAA,KAGF,Qbo2CN,Sal2CQ,cAAA,KAPF,Qb62CN,Sa32CQ,cAAA,OAGF,Qb42CN,Sa12CQ,cAAA,OAPF,Qbq3CN,San3CQ,cAAA,KAGF,Qbo3CN,Sal3CQ,cAAA,MF1DN,0BEUE,QACE,KAAA,EAAA,EAAA,GAGF,oBApCJ,KAAA,EAAA,EAAA,KACA,MAAA,KAcA,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,KAFF,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,IAFF,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,aAFF,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,IAFF,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,IAFF,iBACE,KAAA,EAAA,EAAA,KACA,MAAA,aA+BE,aAhDJ,KAAA,EAAA,EAAA,KACA,MAAA,KAqDQ,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,YA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,IA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,IA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,UAhEN,KAAA,EAAA,EAAA,KACA,MAAA,IA+DM,WAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,WAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,WAhEN,KAAA,EAAA,EAAA,KACA,MAAA,KAuEQ,aAxDV,YAAA,EAwDU,aAxDV,YAAA,YAwDU,aAxDV,YAAA,aAwDU,aAxDV,YAAA,IAwDU,aAxDV,YAAA,aAwDU,aAxDV,YAAA,aAwDU,aAxDV,YAAA,IAwDU,aAxDV,YAAA,aAwDU,aAxDV,YAAA,aAwDU,aAxDV,YAAA,IAwDU,cAxDV,YAAA,aAwDU,cAxDV,YAAA,aAmEM,Qbs/CN,Sap/CQ,cAAA,EAGF,Qbq/CN,San/CQ,cAAA,EAPF,Qb8/CN,Sa5/CQ,cAAA,QAGF,Qb6/CN,Sa3/CQ,cAAA,QAPF,QbsgDN,SapgDQ,cAAA,OAGF,QbqgDN,SangDQ,cAAA,OAPF,Qb8gDN,Sa5gDQ,cAAA,KAGF,Qb6gDN,Sa3gDQ,cAAA,KAPF,QbshDN,SaphDQ,cAAA,OAGF,QbqhDN,SanhDQ,cAAA,OAPF,Qb8hDN,Sa5hDQ,cAAA,KAGF,Qb6hDN,Sa3hDQ,cAAA,MF1DN,0BEUE,SACE,KAAA,EAAA,EAAA,GAGF,qBApCJ,KAAA,EAAA,EAAA,KACA,MAAA,KAcA,kBACE,KAAA,EAAA,EAAA,KACA,MAAA,KAFF,kBACE,KAAA,EAAA,EAAA,KACA,MAAA,IAFF,kBACE,KAAA,EAAA,EAAA,KACA,MAAA,aAFF,kBACE,KAAA,EAAA,EAAA,KACA,MAAA,IAFF,kBACE,KAAA,EAAA,EAAA,KACA,MAAA,IAFF,kBACE,KAAA,EAAA,EAAA,KACA,MAAA,aA+BE,cAhDJ,KAAA,EAAA,EAAA,KACA,MAAA,KAqDQ,WAhEN,KAAA,EAAA,EAAA,KACA,MAAA,YA+DM,WAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,WAhEN,KAAA,EAAA,EAAA,KACA,MAAA,IA+DM,WAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,WAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,WAhEN,KAAA,EAAA,EAAA,KACA,MAAA,IA+DM,WAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,WAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,WAhEN,KAAA,EAAA,EAAA,KACA,MAAA,IA+DM,YAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,YAhEN,KAAA,EAAA,EAAA,KACA,MAAA,aA+DM,YAhEN,KAAA,EAAA,EAAA,KACA,MAAA,KAuEQ,cAxDV,YAAA,EAwDU,cAxDV,YAAA,YAwDU,cAxDV,YAAA,aAwDU,cAxDV,YAAA,IAwDU,cAxDV,YAAA,aAwDU,cAxDV,YAAA,aAwDU,cAxDV,YAAA,IAwDU,cAxDV,YAAA,aAwDU,cAxDV,YAAA,aAwDU,cAxDV,YAAA,IAwDU,eAxDV,YAAA,aAwDU,eAxDV,YAAA,aAmEM,Sb+pDN,Ua7pDQ,cAAA,EAGF,Sb8pDN,Ua5pDQ,cAAA,EAPF,SbuqDN,UarqDQ,cAAA,QAGF,SbsqDN,UapqDQ,cAAA,QAPF,Sb+qDN,Ua7qDQ,cAAA,OAGF,Sb8qDN,Ua5qDQ,cAAA,OAPF,SburDN,UarrDQ,cAAA,KAGF,SbsrDN,UaprDQ,cAAA,KAPF,Sb+rDN,Ua7rDQ,cAAA,OAGF,Sb8rDN,Ua5rDQ,cAAA,OAPF,SbusDN,UarsDQ,cAAA,KAGF,SbssDN,UapsDQ,cAAA,MCrHV,OAEE,sBAAA,QACA,mBAAA,QACA,uBAAA,QACA,oBAAA,QAEA,iBAAA,yBACA,cAAA,kBACA,wBAAA,uBACA,qBAAA,YACA,yBAAA,yBACA,sBAAA,yCACA,wBAAA,yBACA,qBAAA,wCACA,uBAAA,yBACA,oBAAA,0CAEA,MAAA,KACA,cAAA,KACA,eAAA,IACA,aAAA,6BAOA,yBACE,QAAA,MAAA,MAEA,MAAA,6EACA,iBAAA,mBACA,oBAAA,uBACA,WAAA,MAAA,EAAA,EAAA,EAAA,OAAA,2EAGF,aACE,eAAA,QAGF,aACE,eAAA,OAIJ,qBACE,WAAA,iCAAA,MAAA,aAOF,aACE,aAAA,IAUA,4BACE,QAAA,OAAA,OAeF,gCACE,aAAA,uBAAA,EAGA,kCACE,aAAA,EAAA,uBAOJ,oCACE,oBAAA,EAGF,qCACE,iBAAA,EAUF,2CACE,sBAAA,8BACA,mBAAA,2BAMF,uDACE,sBAAA,8BACA,mBAAA,2BAQJ,cACE,uBAAA,6BACA,oBAAA,0BAQA,8BACE,uBAAA,4BACA,oBAAA,yBC5IF,eAOE,iBAAA,KACA,cAAA,QACA,wBAAA,QACA,sBAAA,QACA,yBAAA,KACA,qBAAA,QACA,wBAAA,KACA,oBAAA,QACA,uBAAA,KAEA,MAAA,sBACA,aAAA,6BAlBF,iBAOE,iBAAA,KACA,cAAA,QACA,wBAAA,QACA,sBAAA,QACA,yBAAA,KACA,qBAAA,QACA,wBAAA,KACA,oBAAA,QACA,uBAAA,KAEA,MAAA,sBACA,aAAA,6BAlBF,eAOE,iBAAA,KACA,cAAA,QACA,wBAAA,QACA,sBAAA,QACA,yBAAA,KACA,qBAAA,QACA,wBAAA,KACA,oBAAA,QACA,uBAAA,KAEA,MAAA,sBACA,aAAA,6BAlBF,YAOE,iBAAA,KACA,cAAA,QACA,wBAAA,QACA,sBAAA,QACA,yBAAA,KACA,qBAAA,QACA,wBAAA,KACA,oBAAA,QACA,uBAAA,KAEA,MAAA,sBACA,aAAA,6BAlBF,eAOE,iBAAA,KACA,cAAA,QACA,wBAAA,QACA,sBAAA,QACA,yBAAA,KACA,qBAAA,QACA,wBAAA,KACA,oBAAA,QACA,uBAAA,KAEA,MAAA,sBACA,aAAA,6BAlBF,cAOE,iBAAA,KACA,cAAA,QACA,wBAAA,QACA,sBAAA,QACA,yBAAA,KACA,qBAAA,QACA,wBAAA,KACA,oBAAA,QACA,uBAAA,KAEA,MAAA,sBACA,aAAA,6BAlBF,aAOE,iBAAA,KACA,cAAA,QACA,wBAAA,QACA,sBAAA,QACA,yBAAA,KACA,qBAAA,QACA,wBAAA,KACA,oBAAA,QACA,uBAAA,KAEA,MAAA,sBACA,aAAA,6BAlBF,YAOE,iBAAA,KACA,cAAA,QACA,wBAAA,QACA,sBAAA,QACA,yBAAA,KACA,qBAAA,QACA,wBAAA,KACA,oBAAA,QACA,uBAAA,KAEA,MAAA,sBACA,aAAA,6BDiJA,kBACE,WAAA,KACA,2BAAA,MH3FF,4BGyFA,qBACE,WAAA,KACA,2BAAA,OH3FF,4BGyFA,qBACE,WAAA,KACA,2BAAA,OH3FF,4BGyFA,qBACE,WAAA,KACA,2BAAA,OH3FF,6BGyFA,qBACE,WAAA,KACA,2BAAA,OH3FF,6BGyFA,sBACE,WAAA,KACA,2BAAA,OEnKN,YACE,cAAA,MASF,gBACE,YAAA,uCACA,eAAA,uCACA,cAAA,Ef8QI,UAAA,Qe1QJ,YAAA,IAIF,mBACE,YAAA,qCACA,eAAA,qCfoQI,UAAA,QehQN,mBACE,YAAA,sCACA,eAAA,sCf8PI,UAAA,QgB3RN,WACE,WAAA,OhB0RI,UAAA,OgBtRJ,MAAA,0BCLF,cACE,QAAA,MACA,MAAA,KACA,QAAA,QAAA,OjBwRI,UAAA,KiBrRJ,YAAA,IACA,YAAA,IACA,MAAA,qBACA,mBAAA,KAAA,gBAAA,KAAA,WAAA,KACA,iBAAA,kBACA,gBAAA,YACA,OAAA,uBAAA,MAAA,uBdGE,cAAA,wBeHE,WAAA,aAAA,KAAA,WAAA,CAAA,WAAA,KAAA,YAIA,uCDhBN,cCiBQ,WAAA,MDGN,yBACE,SAAA,OAEA,wDACE,OAAA,QAKJ,oBACE,MAAA,qBACA,iBAAA,kBACA,aAAA,QACA,QAAA,EAKE,WAAA,EAAA,EAAA,EAAA,OAAA,qBAIJ,2CAME,UAAA,KAMA,OAAA,MAKA,OAAA,EAKF,qCACE,QAAA,MACA,QAAA,EAIF,gCACE,MAAA,0BAEA,QAAA,EAHF,2BACE,MAAA,0BAEA,QAAA,EAQF,uBAEE,iBAAA,uBAGA,QAAA,EAIF,0CACE,QAAA,QAAA,OACA,OAAA,SAAA,QACA,mBAAA,OAAA,kBAAA,OACA,MAAA,qBE9FF,iBAAA,sBFgGE,eAAA,KACA,aAAA,QACA,aAAA,MACA,aAAA,EACA,wBAAA,uBACA,cAAA,ECzFE,mBAAA,MAAA,KAAA,WAAA,CAAA,iBAAA,KAAA,WAAA,CAAA,aAAA,KAAA,WAAA,CAAA,WAAA,KAAA,YAAA,WAAA,MAAA,KAAA,WAAA,CAAA,iBAAA,KAAA,WAAA,CAAA,aAAA,KAAA,WAAA,CAAA,WAAA,KAAA,YD8EJ,oCACE,QAAA,QAAA,OACA,OAAA,SAAA,QACA,mBAAA,OAAA,kBAAA,OACA,MAAA,qBE9FF,iBAAA,sBFgGE,eAAA,KACA,aAAA,QACA,aAAA,MACA,aAAA,EACA,wBAAA,uBACA,cAAA,ECzFE,WAAA,MAAA,KAAA,WAAA,CAAA,iBAAA,KAAA,WAAA,CAAA,aAAA,KAAA,WAAA,CAAA,WAAA,KAAA,YAIA,uCD0EJ,0CCzEM,mBAAA,KAAA,WAAA,KDyEN,oCCzEM,WAAA,MDwFN,+EACE,iBAAA,uBADF,yEACE,iBAAA,uBASJ,wBACE,QAAA,MACA,MAAA,KACA,QAAA,QAAA,EACA,cAAA,EACA,YAAA,IACA,MAAA,qBACA,iBAAA,YACA,OAAA,MAAA,YACA,aAAA,uBAAA,EAEA,8BACE,QAAA,EAGF,wCAAA,wCAEE,cAAA,EACA,aAAA,EAWJ,iBACE,WAAA,uDACA,QAAA,OAAA,MjByII,UAAA,QG5QF,cAAA,2BcuIF,6CACE,QAAA,OAAA,MACA,OAAA,QAAA,OACA,mBAAA,MAAA,kBAAA,MAHF,uCACE,QAAA,OAAA,MACA,OAAA,QAAA,OACA,mBAAA,MAAA,kBAAA,MAIJ,iBACE,WAAA,sDACA,QAAA,MAAA,KjB4HI,UAAA,QG5QF,cAAA,2BcoJF,6CACE,QAAA,MAAA,KACA,OAAA,OAAA,MACA,mBAAA,KAAA,kBAAA,KAHF,uCACE,QAAA,MAAA,KACA,OAAA,OAAA,MACA,mBAAA,KAAA,kBAAA,KAQF,sBACE,WAAA,wDAGF,yBACE,WAAA,uDAGF,yBACE,WAAA,sDAKJ,oBACE,MAAA,KACA,OAAA,wDACA,QAAA,QAEA,mDACE,OAAA,QAGF,uCACE,OAAA,YdvLA,cAAA,wBc2LF,0CACE,OAAA,Yd5LA,cAAA,wBcgMF,oCAAoB,OAAA,uDACpB,oCAAoB,OAAA,sDG/MtB,aACE,wBAAA,gOAEA,QAAA,MACA,MAAA,KACA,QAAA,QAAA,QAAA,QAAA,OpBqRI,UAAA,KoBlRJ,YAAA,IACA,YAAA,IACA,MAAA,qBACA,mBAAA,KAAA,gBAAA,KAAA,WAAA,KACA,iBAAA,kBACA,iBAAA,4BAAA,CAAA,mCACA,kBAAA,UACA,oBAAA,MAAA,OAAA,OACA,gBAAA,KAAA,KACA,OAAA,uBAAA,MAAA,uBjBHE,cAAA,wBeHE,WAAA,aAAA,KAAA,WAAA,CAAA,WAAA,KAAA,YAIA,uCEfN,aFgBQ,WAAA,MEMN,mBACE,aAAA,QACA,QAAA,EAKE,WAAA,EAAA,EAAA,EAAA,OAAA,qBAIJ,uBAAA,mCAEE,cAAA,OACA,iBAAA,KAGF,sBAEE,iBAAA,uBAKF,4BACE,MAAA,YACA,YAAA,EAAA,EAAA,EAAA,qBAIJ,gBACE,YAAA,OACA,eAAA,OACA,aAAA,MpBmOI,UAAA,QG5QF,cAAA,2BiB8CJ,gBACE,YAAA,MACA,eAAA,MACA,aAAA,KpB2NI,UAAA,QG5QF,cAAA,2BiBwDA,kCACE,wBAAA,gOCxEN,YACE,QAAA,MACA,WAAA,OACA,aAAA,MACA,cAAA,QAEA,8BACE,MAAA,KACA,YAAA,OAIJ,oBACE,cAAA,MACA,aAAA,EACA,WAAA,MAEA,sCACE,MAAA,MACA,aAAA,OACA,YAAA,EAIJ,kBACE,mBAAA,kBAEA,YAAA,EACA,MAAA,IACA,OAAA,IACA,WAAA,MACA,eAAA,IACA,mBAAA,KAAA,gBAAA,KAAA,WAAA,KACA,iBAAA,wBACA,iBAAA,8BACA,kBAAA,UACA,oBAAA,OACA,gBAAA,QACA,OAAA,uBAAA,MAAA,uBACA,2BAAA,MAAA,aAAA,MAAA,mBAAA,MAGA,iClB3BE,cAAA,MkB+BF,8BAEE,cAAA,IAGF,yBACE,OAAA,gBAGF,wBACE,aAAA,QACA,QAAA,EACA,WAAA,EAAA,EAAA,EAAA,OAAA,qBAGF,0BACE,iBAAA,QACA,aAAA,QAEA,yCAII,yBAAA,8NAIJ,sCAII,yBAAA,sIAKN,+CACE,iBAAA,QACA,aAAA,QAKE,yBAAA,wNAIJ,2BACE,eAAA,KACA,OAAA,KACA,QAAA,GAOA,6CAAA,8CACE,OAAA,QACA,QAAA,GAcN,aACE,aAAA,MAEA,+BACE,oBAAA,uJAEA,MAAA,IACA,YAAA,OACA,iBAAA,yBACA,oBAAA,KAAA,OlBjHA,cAAA,IeHE,WAAA,oBAAA,KAAA,YAIA,uCG0GJ,+BHzGM,WAAA,MGmHJ,qCACE,oBAAA,yIAGF,uCACE,oBAAA,MAAA,OAKE,oBAAA,sIAKN,gCACE,cAAA,MACA,aAAA,EAEA,kDACE,aAAA,OACA,YAAA,EAKN,mBACE,QAAA,aACA,aAAA,KAGF,WACE,SAAA,SACA,KAAA,cACA,eAAA,KAIE,yBAAA,0BACE,eAAA,KACA,OAAA,KACA,QAAA,IAOF,8EACE,oBAAA,6JCnLN,YACE,MAAA,KACA,OAAA,OACA,QAAA,EACA,mBAAA,KAAA,gBAAA,KAAA,WAAA,KACA,iBAAA,YAEA,kBACE,QAAA,EAIA,wCAA0B,WAAA,EAAA,EAAA,EAAA,IAAA,IAAA,CAAA,EAAA,EAAA,EAAA,OAAA,qBAC1B,oCAA0B,WAAA,EAAA,EAAA,EAAA,IAAA,IAAA,CAAA,EAAA,EAAA,EAAA,OAAA,qBAG5B,8BACE,OAAA,EAGF,kCACE,MAAA,KACA,OAAA,KACA,WAAA,QACA,mBAAA,KAAA,WAAA,KH1BF,iBAAA,QG4BE,OAAA,EnBbA,cAAA,KeHE,mBAAA,iBAAA,KAAA,WAAA,CAAA,aAAA,KAAA,WAAA,CAAA,WAAA,KAAA,YAAA,WAAA,iBAAA,KAAA,WAAA,CAAA,aAAA,KAAA,WAAA,CAAA,WAAA,KAAA,YAIA,uCIMJ,kCJLM,mBAAA,KAAA,WAAA,MIgBJ,yCHjCF,iBAAA,QGsCA,2CACE,MAAA,KACA,OAAA,MACA,MAAA,YACA,OAAA,QACA,iBAAA,uBACA,aAAA,YnB7BA,cAAA,KmBkCF,8BACE,MAAA,KACA,OAAA,KACA,gBAAA,KAAA,WAAA,KHpDF,iBAAA,QGsDE,OAAA,EnBvCA,cAAA,KeHE,gBAAA,iBAAA,KAAA,WAAA,CAAA,aAAA,KAAA,WAAA,CAAA,WAAA,KAAA,YAAA,WAAA,iBAAA,KAAA,WAAA,CAAA,aAAA,KAAA,WAAA,CAAA,WAAA,KAAA,YAIA,uCIiCJ,8BJhCM,gBAAA,KAAA,WAAA,MI0CJ,qCH3DF,iBAAA,QGgEA,8BACE,MAAA,KACA,OAAA,MACA,MAAA,YACA,OAAA,QACA,iBAAA,uBACA,aAAA,YnBvDA,cAAA,KmB4DF,qBACE,eAAA,KAEA,2CACE,iBAAA,0BAGF,uCACE,iBAAA,0BCvFN,eACE,SAAA,SAEA,6BxBmiFF,uCACA,4BwBjiFI,OAAA,gDACA,WAAA,gDACA,YAAA,KAGF,qBACE,SAAA,SACA,IAAA,EACA,KAAA,EACA,QAAA,EACA,OAAA,KACA,QAAA,KAAA,OACA,SAAA,OACA,WAAA,MACA,cAAA,SACA,YAAA,OACA,eAAA,KACA,OAAA,uBAAA,MAAA,YACA,iBAAA,EAAA,ELRE,WAAA,QAAA,IAAA,WAAA,CAAA,UAAA,IAAA,YAIA,uCKTJ,qBLUM,WAAA,MKON,6BxBsiFF,uCwBpiFI,QAAA,KAAA,OAEA,yDAAA,+CACE,MAAA,YxBwiFN,oDwBziFI,0CACE,MAAA,YAGF,oEAAA,0DAEE,YAAA,SACA,eAAA,QxB0iFN,6CACA,+DwB9iFI,mCAAA,qDAEE,YAAA,SACA,eAAA,QxBgjFN,wDwB7iFI,8CACE,YAAA,SACA,eAAA,QAIJ,4BACE,YAAA,SACA,eAAA,QAOA,gEACE,MAAA,mCACA,UAAA,WAAA,mBAAA,mBxB0iFN,6CwB5iFI,yCxB2iFJ,2DAEA,kCwB5iFM,MAAA,mCACA,UAAA,WAAA,mBAAA,mBAEA,uEACE,SAAA,SACA,MAAA,KAAA,SACA,QAAA,GACA,OAAA,MACA,QAAA,GACA,iBAAA,kBpBhDJ,cAAA,wBJkmFJ,oDwBxjFM,gDxBujFN,kEAEA,yCwBxjFQ,SAAA,SACA,MAAA,KAAA,SACA,QAAA,GACA,OAAA,MACA,QAAA,GACA,iBAAA,kBpBhDJ,cAAA,wBoBuDA,oDACE,MAAA,mCACA,UAAA,WAAA,mBAAA,mBAKF,6CACE,aAAA,uBAAA,ExBqjFN,4CwBjjFE,+BAEE,MAAA,QxBmjFJ,mDwBjjFI,sCACE,iBAAA,uBCvFN,aACE,SAAA,SACA,QAAA,KACA,UAAA,KACA,YAAA,QACA,MAAA,KAEA,2BzB6oFF,4BADA,0ByBzoFI,SAAA,SACA,KAAA,EAAA,EAAA,KACA,MAAA,GACA,UAAA,EAIF,iCzB2oFF,yCADA,gCyBvoFI,QAAA,EAMF,kBACE,SAAA,SACA,QAAA,EAEA,wBACE,QAAA,EAWN,kBACE,QAAA,KACA,YAAA,OACA,QAAA,QAAA,OxB8OI,UAAA,KwB5OJ,YAAA,IACA,YAAA,IACA,MAAA,qBACA,WAAA,OACA,YAAA,OACA,iBAAA,sBACA,OAAA,uBAAA,MAAA,uBrBtCE,cAAA,wBJ0qFJ,qByB1nFA,8BzBwnFA,6BACA,kCyBrnFE,QAAA,MAAA,KxBwNI,UAAA,QG5QF,cAAA,2BJmrFJ,qByB1nFA,8BzBwnFA,6BACA,kCyBrnFE,QAAA,OAAA,MxB+MI,UAAA,QG5QF,cAAA,2BqBkEJ,6BzBwnFA,6ByBtnFE,cAAA,KzB2nFF,uEACA,gFACA,+EyBhnFI,kHrBjEA,wBAAA,EACA,2BAAA,EJqrFJ,iEACA,6EACA,4EyB9mFI,+GrB1EA,wBAAA,EACA,2BAAA,EqBsFF,0IACE,YAAA,kCrB1EA,uBAAA,EACA,0BAAA,EqB6EF,4DzBsmFF,2DIprFI,uBAAA,EACA,0BAAA,EsBxBF,gBACE,QAAA,KACA,MAAA,KACA,WAAA,OzBkQE,UAAA,OyB/PF,MAAA,2BAGF,eACE,SAAA,SACA,IAAA,KACA,QAAA,EACA,QAAA,KACA,UAAA,KACA,QAAA,OAAA,MACA,WAAA,MzBqPE,UAAA,QyBlPF,MAAA,KACA,iBAAA,kBtB3BA,cAAA,wBJ+uFJ,0BACA,yB0BhtFI,sC1B8sFJ,qC0B5sFM,QAAA,MA/CF,uBAAA,mCAqDE,aAAA,kCAGE,cAAA,qBACA,iBAAA,0OACA,kBAAA,UACA,oBAAA,MAAA,wBAAA,OACA,gBAAA,sBAAA,sBAGF,6BAAA,yCACE,aAAA,kCACA,WAAA,EAAA,EAAA,EAAA,OAAA,gCAjEJ,2CAAA,+BA0EI,cAAA,qBACA,oBAAA,IAAA,wBAAA,MAAA,wBA3EJ,sBAAA,kCAkFE,aAAA,kCAGE,kDAAA,gDAAA,8DAAA,4DAEE,yBAAA,0OACA,cAAA,SACA,oBAAA,MAAA,OAAA,MAAA,CAAA,OAAA,MAAA,QACA,gBAAA,KAAA,IAAA,CAAA,sBAAA,sBAIJ,4BAAA,wCACE,aAAA,kCACA,WAAA,EAAA,EAAA,EAAA,OAAA,gCAhGJ,6BAAA,yCAwGI,MAAA,kCAxGJ,2BAAA,uCA+GE,aAAA,kCAEA,mCAAA,+CACE,iBAAA,2BAGF,iCAAA,6CACE,WAAA,EAAA,EAAA,EAAA,OAAA,gCAGF,6CAAA,yDACE,MAAA,2BAKJ,qDACE,YAAA,KAhIF,gD1B0zFJ,wDAFA,+C0BxzFI,4D1ByzFJ,oEAFA,2D0B7qFU,QAAA,EAtHR,kBACE,QAAA,KACA,MAAA,KACA,WAAA,OzBkQE,UAAA,OyB/PF,MAAA,6BAGF,iBACE,SAAA,SACA,IAAA,KACA,QAAA,EACA,QAAA,KACA,UAAA,KACA,QAAA,OAAA,MACA,WAAA,MzBqPE,UAAA,QyBlPF,MAAA,KACA,iBAAA,iBtB3BA,cAAA,wBJy0FJ,8BACA,6B0B1yFI,0C1BwyFJ,yC0BtyFM,QAAA,MA/CF,yBAAA,qCAqDE,aAAA,oCAGE,cAAA,qBACA,iBAAA,2TACA,kBAAA,UACA,oBAAA,MAAA,wBAAA,OACA,gBAAA,sBAAA,sBAGF,+BAAA,2CACE,aAAA,oCACA,WAAA,EAAA,EAAA,EAAA,OAAA,+BAjEJ,6CAAA,iCA0EI,cAAA,qBACA,oBAAA,IAAA,wBAAA,MAAA,wBA3EJ,wBAAA,oCAkFE,aAAA,oCAGE,oDAAA,kDAAA,gEAAA,8DAEE,yBAAA,2TACA,cAAA,SACA,oBAAA,MAAA,OAAA,MAAA,CAAA,OAAA,MAAA,QACA,gBAAA,KAAA,IAAA,CAAA,sBAAA,sBAIJ,8BAAA,0CACE,aAAA,oCACA,WAAA,EAAA,EAAA,EAAA,OAAA,+BAhGJ,+BAAA,2CAwGI,MAAA,kCAxGJ,6BAAA,yCA+GE,aAAA,oCAEA,qCAAA,iDACE,iBAAA,6BAGF,mCAAA,+CACE,WAAA,EAAA,EAAA,EAAA,OAAA,+BAGF,+CAAA,2DACE,MAAA,6BAKJ,uDACE,YAAA,KAhIF,kD1Bo5FJ,0DAFA,iD0Bl5FI,8D1Bm5FJ,sEAFA,6D0BrwFU,QAAA,EC9IV,KAEE,mBAAA,QACA,mBAAA,SACA,qBAAA,E1BuRI,mBAAA,K0BrRJ,qBAAA,IACA,qBAAA,IACA,eAAA,qBACA,YAAA,YACA,sBAAA,uBACA,sBAAA,YACA,uBAAA,wBACA,4BAAA,YACA,oBAAA,MAAA,EAAA,IAAA,EAAA,yBAAA,CAAA,EAAA,IAAA,IAAA,qBACA,0BAAA,KACA,0BAAA,EAAA,EAAA,EAAA,QAAA,yCAGA,QAAA,aACA,QAAA,wBAAA,wBACA,YAAA,0B1BsQI,UAAA,wB0BpQJ,YAAA,0BACA,YAAA,0BACA,MAAA,oBACA,WAAA,OACA,gBAAA,KAEA,eAAA,OACA,OAAA,QACA,oBAAA,KAAA,iBAAA,KAAA,YAAA,KACA,OAAA,2BAAA,MAAA,2BvBjBE,cAAA,4BgBfF,iBAAA,iBDYI,WAAA,MAAA,KAAA,WAAA,CAAA,iBAAA,KAAA,WAAA,CAAA,aAAA,KAAA,WAAA,CAAA,WAAA,KAAA,YAIA,uCQhBN,KRiBQ,WAAA,MQqBN,WACE,MAAA,0BAEA,iBAAA,uBACA,aAAA,iCAGF,sBAEE,MAAA,oBACA,iBAAA,iBACA,aAAA,2BAGF,mBACE,MAAA,0BPrDF,iBAAA,uBOuDE,aAAA,iCACA,QAAA,EAKE,WAAA,+BAIJ,8BACE,aAAA,iCACA,QAAA,EAKE,WAAA,+BAIJ,wBAAA,YAAA,UAAA,wBAAA,6BAKE,MAAA,2BACA,iBAAA,wBAGA,aAAA,kCAGA,sCAAA,0BAAA,wBAAA,sCAAA,2CAKI,WAAA,+BAKN,cAAA,cAAA,uBAGE,MAAA,6BACA,eAAA,KACA,iBAAA,0BAEA,aAAA,oCACA,QAAA,+BAYF,aCtGA,eAAA,KACA,YAAA,QACA,sBAAA,QACA,qBAAA,KACA,kBAAA,QACA,4BAAA,QACA,0BAAA,EAAA,CAAA,GAAA,CAAA,IACA,sBAAA,KACA,mBAAA,QACA,6BAAA,QACA,uBAAA,MAAA,EAAA,IAAA,IAAA,qBACA,wBAAA,KACA,qBAAA,QACA,+BAAA,QDyFA,eCtGA,eAAA,KACA,YAAA,QACA,sBAAA,QACA,qBAAA,KACA,kBAAA,QACA,4BAAA,QACA,0BAAA,GAAA,CAAA,GAAA,CAAA,IACA,sBAAA,KACA,mBAAA,QACA,6BAAA,QACA,uBAAA,MAAA,EAAA,IAAA,IAAA,qBACA,wBAAA,KACA,qBAAA,QACA,+BAAA,QDyFA,aCtGA,eAAA,KACA,YAAA,QACA,sBAAA,QACA,qBAAA,KACA,kBAAA,QACA,4BAAA,QACA,0BAAA,EAAA,CAAA,GAAA,CAAA,IACA,sBAAA,KACA,mBAAA,QACA,6BAAA,QACA,uBAAA,MAAA,EAAA,IAAA,IAAA,qBACA,wBAAA,KACA,qBAAA,QACA,+BAAA,QDyFA,UCtGA,eAAA,KACA,YAAA,QACA,sBAAA,QACA,qBAAA,KACA,kBAAA,QACA,4BAAA,QACA,0BAAA,EAAA,CAAA,GAAA,CAAA,IACA,sBAAA,KACA,mBAAA,QACA,6BAAA,QACA,uBAAA,MAAA,EAAA,IAAA,IAAA,qBACA,wBAAA,KACA,qBAAA,QACA,+BAAA,QDyFA,aCtGA,eAAA,KACA,YAAA,QACA,sBAAA,QACA,qBAAA,KACA,kBAAA,QACA,4BAAA,QACA,0BAAA,GAAA,CAAA,GAAA,CAAA,EACA,sBAAA,KACA,mBAAA,QACA,6BAAA,QACA,uBAAA,MAAA,EAAA,IAAA,IAAA,qBACA,wBAAA,KACA,qBAAA,QACA,+BAAA,QDyFA,YCtGA,eAAA,KACA,YAAA,QACA,sBAAA,QACA,qBAAA,KACA,kBAAA,QACA,4BAAA,QACA,0BAAA,GAAA,CAAA,EAAA,CAAA,GACA,sBAAA,KACA,mBAAA,QACA,6BAAA,QACA,uBAAA,MAAA,EAAA,IAAA,IAAA,qBACA,wBAAA,KACA,qBAAA,QACA,+BAAA,QDyFA,WCtGA,eAAA,KACA,YAAA,QACA,sBAAA,QACA,qBAAA,KACA,kBAAA,QACA,4BAAA,QACA,0BAAA,GAAA,CAAA,GAAA,CAAA,IACA,sBAAA,KACA,mBAAA,QACA,6BAAA,QACA,uBAAA,MAAA,EAAA,IAAA,IAAA,qBACA,wBAAA,KACA,qBAAA,QACA,+BAAA,QDyFA,UCtGA,eAAA,KACA,YAAA,QACA,sBAAA,QACA,qBAAA,KACA,kBAAA,QACA,4BAAA,QACA,0BAAA,EAAA,CAAA,EAAA,CAAA,GACA,sBAAA,KACA,mBAAA,QACA,6BAAA,QACA,uBAAA,MAAA,EAAA,IAAA,IAAA,qBACA,wBAAA,KACA,qBAAA,QACA,+BAAA,QDmHA,qBCvGA,eAAA,QACA,sBAAA,QACA,qBAAA,KACA,kBAAA,QACA,4BAAA,QACA,0BAAA,EAAA,CAAA,GAAA,CAAA,IACA,sBAAA,KACA,mBAAA,QACA,6BAAA,QACA,uBAAA,MAAA,EAAA,IAAA,IAAA,qBACA,wBAAA,QACA,qBAAA,YACA,+BAAA,QACA,cAAA,KD0FA,uBCvGA,eAAA,QACA,sBAAA,QACA,qBAAA,KACA,kBAAA,QACA,4BAAA,QACA,0BAAA,GAAA,CAAA,GAAA,CAAA,IACA,sBAAA,KACA,mBAAA,QACA,6BAAA,QACA,uBAAA,MAAA,EAAA,IAAA,IAAA,qBACA,wBAAA,QACA,qBAAA,YACA,+BAAA,QACA,cAAA,KD0FA,qBCvGA,eAAA,QACA,sBAAA,QACA,qBAAA,KACA,kBAAA,QACA,4BAAA,QACA,0BAAA,EAAA,CAAA,GAAA,CAAA,GACA,sBAAA,KACA,mBAAA,QACA,6BAAA,QACA,uBAAA,MAAA,EAAA,IAAA,IAAA,qBACA,wBAAA,QACA,qBAAA,YACA,+BAAA,QACA,cAAA,KD0FA,kBCvGA,eAAA,QACA,sBAAA,QACA,qBAAA,KACA,kBAAA,QACA,4BAAA,QACA,0BAAA,EAAA,CAAA,GAAA,CAAA,IACA,sBAAA,KACA,mBAAA,QACA,6BAAA,QACA,uBAAA,MAAA,EAAA,IAAA,IAAA,qBACA,wBAAA,QACA,qBAAA,YACA,+BAAA,QACA,cAAA,KD0FA,qBCvGA,eAAA,QACA,sBAAA,QACA,qBAAA,KACA,kBAAA,QACA,4BAAA,QACA,0BAAA,GAAA,CAAA,GAAA,CAAA,EACA,sBAAA,KACA,mBAAA,QACA,6BAAA,QACA,uBAAA,MAAA,EAAA,IAAA,IAAA,qBACA,wBAAA,QACA,qBAAA,YACA,+BAAA,QACA,cAAA,KD0FA,oBCvGA,eAAA,QACA,sBAAA,QACA,qBAAA,KACA,kBAAA,QACA,4BAAA,QACA,0BAAA,GAAA,CAAA,EAAA,CAAA,GACA,sBAAA,KACA,mBAAA,QACA,6BAAA,QACA,uBAAA,MAAA,EAAA,IAAA,IAAA,qBACA,wBAAA,QACA,qBAAA,YACA,+BAAA,QACA,cAAA,KD0FA,mBCvGA,eAAA,QACA,sBAAA,QACA,qBAAA,KACA,kBAAA,QACA,4BAAA,QACA,0BAAA,GAAA,CAAA,GAAA,CAAA,IACA,sBAAA,KACA,mBAAA,QACA,6BAAA,QACA,uBAAA,MAAA,EAAA,IAAA,IAAA,qBACA,wBAAA,QACA,qBAAA,YACA,+BAAA,QACA,cAAA,KD0FA,kBCvGA,eAAA,QACA,sBAAA,QACA,qBAAA,KACA,kBAAA,QACA,4BAAA,QACA,0BAAA,EAAA,CAAA,EAAA,CAAA,GACA,sBAAA,KACA,mBAAA,QACA,6BAAA,QACA,uBAAA,MAAA,EAAA,IAAA,IAAA,qBACA,wBAAA,QACA,qBAAA,YACA,+BAAA,QACA,cAAA,KDsGF,UACE,qBAAA,IACA,eAAA,qBACA,YAAA,YACA,sBAAA,YACA,qBAAA,2BACA,4BAAA,YACA,sBAAA,2BACA,6BAAA,YACA,wBAAA,QACA,+BAAA,YACA,oBAAA,EAAA,EAAA,EAAA,KACA,0BAAA,EAAA,CAAA,GAAA,CAAA,IAEA,gBAAA,UAUA,wBACE,MAAA,oBAGF,gBACE,MAAA,0BAWJ,mBAAA,QCxIE,mBAAA,OACA,mBAAA,K3B8NI,mBAAA,Q2B5NJ,uBAAA,2BDyIF,mBAAA,QC5IE,mBAAA,QACA,mBAAA,O3B8NI,mBAAA,S2B5NJ,uBAAA,2BCnEF,MVgBM,WAAA,QAAA,KAAA,OAIA,uCUpBN,MVqBQ,WAAA,MUlBN,iBACE,QAAA,EAMF,qBACE,QAAA,KAIJ,YACE,OAAA,EACA,SAAA,OVDI,WAAA,OAAA,KAAA,KAIA,uCULN,YVMQ,WAAA,MUDN,gCACE,MAAA,EACA,OAAA,KVNE,WAAA,MAAA,KAAA,KAIA,uCUAJ,gCVCM,WAAA,MnBkzGR,UAGA,iBAJA,SAEA,W8Bv0GA,Q9Bw0GA,e8Bl0GE,SAAA,SAGF,iBACE,YAAA,OCwBE,wBACE,QAAA,aACA,YAAA,OACA,eAAA,OACA,QAAA,GArCJ,WAAA,KAAA,MACA,aAAA,KAAA,MAAA,YACA,cAAA,EACA,YAAA,KAAA,MAAA,YA0DE,8BACE,YAAA,ED9CN,eAEE,qBAAA,KACA,wBAAA,MACA,wBAAA,EACA,wBAAA,OACA,qBAAA,S7BuQI,wBAAA,K6BrQJ,oBAAA,qBACA,iBAAA,kBACA,2BAAA,mCACA,4BAAA,wBACA,2BAAA,uBACA,kCAAA,uDACA,yBAAA,mCACA,+BAAA,OACA,yBAAA,qBACA,yBAAA,qBACA,+BAAA,qBACA,4BAAA,sBACA,gCAAA,KACA,6BAAA,QACA,kCAAA,yBACA,6BAAA,KACA,6BAAA,QACA,2BAAA,QACA,+BAAA,KACA,+BAAA,OAGA,SAAA,SACA,QAAA,0BACA,QAAA,KACA,UAAA,6BACA,QAAA,6BAAA,6BACA,OAAA,E7B0OI,UAAA,6B6BxOJ,MAAA,yBACA,WAAA,KACA,WAAA,KACA,iBAAA,sBACA,gBAAA,YACA,OAAA,gCAAA,MAAA,gC1BzCE,cAAA,iC0B6CF,+BACE,IAAA,KACA,KAAA,EACA,WAAA,0BAwBA,qBACE,cAAA,MAEA,qCACE,MAAA,KACA,KAAA,EAIJ,mBACE,cAAA,IAEA,mCACE,MAAA,EACA,KAAA,KnB1CJ,yBmB4BA,wBACE,cAAA,MAEA,wCACE,MAAA,KACA,KAAA,EAIJ,sBACE,cAAA,IAEA,sCACE,MAAA,EACA,KAAA,MnB1CJ,yBmB4BA,wBACE,cAAA,MAEA,wCACE,MAAA,KACA,KAAA,EAIJ,sBACE,cAAA,IAEA,sCACE,MAAA,EACA,KAAA,MnB1CJ,yBmB4BA,wBACE,cAAA,MAEA,wCACE,MAAA,KACA,KAAA,EAIJ,sBACE,cAAA,IAEA,sCACE,MAAA,EACA,KAAA,MnB1CJ,0BmB4BA,wBACE,cAAA,MAEA,wCACE,MAAA,KACA,KAAA,EAIJ,sBACE,cAAA,IAEA,sCACE,MAAA,EACA,KAAA,MnB1CJ,0BmB4BA,yBACE,cAAA,MAEA,yCACE,MAAA,KACA,KAAA,EAIJ,uBACE,cAAA,IAEA,uCACE,MAAA,EACA,KAAA,MAUN,uCACE,IAAA,KACA,OAAA,KACA,WAAA,EACA,cAAA,0BCpFA,gCACE,QAAA,aACA,YAAA,OACA,eAAA,OACA,QAAA,GA9BJ,WAAA,EACA,aAAA,KAAA,MAAA,YACA,cAAA,KAAA,MACA,YAAA,KAAA,MAAA,YAmDE,sCACE,YAAA,EDgEJ,wCACE,IAAA,EACA,MAAA,KACA,KAAA,KACA,WAAA,EACA,YAAA,0BClGA,iCACE,QAAA,aACA,YAAA,OACA,eAAA,OACA,QAAA,GAvBJ,WAAA,KAAA,MAAA,YACA,aAAA,EACA,cAAA,KAAA,MAAA,YACA,YAAA,KAAA,MA4CE,uCACE,YAAA,ED0EF,iCACE,eAAA,EAMJ,0CACE,IAAA,EACA,MAAA,KACA,KAAA,KACA,WAAA,EACA,aAAA,0BCnHA,mCACE,QAAA,aACA,YAAA,OACA,eAAA,OACA,QAAA,GAWA,mCACE,QAAA,KAGF,oCACE,QAAA,aACA,aAAA,OACA,eAAA,OACA,QAAA,GAnCN,WAAA,KAAA,MAAA,YACA,aAAA,KAAA,MACA,cAAA,KAAA,MAAA,YAsCE,yCACE,YAAA,ED2FF,oCACE,eAAA,EAON,kBACE,OAAA,EACA,OAAA,oCAAA,EACA,SAAA,OACA,WAAA,IAAA,MAAA,8BACA,QAAA,EAMF,eACE,QAAA,MACA,MAAA,KACA,QAAA,kCAAA,kCACA,MAAA,KACA,YAAA,IACA,MAAA,8BACA,WAAA,QACA,gBAAA,KACA,YAAA,OACA,iBAAA,YACA,OAAA,E1BtKE,cAAA,wC0ByKF,qBAAA,qBAEE,MAAA,oCV1LF,iBAAA,iCU+LA,sBAAA,sBAEE,MAAA,qCACA,gBAAA,KVlMF,iBAAA,kCUsMA,wBAAA,wBAEE,MAAA,uCACA,eAAA,KACA,iBAAA,YAMJ,oBACE,QAAA,MAIF,iBACE,QAAA,MACA,QAAA,oCAAA,oCACA,cAAA,E7BmEI,UAAA,Q6BjEJ,MAAA,gCACA,YAAA,OAIF,oBACE,QAAA,MACA,QAAA,kCAAA,kCACA,MAAA,8BAIF,oBAEE,oBAAA,QACA,iBAAA,QACA,2BAAA,mCACA,yBAAA,EACA,yBAAA,QACA,+BAAA,KACA,yBAAA,mCACA,4BAAA,0BACA,gCAAA,KACA,6BAAA,QACA,kCAAA,QACA,2BAAA,QEtPF,WhCkoHA,oBgChoHE,SAAA,SACA,QAAA,YACA,eAAA,OhCooHF,yBgCloHE,gBACE,SAAA,SACA,KAAA,EAAA,EAAA,KhC0oHJ,4CACA,0CAIA,gCADA,gCADA,+BADA,+BgCvoHE,mChCgoHF,iCAIA,uBADA,uBADA,sBADA,sBgC3nHI,QAAA,EAKJ,aACE,QAAA,KACA,UAAA,KACA,gBAAA,WAEA,0BACE,MAAA,KAIJ,W5BhBI,cAAA,wBJspHJ,wCgCloHE,6CAEE,YAAA,kChCqoHJ,4CADA,kDgChoHE,uD5BVE,wBAAA,EACA,2BAAA,EJgpHJ,6CgC7nHE,+BhC4nHF,iCIloHI,uBAAA,EACA,0BAAA,E4BwBJ,uBACE,cAAA,SACA,aAAA,SAEA,8BAAA,uCAAA,sCAGE,YAAA,EAGF,0CACE,aAAA,EAIJ,0CAAA,+BACE,cAAA,QACA,aAAA,QAGF,0CAAA,+BACE,cAAA,OACA,aAAA,OAoBF,oBACE,eAAA,OACA,YAAA,WACA,gBAAA,OAEA,yBhC2lHF,+BgCzlHI,MAAA,KhC6lHJ,iDgC1lHE,2CAEE,WAAA,kChC4lHJ,qDgCxlHE,gE5B1FE,2BAAA,EACA,0BAAA,EJsrHJ,sDgCxlHE,8B5B7GE,uBAAA,EACA,wBAAA,E6BxBJ,KAEE,wBAAA,KACA,wBAAA,OAEA,0BAAA,EACA,oBAAA,qBACA,0BAAA,2BACA,6BAAA,0BAGA,QAAA,KACA,UAAA,KACA,aAAA,EACA,cAAA,EACA,WAAA,KAGF,UACE,QAAA,MACA,QAAA,6BAAA,6BhCsQI,UAAA,6BgCpQJ,YAAA,+BACA,MAAA,yBACA,gBAAA,KACA,WAAA,IACA,OAAA,EdfI,WAAA,MAAA,KAAA,WAAA,CAAA,iBAAA,KAAA,WAAA,CAAA,aAAA,KAAA,YAIA,uCcGN,UdFQ,WAAA,McaN,gBAAA,gBAEE,MAAA,+BAIF,wBACE,QAAA,EACA,WAAA,EAAA,EAAA,EAAA,OAAA,qBAIF,mBAAA,mBAEE,MAAA,kCACA,eAAA,KACA,OAAA,QAQJ,UAEE,2BAAA,uBACA,2BAAA,uBACA,4BAAA,wBACA,sCAAA,uBAAA,uBAAA,uBACA,gCAAA,yBACA,6BAAA,kBACA,uCAAA,uBAAA,uBAAA,kBAGA,cAAA,gCAAA,MAAA,gCAEA,oBACE,cAAA,2CACA,OAAA,gCAAA,MAAA,Y7B7CA,uBAAA,iCACA,wBAAA,iC6B+CA,0BAAA,0BAGE,UAAA,QACA,aAAA,2CjCstHN,mCiCltHE,2BAEE,MAAA,qCACA,iBAAA,kCACA,aAAA,4CAGF,yBAEE,WAAA,2C7BjEA,uBAAA,EACA,wBAAA,E6B2EJ,WAEE,6BAAA,wBACA,iCAAA,KACA,8BAAA,QAGA,qB7B5FE,cAAA,kC6BgGF,4BjCusHF,2BiCrsHI,MAAA,sCbjHF,iBAAA,mCa2HF,eAEE,uBAAA,KACA,gCAAA,SACA,qCAAA,yBAGA,IAAA,4BAEA,yBACE,cAAA,EACA,aAAA,EACA,cAAA,qCAAA,MAAA,YAEA,+BAAA,+BAEE,oBAAA,aAIJ,gCjC2rHF,+BiCzrHI,YAAA,IACA,MAAA,0CACA,oBAAA,ajC8rHJ,oBiCprHE,oBAEE,KAAA,EAAA,EAAA,KACA,WAAA,OjCurHJ,yBiClrHE,yBAEE,WAAA,EACA,UAAA,EACA,WAAA,OAMF,8BjC+qHF,mCiC9qHI,MAAA,KAUF,uBACE,QAAA,KAEF,qBACE,QAAA,MC7LJ,QAEE,sBAAA,EACA,sBAAA,OACA,kBAAA,yCACA,wBAAA,wCACA,2BAAA,wCACA,yBAAA,sCACA,4BAAA,UACA,6BAAA,KACA,4BAAA,QACA,wBAAA,sCACA,8BAAA,sCACA,+BAAA,OACA,8BAAA,QACA,8BAAA,QACA,8BAAA,QACA,4BAAA,+OACA,iCAAA,yCACA,kCAAA,wBACA,gCAAA,QACA,+BAAA,WAAA,MAAA,YAGA,SAAA,SACA,QAAA,KACA,UAAA,KACA,YAAA,OACA,gBAAA,cACA,QAAA,2BAAA,2BAMA,mBlCk2HF,yBAGA,sBADA,sBADA,sBAGA,sBACA,uBkCt2HI,QAAA,KACA,UAAA,QACA,YAAA,OACA,gBAAA,cAoBJ,cACE,YAAA,iCACA,eAAA,iCACA,aAAA,kCjC4NI,UAAA,iCiC1NJ,MAAA,6BACA,gBAAA,KACA,YAAA,OAEA,oBAAA,oBAEE,MAAA,mCAUJ,YAEE,wBAAA,EACA,wBAAA,OAEA,0BAAA,EACA,oBAAA,uBACA,0BAAA,6BACA,6BAAA,gCAGA,QAAA,KACA,eAAA,OACA,aAAA,EACA,cAAA,EACA,WAAA,KAGE,6BAAA,2BAEE,MAAA,8BAIJ,2BACE,SAAA,OASJ,aACE,YAAA,MACA,eAAA,MACA,MAAA,uBAEA,elCi0HF,qBADA,qBkC7zHI,MAAA,8BAaJ,iBACE,WAAA,KACA,UAAA,EAGA,YAAA,OAIF,gBACE,QAAA,mCAAA,mCjCyII,UAAA,mCiCvIJ,YAAA,EACA,MAAA,uBACA,iBAAA,YACA,OAAA,uBAAA,MAAA,sC9BxIE,cAAA,uCeHE,WAAA,oCAIA,uCeiIN,gBfhIQ,WAAA,Me0IN,sBACE,gBAAA,KAGF,sBACE,gBAAA,KACA,QAAA,EACA,WAAA,EAAA,EAAA,EAAA,qCAMJ,qBACE,QAAA,aACA,MAAA,MACA,OAAA,MACA,eAAA,OACA,iBAAA,iCACA,kBAAA,UACA,oBAAA,OACA,gBAAA,KAGF,mBACE,WAAA,6BACA,WAAA,KvB1HE,yBuBsIA,kBAEI,UAAA,OACA,gBAAA,WAEA,8BACE,eAAA,IAEA,6CACE,SAAA,SAGF,wCACE,cAAA,oCACA,aAAA,oCAIJ,qCACE,SAAA,QAGF,mCACE,QAAA,eACA,WAAA,KAGF,kCACE,QAAA,KAGF,6BAEE,SAAA,OACA,QAAA,KACA,UAAA,EACA,MAAA,eACA,OAAA,eACA,WAAA,kBACA,iBAAA,sBACA,OAAA,YACA,UAAA,ef9NJ,WAAA,KemOI,+CACE,QAAA,KAGF,6CACE,QAAA,KACA,UAAA,EACA,QAAA,EACA,WAAA,SvB5LR,yBuBsIA,kBAEI,UAAA,OACA,gBAAA,WAEA,8BACE,eAAA,IAEA,6CACE,SAAA,SAGF,wCACE,cAAA,oCACA,aAAA,oCAIJ,qCACE,SAAA,QAGF,mCACE,QAAA,eACA,WAAA,KAGF,kCACE,QAAA,KAGF,6BAEE,SAAA,OACA,QAAA,KACA,UAAA,EACA,MAAA,eACA,OAAA,eACA,WAAA,kBACA,iBAAA,sBACA,OAAA,YACA,UAAA,ef9NJ,WAAA,KemOI,+CACE,QAAA,KAGF,6CACE,QAAA,KACA,UAAA,EACA,QAAA,EACA,WAAA,SvB5LR,yBuBsIA,kBAEI,UAAA,OACA,gBAAA,WAEA,8BACE,eAAA,IAEA,6CACE,SAAA,SAGF,wCACE,cAAA,oCACA,aAAA,oCAIJ,qCACE,SAAA,QAGF,mCACE,QAAA,eACA,WAAA,KAGF,kCACE,QAAA,KAGF,6BAEE,SAAA,OACA,QAAA,KACA,UAAA,EACA,MAAA,eACA,OAAA,eACA,WAAA,kBACA,iBAAA,sBACA,OAAA,YACA,UAAA,ef9NJ,WAAA,KemOI,+CACE,QAAA,KAGF,6CACE,QAAA,KACA,UAAA,EACA,QAAA,EACA,WAAA,SvB5LR,0BuBsIA,kBAEI,UAAA,OACA,gBAAA,WAEA,8BACE,eAAA,IAEA,6CACE,SAAA,SAGF,wCACE,cAAA,oCACA,aAAA,oCAIJ,qCACE,SAAA,QAGF,mCACE,QAAA,eACA,WAAA,KAGF,kCACE,QAAA,KAGF,6BAEE,SAAA,OACA,QAAA,KACA,UAAA,EACA,MAAA,eACA,OAAA,eACA,WAAA,kBACA,iBAAA,sBACA,OAAA,YACA,UAAA,ef9NJ,WAAA,KemOI,+CACE,QAAA,KAGF,6CACE,QAAA,KACA,UAAA,EACA,QAAA,EACA,WAAA,SvB5LR,0BuBsIA,mBAEI,UAAA,OACA,gBAAA,WAEA,+BACE,eAAA,IAEA,8CACE,SAAA,SAGF,yCACE,cAAA,oCACA,aAAA,oCAIJ,sCACE,SAAA,QAGF,oCACE,QAAA,eACA,WAAA,KAGF,mCACE,QAAA,KAGF,8BAEE,SAAA,OACA,QAAA,KACA,UAAA,EACA,MAAA,eACA,OAAA,eACA,WAAA,kBACA,iBAAA,sBACA,OAAA,YACA,UAAA,ef9NJ,WAAA,KemOI,gDACE,QAAA,KAGF,8CACE,QAAA,KACA,UAAA,EACA,QAAA,EACA,WAAA,SAtDR,eAEI,UAAA,OACA,gBAAA,WAEA,2BACE,eAAA,IAEA,0CACE,SAAA,SAGF,qCACE,cAAA,oCACA,aAAA,oCAIJ,kCACE,SAAA,QAGF,gCACE,QAAA,eACA,WAAA,KAGF,+BACE,QAAA,KAGF,0BAEE,SAAA,OACA,QAAA,KACA,UAAA,EACA,MAAA,eACA,OAAA,eACA,WAAA,kBACA,iBAAA,sBACA,OAAA,YACA,UAAA,ef9NJ,WAAA,KemOI,4CACE,QAAA,KAGF,0CACE,QAAA,KACA,UAAA,EACA,QAAA,EACA,WAAA,QAiBZ,alC6/HA,4BkC1/HE,kBAAA,0BACA,wBAAA,0BACA,2BAAA,0BACA,yBAAA,KACA,wBAAA,KACA,8BAAA,KACA,iCAAA,yBACA,4BAAA,kPAME,0CACE,4BAAA,kPCzRN,MAEE,mBAAA,KACA,mBAAA,KACA,yBAAA,OACA,sBAAA,EACA,yBAAA,EACA,uBAAA,uBACA,uBAAA,mCACA,wBAAA,wBACA,qBAAA,EACA,8BAAA,yDACA,wBAAA,OACA,wBAAA,KACA,iBAAA,qCACA,oBAAA,EACA,iBAAA,EACA,gBAAA,EACA,aAAA,kBACA,8BAAA,KACA,uBAAA,QAGA,SAAA,SACA,QAAA,KACA,eAAA,OACA,UAAA,EACA,OAAA,sBACA,MAAA,qBACA,UAAA,WACA,iBAAA,kBACA,gBAAA,WACA,OAAA,4BAAA,MAAA,4B/BjBE,cAAA,6B+BqBF,SACE,aAAA,EACA,YAAA,EAGF,kBACE,WAAA,QACA,cAAA,QAEA,8BACE,iBAAA,E/BtBF,uBAAA,mCACA,wBAAA,mC+ByBA,6BACE,oBAAA,E/BbF,2BAAA,mCACA,0BAAA,mC+BmBF,+BnC6wIF,+BmC3wII,WAAA,EAIJ,WAGE,KAAA,EAAA,EAAA,KACA,QAAA,wBAAA,wBACA,MAAA,qBAGF,YACE,cAAA,8BACA,MAAA,2BAGF,eACE,WAAA,0CACA,cAAA,EACA,MAAA,8BAGF,sBACE,cAAA,EAQA,sBACE,YAAA,wBAQJ,aACE,QAAA,6BAAA,6BACA,cAAA,EACA,MAAA,yBACA,iBAAA,sBACA,cAAA,4BAAA,MAAA,4BAEA,yB/B7FE,cAAA,mCAAA,mCAAA,EAAA,E+BkGJ,aACE,QAAA,6BAAA,6BACA,MAAA,yBACA,iBAAA,sBACA,WAAA,4BAAA,MAAA,4BAEA,wB/BxGE,cAAA,EAAA,EAAA,mCAAA,mC+BkHJ,kBACE,aAAA,yCACA,cAAA,wCACA,YAAA,yCACA,cAAA,EAEA,mCACE,iBAAA,kBACA,oBAAA,kBAIJ,mBACE,aAAA,yCACA,YAAA,yCAIF,kBACE,SAAA,SACA,IAAA,EACA,MAAA,EACA,OAAA,EACA,KAAA,EACA,QAAA,mC/B1IE,cAAA,mC+B8IJ,UnCwvIA,iBADA,cmCpvIE,MAAA,KAGF,UnCuvIA,cIl4II,uBAAA,mCACA,wBAAA,mC+B+IJ,UnCwvIA,iBI13II,2BAAA,mCACA,0BAAA,mC+B8IF,kBACE,cAAA,4BxB3HA,yBwBuHJ,YAQI,QAAA,KACA,UAAA,IAAA,KAGA,kBAEE,KAAA,EAAA,EAAA,GACA,cAAA,EAEA,wBACE,YAAA,EACA,YAAA,EAKA,mC/B3KJ,wBAAA,EACA,2BAAA,EJ05IF,gDmC7uIQ,iDAGE,wBAAA,EnC8uIV,gDmC5uIQ,oDAGE,2BAAA,EAIJ,oC/B5KJ,uBAAA,EACA,0BAAA,EJw5IF,iDmC1uIQ,kDAGE,uBAAA,EnC2uIV,iDmCzuIQ,qDAGE,0BAAA,GCpOZ,WAEE,qBAAA,qBACA,kBAAA,kBACA,0BAAA,MAAA,MAAA,WAAA,CAAA,iBAAA,MAAA,WAAA,CAAA,aAAA,MAAA,WAAA,CAAA,WAAA,MAAA,WAAA,CAAA,cAAA,MAAA,KACA,4BAAA,uBACA,4BAAA,uBACA,6BAAA,wBACA,mCAAA,yDACA,6BAAA,QACA,6BAAA,KACA,yBAAA,qBACA,sBAAA,uBACA,wBAAA,gRACA,8BAAA,QACA,kCAAA,gBACA,mCAAA,UAAA,KAAA,YACA,+BAAA,gRACA,sCAAA,QACA,oCAAA,EAAA,EAAA,EAAA,QAAA,yBACA,8BAAA,QACA,8BAAA,KACA,4BAAA,gCACA,yBAAA,4BAIF,kBACE,SAAA,SACA,QAAA,KACA,YAAA,OACA,MAAA,KACA,QAAA,kCAAA,kCnC2PI,UAAA,KmCzPJ,MAAA,8BACA,WAAA,KACA,iBAAA,2BACA,OAAA,EhCtBE,cAAA,EgCwBF,gBAAA,KjB3BI,WAAA,+BAIA,uCiBWN,kBjBVQ,WAAA,MiByBN,kCACE,MAAA,iCACA,iBAAA,8BACA,WAAA,MAAA,EAAA,4CAAA,EAAA,iCAEA,yCACE,iBAAA,oCACA,UAAA,uCAKJ,yBACE,YAAA,EACA,MAAA,mCACA,OAAA,mCACA,YAAA,KACA,QAAA,GACA,iBAAA,6BACA,kBAAA,UACA,gBAAA,mCjBlDE,WAAA,wCAIA,uCiBsCJ,yBjBrCM,WAAA,MiBiDN,wBACE,QAAA,EAGF,wBACE,QAAA,EACA,aAAA,2CACA,QAAA,EACA,WAAA,yCAIJ,kBACE,cAAA,EAGF,gBACE,MAAA,0BACA,iBAAA,uBACA,OAAA,iCAAA,MAAA,iCAEA,8BhC/DE,uBAAA,kCACA,wBAAA,kCgCiEA,gDhClEA,uBAAA,wCACA,wBAAA,wCgCsEF,oCACE,WAAA,EAIF,6BhC9DE,2BAAA,kCACA,0BAAA,kCgCiEE,yDhClEF,2BAAA,wCACA,0BAAA,wCgCsEA,iDhCvEA,2BAAA,kCACA,0BAAA,kCgC4EJ,gBACE,QAAA,mCAAA,mCASA,qCACE,aAAA,EAGF,iCACE,aAAA,EACA,YAAA,EhCpHA,cAAA,EgCuHA,6CAAgB,WAAA,EAChB,4CAAe,cAAA,EAGb,mDAAA,6DhC3HF,cAAA,EgCqIA,8CACE,wBAAA,gRACA,+BAAA,gRC1JN,YAEE,0BAAA,EACA,0BAAA,EACA,8BAAA,KAEA,mBAAA,EACA,8BAAA,EACA,8BAAA,0BACA,+BAAA,OACA,kCAAA,0BAGA,QAAA,KACA,UAAA,KACA,QAAA,+BAAA,+BACA,cAAA,mCpC+QI,UAAA,+BoC7QJ,WAAA,KACA,iBAAA,wBjCAE,cAAA,mCiCMF,kCACE,aAAA,oCAEA,0CACE,MAAA,KACA,cAAA,oCACA,MAAA,mCACA,QAAA,kCAIJ,wBACE,MAAA,uCCrCJ,YAEE,0BAAA,QACA,0BAAA,SrC4RI,0BAAA,KqC1RJ,sBAAA,qBACA,mBAAA,kBACA,6BAAA,uBACA,6BAAA,uBACA,8BAAA,wBACA,4BAAA,2BACA,yBAAA,sBACA,mCAAA,uBACA,4BAAA,2BACA,yBAAA,uBACA,iCAAA,EAAA,EAAA,EAAA,QAAA,yBACA,6BAAA,KACA,0BAAA,QACA,oCAAA,QACA,+BAAA,0BACA,4BAAA,uBACA,sCAAA,uBAGA,QAAA,KhCpBA,aAAA,EACA,WAAA,KgCuBF,WACE,SAAA,SACA,QAAA,MACA,QAAA,+BAAA,+BrCgQI,UAAA,+BqC9PJ,MAAA,2BACA,gBAAA,KACA,iBAAA,wBACA,OAAA,kCAAA,MAAA,kCnBpBI,WAAA,MAAA,KAAA,WAAA,CAAA,iBAAA,KAAA,WAAA,CAAA,aAAA,KAAA,WAAA,CAAA,WAAA,KAAA,YAIA,uCmBQN,WnBPQ,WAAA,MmBkBN,iBACE,QAAA,EACA,MAAA,iCAEA,iBAAA,8BACA,aAAA,wCAGF,iBACE,QAAA,EACA,MAAA,iCACA,iBAAA,8BACA,QAAA,EACA,WAAA,sCAGF,mBAAA,kBAEE,QAAA,EACA,MAAA,kClBtDF,iBAAA,+BkBwDE,aAAA,yCAGF,qBAAA,oBAEE,MAAA,oCACA,eAAA,KACA,iBAAA,iCACA,aAAA,2CAKF,wCACE,YAAA,kCAKE,kClC9BF,uBAAA,mCACA,0BAAA,mCkCmCE,iClClDF,wBAAA,mCACA,2BAAA,mCkCkEJ,eClGE,0BAAA,OACA,0BAAA,QtC0RI,0BAAA,QsCxRJ,8BAAA,2BDmGF,eCtGE,0BAAA,OACA,0BAAA,QtC0RI,0BAAA,SsCxRJ,8BAAA,2BCFF,OAEE,qBAAA,OACA,qBAAA,OvCuRI,qBAAA,OuCrRJ,uBAAA,IACA,iBAAA,KACA,yBAAA,wBAGA,QAAA,aACA,QAAA,0BAAA,0BvC+QI,UAAA,0BuC7QJ,YAAA,4BACA,YAAA,EACA,MAAA,sBACA,WAAA,OACA,YAAA,OACA,eAAA,SpCJE,cAAA,8BoCSF,aACE,QAAA,KAKJ,YACE,SAAA,SACA,IAAA,KChCF,OAEE,cAAA,YACA,qBAAA,KACA,qBAAA,KACA,yBAAA,KACA,iBAAA,QACA,wBAAA,YACA,kBAAA,uBAAA,MAAA,6BACA,yBAAA,wBACA,sBAAA,QAGA,SAAA,SACA,QAAA,0BAAA,0BACA,cAAA,8BACA,MAAA,sBACA,iBAAA,mBACA,OAAA,uBrCHE,cAAA,8BqCQJ,eAEE,MAAA,QAIF,YACE,YAAA,IACA,MAAA,2BAQF,mBACE,cAAA,KAGA,8BACE,SAAA,SACA,IAAA,EACA,MAAA,EACA,QAAA,EACA,QAAA,QAAA,KAQF,eACE,iBAAA,gCACA,cAAA,4BACA,wBAAA,gCACA,sBAAA,gCAJF,iBACE,iBAAA,kCACA,cAAA,8BACA,wBAAA,kCACA,sBAAA,kCAJF,eACE,iBAAA,gCACA,cAAA,4BACA,wBAAA,gCACA,sBAAA,gCAJF,YACE,iBAAA,6BACA,cAAA,yBACA,wBAAA,6BACA,sBAAA,6BAJF,eACE,iBAAA,gCACA,cAAA,4BACA,wBAAA,gCACA,sBAAA,gCAJF,cACE,iBAAA,+BACA,cAAA,2BACA,wBAAA,+BACA,sBAAA,+BAJF,aACE,iBAAA,8BACA,cAAA,0BACA,wBAAA,8BACA,sBAAA,8BAJF,YACE,iBAAA,6BACA,cAAA,yBACA,wBAAA,6BACA,sBAAA,6BC5DF,gCACE,GAAK,sBAAA,MAKT,U1Cm1JA,kB0Ch1JE,qBAAA,KzCkRI,wBAAA,QyChRJ,iBAAA,uBACA,4BAAA,wBACA,yBAAA,2BACA,wBAAA,KACA,qBAAA,QACA,6BAAA,MAAA,KAAA,KAGA,QAAA,KACA,OAAA,0BACA,SAAA,OzCsQI,UAAA,6ByCpQJ,iBAAA,sBtCRE,cAAA,iCsCaJ,cACE,QAAA,KACA,eAAA,OACA,gBAAA,OACA,SAAA,OACA,MAAA,6BACA,WAAA,OACA,YAAA,OACA,iBAAA,0BvBxBI,WAAA,kCAIA,uCuBYN,cvBXQ,WAAA,MuBuBR,sBtBAE,iBAAA,iKsBEA,gBAAA,0BAAA,0BAGF,4BACE,SAAA,QAGF,0CACE,MAAA,KAIA,uBACE,UAAA,GAAA,OAAA,SAAA,qBAGE,uCAJJ,uBAKM,UAAA,MC3DR,YAEE,sBAAA,qBACA,mBAAA,kBACA,6BAAA,uBACA,6BAAA,uBACA,8BAAA,wBACA,+BAAA,KACA,+BAAA,OACA,6BAAA,0BACA,mCAAA,yBACA,gCAAA,sBACA,oCAAA,qBACA,iCAAA,uBACA,+BAAA,0BACA,4BAAA,kBACA,6BAAA,KACA,0BAAA,QACA,oCAAA,QAGA,QAAA,KACA,eAAA,OAGA,aAAA,EACA,cAAA,EvCXE,cAAA,mCuCeJ,qBACE,gBAAA,KACA,cAAA,QAEA,8CAEE,QAAA,uBAAA,KACA,kBAAA,QASJ,wBACE,MAAA,KACA,MAAA,kCACA,WAAA,QAGA,8BAAA,8BAEE,QAAA,EACA,MAAA,wCACA,gBAAA,KACA,iBAAA,qCAGF,+BACE,MAAA,yCACA,iBAAA,sCAQJ,iBACE,SAAA,SACA,QAAA,MACA,QAAA,oCAAA,oCACA,MAAA,2BACA,gBAAA,KACA,iBAAA,wBACA,OAAA,kCAAA,MAAA,kCAEA,6BvCvDE,uBAAA,QACA,wBAAA,QuC0DF,4BvC7CE,2BAAA,QACA,0BAAA,QuCgDF,0BAAA,0BAEE,MAAA,oCACA,eAAA,KACA,iBAAA,iCAIF,wBACE,QAAA,EACA,MAAA,kCACA,iBAAA,+BACA,aAAA,yCAIF,kCACE,iBAAA,EAEA,yCACE,WAAA,6CACA,iBAAA,kCAaF,uBACE,eAAA,IAGE,qEvCvDJ,0BAAA,mCAZA,wBAAA,EuCwEI,qEvCxEJ,wBAAA,mCAYA,0BAAA,EuCiEI,+CACE,WAAA,EAGF,yDACE,iBAAA,kCACA,kBAAA,EAEA,gEACE,YAAA,6CACA,kBAAA,kChCtFR,yBgC8DA,0BACE,eAAA,IAGE,wEvCvDJ,0BAAA,mCAZA,wBAAA,EuCwEI,wEvCxEJ,wBAAA,mCAYA,0BAAA,EuCiEI,kDACE,WAAA,EAGF,4DACE,iBAAA,kCACA,kBAAA,EAEA,mEACE,YAAA,6CACA,kBAAA,mChCtFR,yBgC8DA,0BACE,eAAA,IAGE,wEvCvDJ,0BAAA,mCAZA,wBAAA,EuCwEI,wEvCxEJ,wBAAA,mCAYA,0BAAA,EuCiEI,kDACE,WAAA,EAGF,4DACE,iBAAA,kCACA,kBAAA,EAEA,mEACE,YAAA,6CACA,kBAAA,mChCtFR,yBgC8DA,0BACE,eAAA,IAGE,wEvCvDJ,0BAAA,mCAZA,wBAAA,EuCwEI,wEvCxEJ,wBAAA,mCAYA,0BAAA,EuCiEI,kDACE,WAAA,EAGF,4DACE,iBAAA,kCACA,kBAAA,EAEA,mEACE,YAAA,6CACA,kBAAA,mChCtFR,0BgC8DA,0BACE,eAAA,IAGE,wEvCvDJ,0BAAA,mCAZA,wBAAA,EuCwEI,wEvCxEJ,wBAAA,mCAYA,0BAAA,EuCiEI,kDACE,WAAA,EAGF,4DACE,iBAAA,kCACA,kBAAA,EAEA,mEACE,YAAA,6CACA,kBAAA,mChCtFR,0BgC8DA,2BACE,eAAA,IAGE,yEvCvDJ,0BAAA,mCAZA,wBAAA,EuCwEI,yEvCxEJ,wBAAA,mCAYA,0BAAA,EuCiEI,mDACE,WAAA,EAGF,6DACE,iBAAA,kCACA,kBAAA,EAEA,oEACE,YAAA,6CACA,kBAAA,mCAcZ,kBvChJI,cAAA,EuCmJF,mCACE,aAAA,EAAA,EAAA,kCAEA,8CACE,oBAAA,EAaJ,yBACE,sBAAA,gCACA,mBAAA,4BACA,6BAAA,gCACA,mCAAA,yBACA,gCAAA,gCACA,oCAAA,yBACA,iCAAA,gCACA,6BAAA,4BACA,0BAAA,gCACA,oCAAA,gCAVF,2BACE,sBAAA,kCACA,mBAAA,8BACA,6BAAA,kCACA,mCAAA,yBACA,gCAAA,kCACA,oCAAA,yBACA,iCAAA,kCACA,6BAAA,8BACA,0BAAA,kCACA,oCAAA,kCAVF,yBACE,sBAAA,gCACA,mBAAA,4BACA,6BAAA,gCACA,mCAAA,yBACA,gCAAA,gCACA,oCAAA,yBACA,iCAAA,gCACA,6BAAA,4BACA,0BAAA,gCACA,oCAAA,gCAVF,sBACE,sBAAA,6BACA,mBAAA,yBACA,6BAAA,6BACA,mCAAA,yBACA,gCAAA,6BACA,oCAAA,yBACA,iCAAA,6BACA,6BAAA,yBACA,0BAAA,6BACA,oCAAA,6BAVF,yBACE,sBAAA,gCACA,mBAAA,4BACA,6BAAA,gCACA,mCAAA,yBACA,gCAAA,gCACA,oCAAA,yBACA,iCAAA,gCACA,6BAAA,4BACA,0BAAA,gCACA,oCAAA,gCAVF,wBACE,sBAAA,+BACA,mBAAA,2BACA,6BAAA,+BACA,mCAAA,yBACA,gCAAA,+BACA,oCAAA,yBACA,iCAAA,+BACA,6BAAA,2BACA,0BAAA,+BACA,oCAAA,+BAVF,uBACE,sBAAA,8BACA,mBAAA,0BACA,6BAAA,8BACA,mCAAA,yBACA,gCAAA,8BACA,oCAAA,yBACA,iCAAA,8BACA,6BAAA,0BACA,0BAAA,8BACA,oCAAA,8BAVF,sBACE,sBAAA,6BACA,mBAAA,yBACA,6BAAA,6BACA,mCAAA,yBACA,gCAAA,6BACA,oCAAA,yBACA,iCAAA,6BACA,6BAAA,yBACA,0BAAA,6BACA,oCAAA,6BC5LJ,WAEE,qBAAA,KACA,kBAAA,kUACA,uBAAA,IACA,6BAAA,KACA,4BAAA,EAAA,EAAA,EAAA,QAAA,yBACA,6BAAA,EACA,gCAAA,KACA,4BAAA,UAAA,gBAAA,iBAGA,WAAA,YACA,MAAA,IACA,OAAA,IACA,QAAA,MAAA,MACA,MAAA,0BACA,WAAA,YAAA,uBAAA,MAAA,CAAA,IAAA,KAAA,UACA,OAAA,ExCJE,cAAA,QwCMF,QAAA,4BAGA,iBACE,MAAA,0BACA,gBAAA,KACA,QAAA,kCAGF,iBACE,QAAA,EACA,WAAA,iCACA,QAAA,kCAGF,oBAAA,oBAEE,eAAA,KACA,oBAAA,KAAA,iBAAA,KAAA,YAAA,KACA,QAAA,qCAQJ,iBAHE,OAAA,iCASE,gCATF,OAAA,iCCjDF,OAEE,kBAAA,KACA,qBAAA,QACA,qBAAA,OACA,mBAAA,OACA,qBAAA,M5CyRI,qBAAA,S4CvRJ,iBAAA,EACA,cAAA,kCACA,wBAAA,uBACA,wBAAA,mCACA,yBAAA,wBACA,sBAAA,qBACA,wBAAA,0BACA,qBAAA,kCACA,+BAAA,mCAGA,MAAA,0BACA,UAAA,K5C2QI,UAAA,0B4CzQJ,MAAA,sBACA,eAAA,KACA,iBAAA,mBACA,gBAAA,YACA,OAAA,6BAAA,MAAA,6BACA,WAAA,2BzCRE,cAAA,8ByCWF,eACE,QAAA,EAGF,kBACE,QAAA,KAIJ,iBACE,kBAAA,KAEA,SAAA,SACA,QAAA,uBACA,MAAA,oBAAA,MAAA,iBAAA,MAAA,YACA,UAAA,KACA,eAAA,KAEA,mCACE,cAAA,wBAIJ,cACE,QAAA,KACA,YAAA,OACA,QAAA,0BAAA,0BACA,MAAA,6BACA,iBAAA,0BACA,gBAAA,YACA,cAAA,6BAAA,MAAA,oCzChCE,uBAAA,mEACA,wBAAA,mEyCkCF,yBACE,aAAA,sCACA,YAAA,0BAIJ,YACE,QAAA,0BACA,UAAA,WC9DF,OAEE,kBAAA,KACA,iBAAA,MACA,mBAAA,KACA,kBAAA,OACA,iBAAA,EACA,cAAA,kBACA,wBAAA,mCACA,wBAAA,uBACA,yBAAA,2BACA,sBAAA,wBACA,+BAAA,4DACA,4BAAA,KACA,4BAAA,KACA,0BAAA,KAAA,KACA,+BAAA,uBACA,+BAAA,uBACA,6BAAA,IACA,sBAAA,OACA,qBAAA,EACA,+BAAA,uBACA,+BAAA,uBAGA,SAAA,MACA,IAAA,EACA,KAAA,EACA,QAAA,uBACA,QAAA,KACA,MAAA,KACA,OAAA,KACA,WAAA,OACA,WAAA,KAGA,QAAA,EAOF,cACE,SAAA,SACA,MAAA,KACA,OAAA,uBAEA,eAAA,KAGA,0B3B5CI,WAAA,UAAA,IAAA,S2B8CF,UAAA,mB3B1CE,uC2BwCJ,0B3BvCM,WAAA,M2B2CN,0BACE,UAAA,KAIF,kCACE,UAAA,YAIJ,yBACE,OAAA,wCAEA,wCACE,WAAA,KACA,SAAA,OAGF,qCACE,WAAA,KAIJ,uBACE,QAAA,KACA,YAAA,OACA,WAAA,wCAIF,eACE,SAAA,SACA,QAAA,KACA,eAAA,OACA,MAAA,KAEA,MAAA,sBACA,eAAA,KACA,iBAAA,mBACA,gBAAA,YACA,OAAA,6BAAA,MAAA,6B1CrFE,cAAA,8B0CyFF,QAAA,EAIF,gBAEE,qBAAA,KACA,iBAAA,KACA,sBAAA,IClHA,SAAA,MACA,IAAA,EACA,KAAA,EACA,QAAA,0BACA,MAAA,MACA,OAAA,MACA,iBAAA,sBAGA,qBAAS,QAAA,EACT,qBAAS,QAAA,2BDgHX,cACE,QAAA,KACA,YAAA,EACA,YAAA,OACA,gBAAA,cACA,QAAA,+BACA,cAAA,oCAAA,MAAA,oC1CtGE,uBAAA,oCACA,wBAAA,oC0CwGF,yBACE,QAAA,4CAAA,4CACA,OAAA,6CAAA,6CAAA,6CAAA,KAKJ,aACE,cAAA,EACA,YAAA,kCAKF,YACE,SAAA,SAGA,KAAA,EAAA,EAAA,KACA,QAAA,wBAIF,cACE,QAAA,KACA,YAAA,EACA,UAAA,KACA,YAAA,OACA,gBAAA,SACA,QAAA,gEACA,iBAAA,0BACA,WAAA,oCAAA,MAAA,oC1C1HE,2BAAA,oCACA,0BAAA,oC0C+HF,gBACE,OAAA,sCnC5GA,yBmCkHF,OACE,kBAAA,QACA,sBAAA,qBAIF,cACE,UAAA,sBACA,aAAA,KACA,YAAA,KAGF,UACE,iBAAA,OnC/HA,yBmCoIF,U9C+zKA,U8C7zKE,iBAAA,OnCtIA,0BmC2IF,UACE,iBAAA,QAUA,kBACE,MAAA,MACA,UAAA,KACA,OAAA,KACA,OAAA,EAEA,iCACE,OAAA,KACA,OAAA,E1C1MJ,cAAA,EJogLJ,gC8CtzKM,gC1C9MF,cAAA,E0CmNE,8BACE,WAAA,KnC3JJ,4BmCyIA,0BACE,MAAA,MACA,UAAA,KACA,OAAA,KACA,OAAA,EAEA,yCACE,OAAA,KACA,OAAA,E1C1MJ,cAAA,EJwhLF,wC8C10KI,wC1C9MF,cAAA,E0CmNE,sCACE,WAAA,MnC3JJ,4BmCyIA,0BACE,MAAA,MACA,UAAA,KACA,OAAA,KACA,OAAA,EAEA,yCACE,OAAA,KACA,OAAA,E1C1MJ,cAAA,EJ4iLF,wC8C91KI,wC1C9MF,cAAA,E0CmNE,sCACE,WAAA,MnC3JJ,4BmCyIA,0BACE,MAAA,MACA,UAAA,KACA,OAAA,KACA,OAAA,EAEA,yCACE,OAAA,KACA,OAAA,E1C1MJ,cAAA,EJgkLF,wC8Cl3KI,wC1C9MF,cAAA,E0CmNE,sCACE,WAAA,MnC3JJ,6BmCyIA,0BACE,MAAA,MACA,UAAA,KACA,OAAA,KACA,OAAA,EAEA,yCACE,OAAA,KACA,OAAA,E1C1MJ,cAAA,EJolLF,wC8Ct4KI,wC1C9MF,cAAA,E0CmNE,sCACE,WAAA,MnC3JJ,6BmCyIA,2BACE,MAAA,MACA,UAAA,KACA,OAAA,KACA,OAAA,EAEA,0CACE,OAAA,KACA,OAAA,E1C1MJ,cAAA,EJwmLF,yC8C15KI,yC1C9MF,cAAA,E0CmNE,uCACE,WAAA,MEtOR,SAEE,oBAAA,KACA,uBAAA,MACA,uBAAA,OACA,uBAAA,QACA,oBAAA,E/CwRI,uBAAA,S+CtRJ,mBAAA,kBACA,gBAAA,yBACA,2BAAA,wBACA,qBAAA,IACA,yBAAA,OACA,0BAAA,OAGA,QAAA,yBACA,QAAA,MACA,OAAA,yBClBA,YAAA,0BAEA,WAAA,OACA,YAAA,IACA,YAAA,IACA,WAAA,KACA,WAAA,MACA,gBAAA,KACA,YAAA,KACA,eAAA,KACA,eAAA,OACA,WAAA,OACA,YAAA,OACA,aAAA,OACA,WAAA,KhDgRI,UAAA,4B+CrQJ,UAAA,WACA,QAAA,EAEA,cAAS,QAAA,0BAET,wBACE,QAAA,MACA,MAAA,8BACA,OAAA,+BAEA,gCACE,SAAA,SACA,QAAA,GACA,aAAA,YACA,aAAA,MAKN,4DAAA,+BACE,OAAA,0CAEA,oEAAA,uCACE,IAAA,KACA,aAAA,+BAAA,yCAAA,EACA,iBAAA,qBAKJ,8DAAA,+BACE,KAAA,0CACA,MAAA,+BACA,OAAA,8BAEA,sEAAA,uCACE,MAAA,KACA,aAAA,yCAAA,+BAAA,yCAAA,EACA,mBAAA,qBAMJ,+DAAA,kCACE,IAAA,0CAEA,uEAAA,0CACE,OAAA,KACA,aAAA,EAAA,yCAAA,+BACA,oBAAA,qBAKJ,6DAAA,iCACE,MAAA,0CACA,MAAA,+BACA,OAAA,8BAEA,qEAAA,yCACE,KAAA,KACA,aAAA,yCAAA,EAAA,yCAAA,+BACA,kBAAA,qBAsBJ,eACE,UAAA,4BACA,QAAA,4BAAA,4BACA,MAAA,wBACA,WAAA,OACA,iBAAA,qB5CjGE,cAAA,gC8CnBJ,SAEE,oBAAA,KACA,uBAAA,MjD4RI,uBAAA,SiD1RJ,gBAAA,kBACA,0BAAA,uBACA,0BAAA,mCACA,2BAAA,2BACA,iCAAA,0DACA,wBAAA,qBACA,8BAAA,KACA,8BAAA,OjDmRI,8BAAA,KiDjRJ,0BAAA,QACA,uBAAA,uBACA,4BAAA,KACA,4BAAA,KACA,wBAAA,qBACA,yBAAA,KACA,0BAAA,OACA,0BAAA,+BAGA,QAAA,yBACA,QAAA,MACA,UAAA,4BDzBA,YAAA,0BAEA,WAAA,OACA,YAAA,IACA,YAAA,IACA,WAAA,KACA,WAAA,MACA,gBAAA,KACA,YAAA,KACA,eAAA,KACA,eAAA,OACA,WAAA,OACA,YAAA,OACA,aAAA,OACA,WAAA,KhDgRI,UAAA,4BiD/PJ,UAAA,WACA,iBAAA,qBACA,gBAAA,YACA,OAAA,+BAAA,MAAA,+B9ChBE,cAAA,gC8CoBF,wBACE,QAAA,MACA,MAAA,8BACA,OAAA,+BAEA,+BAAA,gCAEE,SAAA,SACA,QAAA,MACA,QAAA,GACA,aAAA,YACA,aAAA,MACA,aAAA,EAMJ,4DAAA,+BACE,OAAA,6EAEA,mEAAA,oEAAA,sCAAA,uCAEE,aAAA,+BAAA,yCAAA,EAGF,oEAAA,uCACE,OAAA,EACA,iBAAA,+BAGF,mEAAA,sCACE,OAAA,+BACA,iBAAA,qBAOJ,8DAAA,+BACE,KAAA,6EACA,MAAA,+BACA,OAAA,8BAEA,qEAAA,sEAAA,sCAAA,uCAEE,aAAA,yCAAA,+BAAA,yCAAA,EAGF,sEAAA,uCACE,KAAA,EACA,mBAAA,+BAGF,qEAAA,sCACE,KAAA,+BACA,mBAAA,qBAQJ,+DAAA,kCACE,IAAA,6EAEA,sEAAA,uEAAA,yCAAA,0CAEE,aAAA,EAAA,yCAAA,+BAGF,uEAAA,0CACE,IAAA,EACA,oBAAA,+BAGF,sEAAA,yCACE,IAAA,+BACA,oBAAA,qBAKJ,wEAAA,2CACE,SAAA,SACA,IAAA,EACA,KAAA,IACA,QAAA,MACA,MAAA,8BACA,YAAA,0CACA,QAAA,GACA,cAAA,+BAAA,MAAA,4BAMF,6DAAA,iCACE,MAAA,6EACA,MAAA,+BACA,OAAA,8BAEA,oEAAA,qEAAA,wCAAA,yCAEE,aAAA,yCAAA,EAAA,yCAAA,+BAGF,qEAAA,yCACE,MAAA,EACA,kBAAA,+BAGF,oEAAA,wCACE,MAAA,+BACA,kBAAA,qBAuBN,gBACE,QAAA,mCAAA,mCACA,cAAA,EjD2GI,UAAA,mCiDzGJ,MAAA,+BACA,iBAAA,4BACA,cAAA,+BAAA,MAAA,+B9C5JE,uBAAA,sCACA,wBAAA,sC8C8JF,sBACE,QAAA,KAIJ,cACE,QAAA,iCAAA,iCACA,MAAA,6BCrLF,UACE,SAAA,SAGF,wBACE,aAAA,MAGF,gBACE,SAAA,SACA,MAAA,KACA,SAAA,OCtBA,uBACE,QAAA,MACA,MAAA,KACA,QAAA,GDuBJ,eACE,SAAA,SACA,QAAA,KACA,MAAA,KACA,MAAA,KACA,aAAA,MACA,4BAAA,OAAA,oBAAA,OhClBI,WAAA,UAAA,IAAA,YAIA,uCgCQN,ehCPQ,WAAA,MnBm5LR,oBACA,oBmDn4LA,sBAGE,QAAA,MnDq4LF,0BmDl4LA,8CAEE,UAAA,iBnDq4LF,4BmDl4LA,4CAEE,UAAA,kBASA,8BACE,QAAA,EACA,oBAAA,QACA,UAAA,KnD83LJ,uDACA,qDmD53LE,qCAGE,QAAA,EACA,QAAA,EnD63LJ,yCmD13LE,2CAEE,QAAA,EACA,QAAA,EhC5DE,WAAA,QAAA,GAAA,IAIA,uCnBs7LJ,yCmDj4LA,2ChCpDM,WAAA,MnB27LR,uBmD13LA,uBAEE,SAAA,SACA,IAAA,EACA,OAAA,EACA,QAAA,EAEA,QAAA,KACA,YAAA,OACA,gBAAA,OACA,MAAA,IACA,QAAA,EACA,MAAA,KACA,WAAA,OACA,WAAA,IACA,OAAA,EACA,QAAA,GhCtFI,WAAA,QAAA,KAAA,KAIA,uCnB+8LJ,uBmD74LF,uBhCjEQ,WAAA,MnBo9LR,6BADA,6BmD93LE,6BAAA,6BAEE,MAAA,KACA,gBAAA,KACA,QAAA,EACA,QAAA,GAGJ,uBACE,KAAA,EAGF,uBACE,MAAA,EnDk4LF,4BmD73LA,4BAEE,QAAA,aACA,MAAA,KACA,OAAA,KACA,kBAAA,UACA,oBAAA,IACA,gBAAA,KAAA,KAWF,4BACE,iBAAA,wPAEF,4BACE,iBAAA,yPAQF,qBACE,SAAA,SACA,MAAA,EACA,OAAA,EACA,KAAA,EACA,QAAA,EACA,QAAA,KACA,gBAAA,OACA,QAAA,EAEA,aAAA,IACA,cAAA,KACA,YAAA,IAEA,sCACE,WAAA,YACA,KAAA,EAAA,EAAA,KACA,MAAA,KACA,OAAA,IACA,QAAA,EACA,aAAA,IACA,YAAA,IACA,YAAA,OACA,OAAA,QACA,iBAAA,KACA,gBAAA,YACA,OAAA,EAEA,WAAA,KAAA,MAAA,YACA,cAAA,KAAA,MAAA,YACA,QAAA,GhCxKE,WAAA,QAAA,IAAA,KAIA,uCgCoJJ,sChCnJM,WAAA,MgCuKN,6BACE,QAAA,EASJ,kBACE,SAAA,SACA,MAAA,IACA,OAAA,QACA,KAAA,IACA,YAAA,QACA,eAAA,QACA,MAAA,KACA,WAAA,OnDw3LF,2CmDl3LE,2CAEE,OAAA,UAAA,eAGF,qDACE,iBAAA,KAGF,iCACE,MAAA,KnDm3LJ,2DmD73LE,2DnD83LF,0DAD4D,0DmD33LxD,OAAA,UAAA,eAGF,qEAAA,oEACE,iBAAA,KAGF,iDAAA,gDACE,MAAA,KnD+3LJ,gBqDzlMA,cAEE,QAAA,aACA,MAAA,wBACA,OAAA,yBACA,eAAA,iCAEA,cAAA,IACA,UAAA,kCAAA,OAAA,SAAA,iCAIF,0BACE,GAAK,UAAA,gBAIP,gBAEE,mBAAA,KACA,oBAAA,KACA,4BAAA,SACA,0BAAA,OACA,6BAAA,MACA,4BAAA,eAGA,OAAA,+BAAA,MAAA,aACA,mBAAA,YAGF,mBAEE,mBAAA,KACA,oBAAA,KACA,0BAAA,MASF,wBACE,GACE,UAAA,SAEF,IACE,QAAA,EACA,UAAA,MAKJ,cAEE,mBAAA,KACA,oBAAA,KACA,4BAAA,SACA,6BAAA,MACA,4BAAA,aAGA,iBAAA,aACA,QAAA,EAGF,iBACE,mBAAA,KACA,oBAAA,KAIA,uCACE,gBrDukMF,cqDrkMI,6BAAA,MC/EN,WAAA,cAAA,cAAA,cAAA,cAAA,eAEE,sBAAA,KACA,qBAAA,MACA,sBAAA,KACA,yBAAA,KACA,yBAAA,KACA,qBAAA,qBACA,kBAAA,kBACA,4BAAA,uBACA,4BAAA,mCACA,0BAAA,wBACA,0BAAA,UAAA,KAAA,YACA,iCAAA,I3C6DE,4B2C5CF,cAEI,SAAA,MACA,OAAA,EACA,QAAA,2BACA,QAAA,KACA,eAAA,OACA,UAAA,KACA,MAAA,0BACA,WAAA,OACA,iBAAA,uBACA,gBAAA,YACA,QAAA,EnC5BA,WAAA,gCAIA,gEmCYJ,cnCXM,WAAA,MRuDJ,4B2C5BE,8BACE,IAAA,EACA,KAAA,EACA,MAAA,0BACA,aAAA,iCAAA,MAAA,iCACA,UAAA,kBAGF,4BACE,IAAA,EACA,MAAA,EACA,MAAA,0BACA,YAAA,iCAAA,MAAA,iCACA,UAAA,iBAGF,4BACE,IAAA,EACA,MAAA,EACA,KAAA,EACA,OAAA,2BACA,WAAA,KACA,cAAA,iCAAA,MAAA,iCACA,UAAA,kBAGF,+BACE,MAAA,EACA,KAAA,EACA,OAAA,2BACA,WAAA,KACA,WAAA,iCAAA,MAAA,iCACA,UAAA,iBAGF,gCAAA,sBAEE,UAAA,KAGF,qBAAA,mBAAA,sBAGE,WAAA,S3C5BJ,yB2C/BF,cAiEM,sBAAA,KACA,4BAAA,EACA,iBAAA,sBAEA,gCACE,QAAA,KAGF,8BACE,QAAA,KACA,UAAA,EACA,QAAA,EACA,WAAA,QAEA,iBAAA,uB3CnCN,4B2C5CF,cAEI,SAAA,MACA,OAAA,EACA,QAAA,2BACA,QAAA,KACA,eAAA,OACA,UAAA,KACA,MAAA,0BACA,WAAA,OACA,iBAAA,uBACA,gBAAA,YACA,QAAA,EnC5BA,WAAA,gCAIA,gEmCYJ,cnCXM,WAAA,MRuDJ,4B2C5BE,8BACE,IAAA,EACA,KAAA,EACA,MAAA,0BACA,aAAA,iCAAA,MAAA,iCACA,UAAA,kBAGF,4BACE,IAAA,EACA,MAAA,EACA,MAAA,0BACA,YAAA,iCAAA,MAAA,iCACA,UAAA,iBAGF,4BACE,IAAA,EACA,MAAA,EACA,KAAA,EACA,OAAA,2BACA,WAAA,KACA,cAAA,iCAAA,MAAA,iCACA,UAAA,kBAGF,+BACE,MAAA,EACA,KAAA,EACA,OAAA,2BACA,WAAA,KACA,WAAA,iCAAA,MAAA,iCACA,UAAA,iBAGF,gCAAA,sBAEE,UAAA,KAGF,qBAAA,mBAAA,sBAGE,WAAA,S3C5BJ,yB2C/BF,cAiEM,sBAAA,KACA,4BAAA,EACA,iBAAA,sBAEA,gCACE,QAAA,KAGF,8BACE,QAAA,KACA,UAAA,EACA,QAAA,EACA,WAAA,QAEA,iBAAA,uB3CnCN,4B2C5CF,cAEI,SAAA,MACA,OAAA,EACA,QAAA,2BACA,QAAA,KACA,eAAA,OACA,UAAA,KACA,MAAA,0BACA,WAAA,OACA,iBAAA,uBACA,gBAAA,YACA,QAAA,EnC5BA,WAAA,gCAIA,gEmCYJ,cnCXM,WAAA,MRuDJ,4B2C5BE,8BACE,IAAA,EACA,KAAA,EACA,MAAA,0BACA,aAAA,iCAAA,MAAA,iCACA,UAAA,kBAGF,4BACE,IAAA,EACA,MAAA,EACA,MAAA,0BACA,YAAA,iCAAA,MAAA,iCACA,UAAA,iBAGF,4BACE,IAAA,EACA,MAAA,EACA,KAAA,EACA,OAAA,2BACA,WAAA,KACA,cAAA,iCAAA,MAAA,iCACA,UAAA,kBAGF,+BACE,MAAA,EACA,KAAA,EACA,OAAA,2BACA,WAAA,KACA,WAAA,iCAAA,MAAA,iCACA,UAAA,iBAGF,gCAAA,sBAEE,UAAA,KAGF,qBAAA,mBAAA,sBAGE,WAAA,S3C5BJ,yB2C/BF,cAiEM,sBAAA,KACA,4BAAA,EACA,iBAAA,sBAEA,gCACE,QAAA,KAGF,8BACE,QAAA,KACA,UAAA,EACA,QAAA,EACA,WAAA,QAEA,iBAAA,uB3CnCN,6B2C5CF,cAEI,SAAA,MACA,OAAA,EACA,QAAA,2BACA,QAAA,KACA,eAAA,OACA,UAAA,KACA,MAAA,0BACA,WAAA,OACA,iBAAA,uBACA,gBAAA,YACA,QAAA,EnC5BA,WAAA,gCAIA,iEmCYJ,cnCXM,WAAA,MRuDJ,6B2C5BE,8BACE,IAAA,EACA,KAAA,EACA,MAAA,0BACA,aAAA,iCAAA,MAAA,iCACA,UAAA,kBAGF,4BACE,IAAA,EACA,MAAA,EACA,MAAA,0BACA,YAAA,iCAAA,MAAA,iCACA,UAAA,iBAGF,4BACE,IAAA,EACA,MAAA,EACA,KAAA,EACA,OAAA,2BACA,WAAA,KACA,cAAA,iCAAA,MAAA,iCACA,UAAA,kBAGF,+BACE,MAAA,EACA,KAAA,EACA,OAAA,2BACA,WAAA,KACA,WAAA,iCAAA,MAAA,iCACA,UAAA,iBAGF,gCAAA,sBAEE,UAAA,KAGF,qBAAA,mBAAA,sBAGE,WAAA,S3C5BJ,0B2C/BF,cAiEM,sBAAA,KACA,4BAAA,EACA,iBAAA,sBAEA,gCACE,QAAA,KAGF,8BACE,QAAA,KACA,UAAA,EACA,QAAA,EACA,WAAA,QAEA,iBAAA,uB3CnCN,6B2C5CF,eAEI,SAAA,MACA,OAAA,EACA,QAAA,2BACA,QAAA,KACA,eAAA,OACA,UAAA,KACA,MAAA,0BACA,WAAA,OACA,iBAAA,uBACA,gBAAA,YACA,QAAA,EnC5BA,WAAA,gCAIA,iEmCYJ,enCXM,WAAA,MRuDJ,6B2C5BE,+BACE,IAAA,EACA,KAAA,EACA,MAAA,0BACA,aAAA,iCAAA,MAAA,iCACA,UAAA,kBAGF,6BACE,IAAA,EACA,MAAA,EACA,MAAA,0BACA,YAAA,iCAAA,MAAA,iCACA,UAAA,iBAGF,6BACE,IAAA,EACA,MAAA,EACA,KAAA,EACA,OAAA,2BACA,WAAA,KACA,cAAA,iCAAA,MAAA,iCACA,UAAA,kBAGF,gCACE,MAAA,EACA,KAAA,EACA,OAAA,2BACA,WAAA,KACA,WAAA,iCAAA,MAAA,iCACA,UAAA,iBAGF,iCAAA,uBAEE,UAAA,KAGF,sBAAA,oBAAA,uBAGE,WAAA,S3C5BJ,0B2C/BF,eAiEM,sBAAA,KACA,4BAAA,EACA,iBAAA,sBAEA,iCACE,QAAA,KAGF,+BACE,QAAA,KACA,UAAA,EACA,QAAA,EACA,WAAA,QAEA,iBAAA,uBA/ER,WAEI,SAAA,MACA,OAAA,EACA,QAAA,2BACA,QAAA,KACA,eAAA,OACA,UAAA,KACA,MAAA,0BACA,WAAA,OACA,iBAAA,uBACA,gBAAA,YACA,QAAA,EnC5BA,WAAA,+BAIA,uCmCYJ,WnCXM,WAAA,MmC2BF,2BACE,IAAA,EACA,KAAA,EACA,MAAA,0BACA,aAAA,iCAAA,MAAA,iCACA,UAAA,kBAGF,yBACE,IAAA,EACA,MAAA,EACA,MAAA,0BACA,YAAA,iCAAA,MAAA,iCACA,UAAA,iBAGF,yBACE,IAAA,EACA,MAAA,EACA,KAAA,EACA,OAAA,2BACA,WAAA,KACA,cAAA,iCAAA,MAAA,iCACA,UAAA,kBAGF,4BACE,MAAA,EACA,KAAA,EACA,OAAA,2BACA,WAAA,KACA,WAAA,iCAAA,MAAA,iCACA,UAAA,iBAGF,6BAAA,mBAEE,UAAA,KAGF,kBAAA,gBAAA,mBAGE,WAAA,QA2BR,oBPpHE,SAAA,MACA,IAAA,EACA,KAAA,EACA,QAAA,KACA,MAAA,MACA,OAAA,MACA,iBAAA,KAGA,yBAAS,QAAA,EACT,yBAAS,QAAA,GO8GX,kBACE,QAAA,KACA,YAAA,OACA,gBAAA,cACA,QAAA,8BAAA,8BAEA,6BACE,QAAA,yCAAA,yCACA,WAAA,0CACA,aAAA,0CACA,cAAA,0CAIJ,iBACE,cAAA,EACA,YAAA,sCAGF,gBACE,UAAA,EACA,QAAA,8BAAA,8BACA,WAAA,KChJF,aACE,QAAA,aACA,WAAA,IACA,eAAA,OACA,OAAA,KACA,iBAAA,aACA,QAAA,GAEA,yBACE,QAAA,aACA,QAAA,GAKJ,gBACE,WAAA,KAGF,gBACE,WAAA,KAGF,gBACE,WAAA,MAKA,+BACE,UAAA,iBAAA,GAAA,YAAA,SAIJ,4BACE,IACE,QAAA,IAIJ,kBACE,mBAAA,8DAAA,WAAA,8DACA,kBAAA,KAAA,KAAA,UAAA,KAAA,KACA,UAAA,iBAAA,GAAA,OAAA,SAGF,4BACE,KACE,sBAAA,MAAA,GAAA,cAAA,MAAA,IH9CF,iBACE,QAAA,MACA,MAAA,KACA,QAAA,GIHF,iBACE,MAAA,eACA,iBAAA,6DAFF,mBACE,MAAA,eACA,iBAAA,+DAFF,iBACE,MAAA,eACA,iBAAA,6DAFF,cACE,MAAA,eACA,iBAAA,0DAFF,iBACE,MAAA,eACA,iBAAA,6DAFF,gBACE,MAAA,eACA,iBAAA,4DAFF,eACE,MAAA,eACA,iBAAA,2DAFF,cACE,MAAA,eACA,iBAAA,0DCFF,cACE,MAAA,+DACA,8BAAA,yEAAA,sBAAA,yEAGE,oBAAA,oBAGE,MAAA,mDACA,8BAAA,6DAAA,sBAAA,6DATN,gBACE,MAAA,iEACA,8BAAA,2EAAA,sBAAA,2EAGE,sBAAA,sBAGE,MAAA,mDACA,8BAAA,6DAAA,sBAAA,6DATN,cACE,MAAA,+DACA,8BAAA,yEAAA,sBAAA,yEAGE,oBAAA,oBAGE,MAAA,mDACA,8BAAA,6DAAA,sBAAA,6DATN,WACE,MAAA,4DACA,8BAAA,sEAAA,sBAAA,sEAGE,iBAAA,iBAGE,MAAA,oDACA,8BAAA,8DAAA,sBAAA,8DATN,cACE,MAAA,+DACA,8BAAA,yEAAA,sBAAA,yEAGE,oBAAA,oBAGE,MAAA,oDACA,8BAAA,8DAAA,sBAAA,8DATN,aACE,MAAA,8DACA,8BAAA,wEAAA,sBAAA,wEAGE,mBAAA,mBAGE,MAAA,mDACA,8BAAA,6DAAA,sBAAA,6DATN,YACE,MAAA,6DACA,8BAAA,uEAAA,sBAAA,uEAGE,kBAAA,kBAGE,MAAA,qDACA,8BAAA,+DAAA,sBAAA,+DATN,WACE,MAAA,4DACA,8BAAA,sEAAA,sBAAA,sEAGE,iBAAA,iBAGE,MAAA,kDACA,8BAAA,4DAAA,sBAAA,4DAOR,oBACE,MAAA,sEACA,8BAAA,gFAAA,sBAAA,gFAGE,0BAAA,0BAEE,MAAA,wEACA,8BAAA,mFAAA,sBAAA,mFC1BN,kBACE,QAAA,EAEA,WAAA,yBAAA,yBAAA,4BAAA,2BAAA,2BCHF,WACE,QAAA,YACA,IAAA,QACA,YAAA,OACA,8BAAA,0DAAA,sBAAA,0DACA,sBAAA,OACA,4BAAA,OAAA,oBAAA,OAEA,eACE,YAAA,EACA,MAAA,IACA,OAAA,IACA,KAAA,axCIE,WAAA,IAAA,YAAA,UAIA,uCwCZJ,exCaM,WAAA,MwCDJ,mCAAA,2BACE,UAAA,qDCnBN,OACE,SAAA,SACA,MAAA,KAEA,eACE,QAAA,MACA,YAAA,uBACA,QAAA,GAGF,SACE,SAAA,SACA,IAAA,EACA,KAAA,EACA,MAAA,KACA,OAAA,KAKF,WACE,kBAAA,KADF,WACE,kBAAA,IADF,YACE,kBAAA,OADF,YACE,kBAAA,eCrBJ,WACE,SAAA,MACA,IAAA,EACA,MAAA,EACA,KAAA,EACA,QAAA,KAGF,cACE,SAAA,MACA,MAAA,EACA,OAAA,EACA,KAAA,EACA,QAAA,KAQE,YACE,SAAA,eAAA,SAAA,OACA,IAAA,EACA,QAAA,KAGF,eACE,SAAA,eAAA,SAAA,OACA,OAAA,EACA,QAAA,KlD+BF,yBkDxCA,eACE,SAAA,eAAA,SAAA,OACA,IAAA,EACA,QAAA,KAGF,kBACE,SAAA,eAAA,SAAA,OACA,OAAA,EACA,QAAA,MlD+BF,yBkDxCA,eACE,SAAA,eAAA,SAAA,OACA,IAAA,EACA,QAAA,KAGF,kBACE,SAAA,eAAA,SAAA,OACA,OAAA,EACA,QAAA,MlD+BF,yBkDxCA,eACE,SAAA,eAAA,SAAA,OACA,IAAA,EACA,QAAA,KAGF,kBACE,SAAA,eAAA,SAAA,OACA,OAAA,EACA,QAAA,MlD+BF,0BkDxCA,eACE,SAAA,eAAA,SAAA,OACA,IAAA,EACA,QAAA,KAGF,kBACE,SAAA,eAAA,SAAA,OACA,OAAA,EACA,QAAA,MlD+BF,0BkDxCA,gBACE,SAAA,eAAA,SAAA,OACA,IAAA,EACA,QAAA,KAGF,mBACE,SAAA,eAAA,SAAA,OACA,OAAA,EACA,QAAA,MC/BN,QACE,QAAA,KACA,eAAA,IACA,YAAA,OACA,WAAA,QAGF,QACE,QAAA,KACA,KAAA,EAAA,EAAA,KACA,eAAA,OACA,WAAA,QCRF,iB/DigOA,0DgE7/NE,MAAA,cACA,OAAA,cACA,QAAA,YACA,OAAA,eACA,SAAA,iBACA,KAAA,wBACA,YAAA,iBACA,OAAA,YhEigOF,uEgE9/NE,8BACE,SAAA,mBCdF,uBACE,SAAA,SACA,IAAA,EACA,MAAA,EACA,OAAA,EACA,KAAA,EACA,QAAA,EACA,QAAA,GCRJ,eCAE,SAAA,OACA,cAAA,SACA,YAAA,OCNF,IACE,QAAA,aACA,WAAA,QACA,MAAA,uBACA,WAAA,IACA,iBAAA,aACA,QAAA,IC4DM,gBAOI,eAAA,mBAPJ,WAOI,eAAA,cAPJ,cAOI,eAAA,iBAPJ,cAOI,eAAA,iBAPJ,mBAOI,eAAA,sBAPJ,gBAOI,eAAA,mBAPJ,aAOI,MAAA,eAPJ,WAOI,MAAA,gBAPJ,YAOI,MAAA,eAPJ,oBAOI,cAAA,kBAAA,WAAA,kBAPJ,kBAOI,cAAA,gBAAA,WAAA,gBAPJ,iBAOI,cAAA,eAAA,WAAA,eAPJ,kBAOI,cAAA,qBAAA,WAAA,qBAPJ,iBAOI,cAAA,eAAA,WAAA,eAPJ,WAOI,QAAA,YAPJ,YAOI,QAAA,cAPJ,YAOI,QAAA,aAPJ,YAOI,QAAA,cAPJ,aAOI,QAAA,YAPJ,eAOI,SAAA,eAPJ,iBAOI,SAAA,iBAPJ,kBAOI,SAAA,kBAPJ,iBAOI,SAAA,iBAPJ,iBAOI,WAAA,eAPJ,mBAOI,WAAA,iBAPJ,oBAOI,WAAA,kBAPJ,mBAOI,WAAA,iBAPJ,iBAOI,WAAA,eAPJ,mBAOI,WAAA,iBAPJ,oBAOI,WAAA,kBAPJ,mBAOI,WAAA,iBAPJ,UAOI,QAAA,iBAPJ,gBAOI,QAAA,uBAPJ,SAOI,QAAA,gBAPJ,QAOI,QAAA,eAPJ,eAOI,QAAA,sBAPJ,SAOI,QAAA,gBAPJ,aAOI,QAAA,oBAPJ,cAOI,QAAA,qBAPJ,QAOI,QAAA,eAPJ,eAOI,QAAA,sBAPJ,QAOI,QAAA,eAPJ,QAOI,WAAA,+BAPJ,WAOI,WAAA,kCAPJ,WAOI,WAAA,kCAPJ,aAOI,WAAA,eAjBJ,oBACE,sBAAA,0DADF,sBACE,sBAAA,4DADF,oBACE,sBAAA,0DADF,iBACE,sBAAA,uDADF,oBACE,sBAAA,0DADF,mBACE,sBAAA,yDADF,kBACE,sBAAA,wDADF,iBACE,sBAAA,uDASF,iBAOI,SAAA,iBAPJ,mBAOI,SAAA,mBAPJ,mBAOI,SAAA,mBAPJ,gBAOI,SAAA,gBAPJ,iBAOI,SAAA,yBAAA,SAAA,iBAPJ,OAOI,IAAA,YAPJ,QAOI,IAAA,cAPJ,SAOI,IAAA,eAPJ,UAOI,OAAA,YAPJ,WAOI,OAAA,cAPJ,YAOI,OAAA,eAPJ,SAOI,KAAA,YAPJ,UAOI,KAAA,cAPJ,WAOI,KAAA,eAPJ,OAOI,MAAA,YAPJ,QAOI,MAAA,cAPJ,SAOI,MAAA,eAPJ,kBAOI,UAAA,+BAPJ,oBAOI,UAAA,2BAPJ,oBAOI,UAAA,2BAPJ,QAOI,OAAA,uBAAA,uBAAA,iCAPJ,UAOI,OAAA,YAPJ,YAOI,WAAA,uBAAA,uBAAA,iCAPJ,cAOI,WAAA,YAPJ,YAOI,aAAA,uBAAA,uBAAA,iCAPJ,cAOI,aAAA,YAPJ,eAOI,cAAA,uBAAA,uBAAA,iCAPJ,iBAOI,cAAA,YAPJ,cAOI,YAAA,uBAAA,uBAAA,iCAPJ,gBAOI,YAAA,YAPJ,gBAIQ,oBAAA,EAGJ,aAAA,+DAPJ,kBAIQ,oBAAA,EAGJ,aAAA,iEAPJ,gBAIQ,oBAAA,EAGJ,aAAA,+DAPJ,aAIQ,oBAAA,EAGJ,aAAA,4DAPJ,gBAIQ,oBAAA,EAGJ,aAAA,+DAPJ,eAIQ,oBAAA,EAGJ,aAAA,8DAPJ,cAIQ,oBAAA,EAGJ,aAAA,6DAPJ,aAIQ,oBAAA,EAGJ,aAAA,4DAPJ,cAIQ,oBAAA,EAGJ,aAAA,6DAPJ,cAIQ,oBAAA,EAGJ,aAAA,6DAPJ,uBAOI,aAAA,0CAPJ,yBAOI,aAAA,4CAPJ,uBAOI,aAAA,0CAPJ,oBAOI,aAAA,uCAPJ,uBAOI,aAAA,0CAPJ,sBAOI,aAAA,yCAPJ,qBAOI,aAAA,wCAPJ,oBAOI,aAAA,uCAPJ,UAOI,aAAA,cAPJ,UAOI,aAAA,cAPJ,UAOI,aAAA,cAPJ,UAOI,aAAA,cAPJ,UAOI,aAAA,cAjBJ,mBACE,oBAAA,IADF,mBACE,oBAAA,KADF,mBACE,oBAAA,IADF,mBACE,oBAAA,KADF,oBACE,oBAAA,EASF,MAOI,MAAA,cAPJ,MAOI,MAAA,cAPJ,MAOI,MAAA,cAPJ,OAOI,MAAA,eAPJ,QAOI,MAAA,eAPJ,QAOI,UAAA,eAPJ,QAOI,MAAA,gBAPJ,YAOI,UAAA,gBAPJ,MAOI,OAAA,cAPJ,MAOI,OAAA,cAPJ,MAOI,OAAA,cAPJ,OAOI,OAAA,eAPJ,QAOI,OAAA,eAPJ,QAOI,WAAA,eAPJ,QAOI,OAAA,gBAPJ,YAOI,WAAA,gBAPJ,WAOI,KAAA,EAAA,EAAA,eAPJ,UAOI,eAAA,cAPJ,aAOI,eAAA,iBAPJ,kBAOI,eAAA,sBAPJ,qBAOI,eAAA,yBAPJ,aAOI,UAAA,YAPJ,aAOI,UAAA,YAPJ,eAOI,YAAA,YAPJ,eAOI,YAAA,YAPJ,WAOI,UAAA,eAPJ,aAOI,UAAA,iBAPJ,mBAOI,UAAA,uBAPJ,uBAOI,gBAAA,qBAPJ,qBAOI,gBAAA,mBAPJ,wBAOI,gBAAA,iBAPJ,yBAOI,gBAAA,wBAPJ,wBAOI,gBAAA,uBAPJ,wBAOI,gBAAA,uBAPJ,mBAOI,YAAA,qBAPJ,iBAOI,YAAA,mBAPJ,oBAOI,YAAA,iBAPJ,sBAOI,YAAA,mBAPJ,qBAOI,YAAA,kBAPJ,qBAOI,cAAA,qBAPJ,mBAOI,cAAA,mBAPJ,sBAOI,cAAA,iBAPJ,uBAOI,cAAA,wBAPJ,sBAOI,cAAA,uBAPJ,uBAOI,cAAA,kBAPJ,iBAOI,WAAA,eAPJ,kBAOI,WAAA,qBAPJ,gBAOI,WAAA,mBAPJ,mBAOI,WAAA,iBAPJ,qBAOI,WAAA,mBAPJ,oBAOI,WAAA,kBAPJ,aAOI,MAAA,aAPJ,SAOI,MAAA,YAPJ,SAOI,MAAA,YAPJ,SAOI,MAAA,YAPJ,SAOI,MAAA,YAPJ,SAOI,MAAA,YAPJ,SAOI,MAAA,YAPJ,YAOI,MAAA,YAPJ,KAOI,OAAA,YAPJ,KAOI,OAAA,iBAPJ,KAOI,OAAA,gBAPJ,KAOI,OAAA,eAPJ,KAOI,OAAA,iBAPJ,KAOI,OAAA,eAPJ,QAOI,OAAA,eAPJ,MAOI,aAAA,YAAA,YAAA,YAPJ,MAOI,aAAA,iBAAA,YAAA,iBAPJ,MAOI,aAAA,gBAAA,YAAA,gBAPJ,MAOI,aAAA,eAAA,YAAA,eAPJ,MAOI,aAAA,iBAAA,YAAA,iBAPJ,MAOI,aAAA,eAAA,YAAA,eAPJ,SAOI,aAAA,eAAA,YAAA,eAPJ,MAOI,WAAA,YAAA,cAAA,YAPJ,MAOI,WAAA,iBAAA,cAAA,iBAPJ,MAOI,WAAA,gBAAA,cAAA,gBAPJ,MAOI,WAAA,eAAA,cAAA,eAPJ,MAOI,WAAA,iBAAA,cAAA,iBAPJ,MAOI,WAAA,eAAA,cAAA,eAPJ,SAOI,WAAA,eAAA,cAAA,eAPJ,MAOI,WAAA,YAPJ,MAOI,WAAA,iBAPJ,MAOI,WAAA,gBAPJ,MAOI,WAAA,eAPJ,MAOI,WAAA,iBAPJ,MAOI,WAAA,eAPJ,SAOI,WAAA,eAPJ,MAOI,aAAA,YAPJ,MAOI,aAAA,iBAPJ,MAOI,aAAA,gBAPJ,MAOI,aAAA,eAPJ,MAOI,aAAA,iBAPJ,MAOI,aAAA,eAPJ,SAOI,aAAA,eAPJ,MAOI,cAAA,YAPJ,MAOI,cAAA,iBAPJ,MAOI,cAAA,gBAPJ,MAOI,cAAA,eAPJ,MAOI,cAAA,iBAPJ,MAOI,cAAA,eAPJ,SAOI,cAAA,eAPJ,MAOI,YAAA,YAPJ,MAOI,YAAA,iBAPJ,MAOI,YAAA,gBAPJ,MAOI,YAAA,eAPJ,MAOI,YAAA,iBAPJ,MAOI,YAAA,eAPJ,SAOI,YAAA,eAPJ,KAOI,QAAA,YAPJ,KAOI,QAAA,iBAPJ,KAOI,QAAA,gBAPJ,KAOI,QAAA,eAPJ,KAOI,QAAA,iBAPJ,KAOI,QAAA,eAPJ,MAOI,cAAA,YAAA,aAAA,YAPJ,MAOI,cAAA,iBAAA,aAAA,iBAPJ,MAOI,cAAA,gBAAA,aAAA,gBAPJ,MAOI,cAAA,eAAA,aAAA,eAPJ,MAOI,cAAA,iBAAA,aAAA,iBAPJ,MAOI,cAAA,eAAA,aAAA,eAPJ,MAOI,YAAA,YAAA,eAAA,YAPJ,MAOI,YAAA,iBAAA,eAAA,iBAPJ,MAOI,YAAA,gBAAA,eAAA,gBAPJ,MAOI,YAAA,eAAA,eAAA,eAPJ,MAOI,YAAA,iBAAA,eAAA,iBAPJ,MAOI,YAAA,eAAA,eAAA,eAPJ,MAOI,YAAA,YAPJ,MAOI,YAAA,iBAPJ,MAOI,YAAA,gBAPJ,MAOI,YAAA,eAPJ,MAOI,YAAA,iBAPJ,MAOI,YAAA,eAPJ,MAOI,cAAA,YAPJ,MAOI,cAAA,iBAPJ,MAOI,cAAA,gBAPJ,MAOI,cAAA,eAPJ,MAOI,cAAA,iBAPJ,MAOI,cAAA,eAPJ,MAOI,eAAA,YAPJ,MAOI,eAAA,iBAPJ,MAOI,eAAA,gBAPJ,MAOI,eAAA,eAPJ,MAOI,eAAA,iBAPJ,MAOI,eAAA,eAPJ,MAOI,aAAA,YAPJ,MAOI,aAAA,iBAPJ,MAOI,aAAA,gBAPJ,MAOI,aAAA,eAPJ,MAOI,aAAA,iBAPJ,MAOI,aAAA,eAPJ,OAOI,IAAA,YAPJ,OAOI,IAAA,iBAPJ,OAOI,IAAA,gBAPJ,OAOI,IAAA,eAPJ,OAOI,IAAA,iBAPJ,OAOI,IAAA,eAPJ,WAOI,QAAA,YAPJ,WAOI,QAAA,iBAPJ,WAOI,QAAA,gBAPJ,WAOI,QAAA,eAPJ,WAOI,QAAA,iBAPJ,WAOI,QAAA,eAPJ,cAOI,gBAAA,YAAA,WAAA,YAPJ,cAOI,gBAAA,kBAAA,WAAA,iBAPJ,cAOI,gBAAA,iBAAA,WAAA,gBAPJ,cAOI,gBAAA,eAAA,WAAA,eAPJ,cAOI,gBAAA,iBAAA,WAAA,iBAPJ,cAOI,gBAAA,eAAA,WAAA,eAPJ,gBAOI,YAAA,mCAPJ,MAOI,UAAA,iCAPJ,MAOI,UAAA,gCAPJ,MAOI,UAAA,8BAPJ,MAOI,UAAA,gCAPJ,MAOI,UAAA,kBAPJ,MAOI,UAAA,eAPJ,YAOI,WAAA,iBAPJ,YAOI,WAAA,iBAPJ,YAOI,YAAA,kBAPJ,UAOI,YAAA,cAPJ,WAOI,YAAA,cAPJ,WAOI,YAAA,cAPJ,aAOI,YAAA,cAPJ,SAOI,YAAA,cAPJ,WAOI,YAAA,iBAPJ,MAOI,YAAA,YAPJ,OAOI,YAAA,eAPJ,SAOI,YAAA,cAPJ,OAOI,YAAA,YAPJ,YAOI,WAAA,eAPJ,UAOI,WAAA,gBAPJ,aAOI,WAAA,iBAPJ,sBAOI,gBAAA,eAPJ,2BAOI,gBAAA,oBAPJ,8BAOI,gBAAA,uBAPJ,gBAOI,eAAA,oBAPJ,gBAOI,eAAA,oBAPJ,iBAOI,eAAA,qBAPJ,WAOI,YAAA,iBAPJ,aAOI,YAAA,iBAPJ,YAOI,UAAA,qBAAA,WAAA,qBAPJ,cAIQ,kBAAA,EAGJ,MAAA,6DAPJ,gBAIQ,kBAAA,EAGJ,MAAA,+DAPJ,cAIQ,kBAAA,EAGJ,MAAA,6DAPJ,WAIQ,kBAAA,EAGJ,MAAA,0DAPJ,cAIQ,kBAAA,EAGJ,MAAA,6DAPJ,aAIQ,kBAAA,EAGJ,MAAA,4DAPJ,YAIQ,kBAAA,EAGJ,MAAA,2DAPJ,WAIQ,kBAAA,EAGJ,MAAA,0DAPJ,YAIQ,kBAAA,EAGJ,MAAA,2DAPJ,YAIQ,kBAAA,EAGJ,MAAA,2DAPJ,WAIQ,kBAAA,EAGJ,MAAA,gEAPJ,YAIQ,kBAAA,EAGJ,MAAA,oCAPJ,eAIQ,kBAAA,EAGJ,MAAA,yBAPJ,eAIQ,kBAAA,EAGJ,MAAA,+BAPJ,qBAIQ,kBAAA,EAGJ,MAAA,oCAPJ,oBAIQ,kBAAA,EAGJ,MAAA,mCAPJ,oBAIQ,kBAAA,EAGJ,MAAA,mCAPJ,YAIQ,kBAAA,EAGJ,MAAA,kBAjBJ,iBACE,kBAAA,KADF,iBACE,kBAAA,IADF,iBACE,kBAAA,KADF,kBACE,kBAAA,EASF,uBAOI,MAAA,0CAPJ,yBAOI,MAAA,4CAPJ,uBAOI,MAAA,0CAPJ,oBAOI,MAAA,uCAPJ,uBAOI,MAAA,0CAPJ,sBAOI,MAAA,yCAPJ,qBAOI,MAAA,wCAPJ,oBAOI,MAAA,uCAjBJ,iBACE,kBAAA,IAIA,6BACE,kBAAA,IANJ,iBACE,kBAAA,KAIA,6BACE,kBAAA,KANJ,iBACE,kBAAA,IAIA,6BACE,kBAAA,IANJ,iBACE,kBAAA,KAIA,6BACE,kBAAA,KANJ,kBACE,kBAAA,EAIA,8BACE,kBAAA,EAIJ,eAOI,sBAAA,kBAKF,2BAOI,sBAAA,kBAnBN,eAOI,sBAAA,iBAKF,2BAOI,sBAAA,iBAnBN,eAOI,sBAAA,kBAKF,2BAOI,sBAAA,kBAnBN,wBAIQ,4BAAA,EAGJ,8BAAA,uEAAA,sBAAA,uEAPJ,0BAIQ,4BAAA,EAGJ,8BAAA,yEAAA,sBAAA,yEAPJ,wBAIQ,4BAAA,EAGJ,8BAAA,uEAAA,sBAAA,uEAPJ,qBAIQ,4BAAA,EAGJ,8BAAA,oEAAA,sBAAA,oEAPJ,wBAIQ,4BAAA,EAGJ,8BAAA,uEAAA,sBAAA,uEAPJ,uBAIQ,4BAAA,EAGJ,8BAAA,sEAAA,sBAAA,sEAPJ,sBAIQ,4BAAA,EAGJ,8BAAA,qEAAA,sBAAA,qEAPJ,qBAIQ,4BAAA,EAGJ,8BAAA,oEAAA,sBAAA,oEAPJ,gBAIQ,4BAAA,EAGJ,8BAAA,4EAAA,sBAAA,4EAjBJ,0BACE,4BAAA,EAIA,sCACE,4BAAA,EANJ,2BACE,4BAAA,IAIA,uCACE,4BAAA,IANJ,2BACE,4BAAA,KAIA,uCACE,4BAAA,KANJ,2BACE,4BAAA,IAIA,uCACE,4BAAA,IANJ,2BACE,4BAAA,KAIA,uCACE,4BAAA,KANJ,4BACE,4BAAA,EAIA,wCACE,4BAAA,EAIJ,YAIQ,gBAAA,EAGJ,iBAAA,2DAPJ,cAIQ,gBAAA,EAGJ,iBAAA,6DAPJ,YAIQ,gBAAA,EAGJ,iBAAA,2DAPJ,SAIQ,gBAAA,EAGJ,iBAAA,wDAPJ,YAIQ,gBAAA,EAGJ,iBAAA,2DAPJ,WAIQ,gBAAA,EAGJ,iBAAA,0DAPJ,UAIQ,gBAAA,EAGJ,iBAAA,yDAPJ,SAIQ,gBAAA,EAGJ,iBAAA,wDAPJ,UAIQ,gBAAA,EAGJ,iBAAA,yDAPJ,UAIQ,gBAAA,EAGJ,iBAAA,yDAPJ,SAIQ,gBAAA,EAGJ,iBAAA,2DAPJ,gBAIQ,gBAAA,EAGJ,iBAAA,sBAPJ,mBAIQ,gBAAA,EAGJ,iBAAA,gEAPJ,kBAIQ,gBAAA,EAGJ,iBAAA,+DAjBJ,eACE,gBAAA,IADF,eACE,gBAAA,KADF,eACE,gBAAA,IADF,eACE,gBAAA,KADF,gBACE,gBAAA,EASF,mBAOI,iBAAA,sCAPJ,qBAOI,iBAAA,wCAPJ,mBAOI,iBAAA,sCAPJ,gBAOI,iBAAA,mCAPJ,mBAOI,iBAAA,sCAPJ,kBAOI,iBAAA,qCAPJ,iBAOI,iBAAA,oCAPJ,gBAOI,iBAAA,mCAPJ,aAOI,iBAAA,6BAPJ,iBAOI,oBAAA,cAAA,iBAAA,cAAA,YAAA,cAPJ,kBAOI,oBAAA,eAAA,iBAAA,eAAA,YAAA,eAPJ,kBAOI,oBAAA,eAAA,iBAAA,eAAA,YAAA,eAPJ,SAOI,eAAA,eAPJ,SAOI,eAAA,eAPJ,SAOI,cAAA,kCAPJ,WAOI,cAAA,YAPJ,WAOI,cAAA,qCAPJ,WAOI,cAAA,kCAPJ,WAOI,cAAA,qCAPJ,WAOI,cAAA,qCAPJ,WAOI,cAAA,sCAPJ,gBAOI,cAAA,cAPJ,cAOI,cAAA,uCAPJ,aAOI,uBAAA,kCAAA,wBAAA,kCAPJ,eAOI,uBAAA,YAAA,wBAAA,YAPJ,eAOI,uBAAA,qCAAA,wBAAA,qCAPJ,eAOI,uBAAA,kCAAA,wBAAA,kCAPJ,eAOI,uBAAA,qCAAA,wBAAA,qCAPJ,eAOI,uBAAA,qCAAA,wBAAA,qCAPJ,eAOI,uBAAA,sCAAA,wBAAA,sCAPJ,oBAOI,uBAAA,cAAA,wBAAA,cAPJ,kBAOI,uBAAA,uCAAA,wBAAA,uCAPJ,aAOI,wBAAA,kCAAA,2BAAA,kCAPJ,eAOI,wBAAA,YAAA,2BAAA,YAPJ,eAOI,wBAAA,qCAAA,2BAAA,qCAPJ,eAOI,wBAAA,kCAAA,2BAAA,kCAPJ,eAOI,wBAAA,qCAAA,2BAAA,qCAPJ,eAOI,wBAAA,qCAAA,2BAAA,qCAPJ,eAOI,wBAAA,sCAAA,2BAAA,sCAPJ,oBAOI,wBAAA,cAAA,2BAAA,cAPJ,kBAOI,wBAAA,uCAAA,2BAAA,uCAPJ,gBAOI,2BAAA,kCAAA,0BAAA,kCAPJ,kBAOI,2BAAA,YAAA,0BAAA,YAPJ,kBAOI,2BAAA,qCAAA,0BAAA,qCAPJ,kBAOI,2BAAA,kCAAA,0BAAA,kCAPJ,kBAOI,2BAAA,qCAAA,0BAAA,qCAPJ,kBAOI,2BAAA,qCAAA,0BAAA,qCAPJ,kBAOI,2BAAA,sCAAA,0BAAA,sCAPJ,uBAOI,2BAAA,cAAA,0BAAA,cAPJ,qBAOI,2BAAA,uCAAA,0BAAA,uCAPJ,eAOI,0BAAA,kCAAA,uBAAA,kCAPJ,iBAOI,0BAAA,YAAA,uBAAA,YAPJ,iBAOI,0BAAA,qCAAA,uBAAA,qCAPJ,iBAOI,0BAAA,kCAAA,uBAAA,kCAPJ,iBAOI,0BAAA,qCAAA,uBAAA,qCAPJ,iBAOI,0BAAA,qCAAA,uBAAA,qCAPJ,iBAOI,0BAAA,sCAAA,uBAAA,sCAPJ,sBAOI,0BAAA,cAAA,uBAAA,cAPJ,oBAOI,0BAAA,uCAAA,uBAAA,uCAPJ,SAOI,WAAA,kBAPJ,WAOI,WAAA,iBAPJ,MAOI,QAAA,aAPJ,KAOI,QAAA,YAPJ,KAOI,QAAA,YAPJ,KAOI,QAAA,YAPJ,KAOI,QAAA,Y1DVR,yB0DGI,gBAOI,MAAA,eAPJ,cAOI,MAAA,gBAPJ,eAOI,MAAA,eAPJ,uBAOI,cAAA,kBAAA,WAAA,kBAPJ,qBAOI,cAAA,gBAAA,WAAA,gBAPJ,oBAOI,cAAA,eAAA,WAAA,eAPJ,qBAOI,cAAA,qBAAA,WAAA,qBAPJ,oBAOI,cAAA,eAAA,WAAA,eAPJ,aAOI,QAAA,iBAPJ,mBAOI,QAAA,uBAPJ,YAOI,QAAA,gBAPJ,WAOI,QAAA,eAPJ,kBAOI,QAAA,sBAPJ,YAOI,QAAA,gBAPJ,gBAOI,QAAA,oBAPJ,iBAOI,QAAA,qBAPJ,WAOI,QAAA,eAPJ,kBAOI,QAAA,sBAPJ,WAOI,QAAA,eAPJ,cAOI,KAAA,EAAA,EAAA,eAPJ,aAOI,eAAA,cAPJ,gBAOI,eAAA,iBAPJ,qBAOI,eAAA,sBAPJ,wBAOI,eAAA,yBAPJ,gBAOI,UAAA,YAPJ,gBAOI,UAAA,YAPJ,kBAOI,YAAA,YAPJ,kBAOI,YAAA,YAPJ,cAOI,UAAA,eAPJ,gBAOI,UAAA,iBAPJ,sBAOI,UAAA,uBAPJ,0BAOI,gBAAA,qBAPJ,wBAOI,gBAAA,mBAPJ,2BAOI,gBAAA,iBAPJ,4BAOI,gBAAA,wBAPJ,2BAOI,gBAAA,uBAPJ,2BAOI,gBAAA,uBAPJ,sBAOI,YAAA,qBAPJ,oBAOI,YAAA,mBAPJ,uBAOI,YAAA,iBAPJ,yBAOI,YAAA,mBAPJ,wBAOI,YAAA,kBAPJ,wBAOI,cAAA,qBAPJ,sBAOI,cAAA,mBAPJ,yBAOI,cAAA,iBAPJ,0BAOI,cAAA,wBAPJ,yBAOI,cAAA,uBAPJ,0BAOI,cAAA,kBAPJ,oBAOI,WAAA,eAPJ,qBAOI,WAAA,qBAPJ,mBAOI,WAAA,mBAPJ,sBAOI,WAAA,iBAPJ,wBAOI,WAAA,mBAPJ,uBAOI,WAAA,kBAPJ,gBAOI,MAAA,aAPJ,YAOI,MAAA,YAPJ,YAOI,MAAA,YAPJ,YAOI,MAAA,YAPJ,YAOI,MAAA,YAPJ,YAOI,MAAA,YAPJ,YAOI,MAAA,YAPJ,eAOI,MAAA,YAPJ,QAOI,OAAA,YAPJ,QAOI,OAAA,iBAPJ,QAOI,OAAA,gBAPJ,QAOI,OAAA,eAPJ,QAOI,OAAA,iBAPJ,QAOI,OAAA,eAPJ,WAOI,OAAA,eAPJ,SAOI,aAAA,YAAA,YAAA,YAPJ,SAOI,aAAA,iBAAA,YAAA,iBAPJ,SAOI,aAAA,gBAAA,YAAA,gBAPJ,SAOI,aAAA,eAAA,YAAA,eAPJ,SAOI,aAAA,iBAAA,YAAA,iBAPJ,SAOI,aAAA,eAAA,YAAA,eAPJ,YAOI,aAAA,eAAA,YAAA,eAPJ,SAOI,WAAA,YAAA,cAAA,YAPJ,SAOI,WAAA,iBAAA,cAAA,iBAPJ,SAOI,WAAA,gBAAA,cAAA,gBAPJ,SAOI,WAAA,eAAA,cAAA,eAPJ,SAOI,WAAA,iBAAA,cAAA,iBAPJ,SAOI,WAAA,eAAA,cAAA,eAPJ,YAOI,WAAA,eAAA,cAAA,eAPJ,SAOI,WAAA,YAPJ,SAOI,WAAA,iBAPJ,SAOI,WAAA,gBAPJ,SAOI,WAAA,eAPJ,SAOI,WAAA,iBAPJ,SAOI,WAAA,eAPJ,YAOI,WAAA,eAPJ,SAOI,aAAA,YAPJ,SAOI,aAAA,iBAPJ,SAOI,aAAA,gBAPJ,SAOI,aAAA,eAPJ,SAOI,aAAA,iBAPJ,SAOI,aAAA,eAPJ,YAOI,aAAA,eAPJ,SAOI,cAAA,YAPJ,SAOI,cAAA,iBAPJ,SAOI,cAAA,gBAPJ,SAOI,cAAA,eAPJ,SAOI,cAAA,iBAPJ,SAOI,cAAA,eAPJ,YAOI,cAAA,eAPJ,SAOI,YAAA,YAPJ,SAOI,YAAA,iBAPJ,SAOI,YAAA,gBAPJ,SAOI,YAAA,eAPJ,SAOI,YAAA,iBAPJ,SAOI,YAAA,eAPJ,YAOI,YAAA,eAPJ,QAOI,QAAA,YAPJ,QAOI,QAAA,iBAPJ,QAOI,QAAA,gBAPJ,QAOI,QAAA,eAPJ,QAOI,QAAA,iBAPJ,QAOI,QAAA,eAPJ,SAOI,cAAA,YAAA,aAAA,YAPJ,SAOI,cAAA,iBAAA,aAAA,iBAPJ,SAOI,cAAA,gBAAA,aAAA,gBAPJ,SAOI,cAAA,eAAA,aAAA,eAPJ,SAOI,cAAA,iBAAA,aAAA,iBAPJ,SAOI,cAAA,eAAA,aAAA,eAPJ,SAOI,YAAA,YAAA,eAAA,YAPJ,SAOI,YAAA,iBAAA,eAAA,iBAPJ,SAOI,YAAA,gBAAA,eAAA,gBAPJ,SAOI,YAAA,eAAA,eAAA,eAPJ,SAOI,YAAA,iBAAA,eAAA,iBAPJ,SAOI,YAAA,eAAA,eAAA,eAPJ,SAOI,YAAA,YAPJ,SAOI,YAAA,iBAPJ,SAOI,YAAA,gBAPJ,SAOI,YAAA,eAPJ,SAOI,YAAA,iBAPJ,SAOI,YAAA,eAPJ,SAOI,cAAA,YAPJ,SAOI,cAAA,iBAPJ,SAOI,cAAA,gBAPJ,SAOI,cAAA,eAPJ,SAOI,cAAA,iBAPJ,SAOI,cAAA,eAPJ,SAOI,eAAA,YAPJ,SAOI,eAAA,iBAPJ,SAOI,eAAA,gBAPJ,SAOI,eAAA,eAPJ,SAOI,eAAA,iBAPJ,SAOI,eAAA,eAPJ,SAOI,aAAA,YAPJ,SAOI,aAAA,iBAPJ,SAOI,aAAA,gBAPJ,SAOI,aAAA,eAPJ,SAOI,aAAA,iBAPJ,SAOI,aAAA,eAPJ,UAOI,IAAA,YAPJ,UAOI,IAAA,iBAPJ,UAOI,IAAA,gBAPJ,UAOI,IAAA,eAPJ,UAOI,IAAA,iBAPJ,UAOI,IAAA,eAPJ,cAOI,QAAA,YAPJ,cAOI,QAAA,iBAPJ,cAOI,QAAA,gBAPJ,cAOI,QAAA,eAPJ,cAOI,QAAA,iBAPJ,cAOI,QAAA,eAPJ,iBAOI,gBAAA,YAAA,WAAA,YAPJ,iBAOI,gBAAA,kBAAA,WAAA,iBAPJ,iBAOI,gBAAA,iBAAA,WAAA,gBAPJ,iBAOI,gBAAA,eAAA,WAAA,eAPJ,iBAOI,gBAAA,iBAAA,WAAA,iBAPJ,iBAOI,gBAAA,eAAA,WAAA,eAPJ,eAOI,WAAA,eAPJ,aAOI,WAAA,gBAPJ,gBAOI,WAAA,kB1DVR,yB0DGI,gBAOI,MAAA,eAPJ,cAOI,MAAA,gBAPJ,eAOI,MAAA,eAPJ,uBAOI,cAAA,kBAAA,WAAA,kBAPJ,qBAOI,cAAA,gBAAA,WAAA,gBAPJ,oBAOI,cAAA,eAAA,WAAA,eAPJ,qBAOI,cAAA,qBAAA,WAAA,qBAPJ,oBAOI,cAAA,eAAA,WAAA,eAPJ,aAOI,QAAA,iBAPJ,mBAOI,QAAA,uBAPJ,YAOI,QAAA,gBAPJ,WAOI,QAAA,eAPJ,kBAOI,QAAA,sBAPJ,YAOI,QAAA,gBAPJ,gBAOI,QAAA,oBAPJ,iBAOI,QAAA,qBAPJ,WAOI,QAAA,eAPJ,kBAOI,QAAA,sBAPJ,WAOI,QAAA,eAPJ,cAOI,KAAA,EAAA,EAAA,eAPJ,aAOI,eAAA,cAPJ,gBAOI,eAAA,iBAPJ,qBAOI,eAAA,sBAPJ,wBAOI,eAAA,yBAPJ,gBAOI,UAAA,YAPJ,gBAOI,UAAA,YAPJ,kBAOI,YAAA,YAPJ,kBAOI,YAAA,YAPJ,cAOI,UAAA,eAPJ,gBAOI,UAAA,iBAPJ,sBAOI,UAAA,uBAPJ,0BAOI,gBAAA,qBAPJ,wBAOI,gBAAA,mBAPJ,2BAOI,gBAAA,iBAPJ,4BAOI,gBAAA,wBAPJ,2BAOI,gBAAA,uBAPJ,2BAOI,gBAAA,uBAPJ,sBAOI,YAAA,qBAPJ,oBAOI,YAAA,mBAPJ,uBAOI,YAAA,iBAPJ,yBAOI,YAAA,mBAPJ,wBAOI,YAAA,kBAPJ,wBAOI,cAAA,qBAPJ,sBAOI,cAAA,mBAPJ,yBAOI,cAAA,iBAPJ,0BAOI,cAAA,wBAPJ,yBAOI,cAAA,uBAPJ,0BAOI,cAAA,kBAPJ,oBAOI,WAAA,eAPJ,qBAOI,WAAA,qBAPJ,mBAOI,WAAA,mBAPJ,sBAOI,WAAA,iBAPJ,wBAOI,WAAA,mBAPJ,uBAOI,WAAA,kBAPJ,gBAOI,MAAA,aAPJ,YAOI,MAAA,YAPJ,YAOI,MAAA,YAPJ,YAOI,MAAA,YAPJ,YAOI,MAAA,YAPJ,YAOI,MAAA,YAPJ,YAOI,MAAA,YAPJ,eAOI,MAAA,YAPJ,QAOI,OAAA,YAPJ,QAOI,OAAA,iBAPJ,QAOI,OAAA,gBAPJ,QAOI,OAAA,eAPJ,QAOI,OAAA,iBAPJ,QAOI,OAAA,eAPJ,WAOI,OAAA,eAPJ,SAOI,aAAA,YAAA,YAAA,YAPJ,SAOI,aAAA,iBAAA,YAAA,iBAPJ,SAOI,aAAA,gBAAA,YAAA,gBAPJ,SAOI,aAAA,eAAA,YAAA,eAPJ,SAOI,aAAA,iBAAA,YAAA,iBAPJ,SAOI,aAAA,eAAA,YAAA,eAPJ,YAOI,aAAA,eAAA,YAAA,eAPJ,SAOI,WAAA,YAAA,cAAA,YAPJ,SAOI,WAAA,iBAAA,cAAA,iBAPJ,SAOI,WAAA,gBAAA,cAAA,gBAPJ,SAOI,WAAA,eAAA,cAAA,eAPJ,SAOI,WAAA,iBAAA,cAAA,iBAPJ,SAOI,WAAA,eAAA,cAAA,eAPJ,YAOI,WAAA,eAAA,cAAA,eAPJ,SAOI,WAAA,YAPJ,SAOI,WAAA,iBAPJ,SAOI,WAAA,gBAPJ,SAOI,WAAA,eAPJ,SAOI,WAAA,iBAPJ,SAOI,WAAA,eAPJ,YAOI,WAAA,eAPJ,SAOI,aAAA,YAPJ,SAOI,aAAA,iBAPJ,SAOI,aAAA,gBAPJ,SAOI,aAAA,eAPJ,SAOI,aAAA,iBAPJ,SAOI,aAAA,eAPJ,YAOI,aAAA,eAPJ,SAOI,cAAA,YAPJ,SAOI,cAAA,iBAPJ,SAOI,cAAA,gBAPJ,SAOI,cAAA,eAPJ,SAOI,cAAA,iBAPJ,SAOI,cAAA,eAPJ,YAOI,cAAA,eAPJ,SAOI,YAAA,YAPJ,SAOI,YAAA,iBAPJ,SAOI,YAAA,gBAPJ,SAOI,YAAA,eAPJ,SAOI,YAAA,iBAPJ,SAOI,YAAA,eAPJ,YAOI,YAAA,eAPJ,QAOI,QAAA,YAPJ,QAOI,QAAA,iBAPJ,QAOI,QAAA,gBAPJ,QAOI,QAAA,eAPJ,QAOI,QAAA,iBAPJ,QAOI,QAAA,eAPJ,SAOI,cAAA,YAAA,aAAA,YAPJ,SAOI,cAAA,iBAAA,aAAA,iBAPJ,SAOI,cAAA,gBAAA,aAAA,gBAPJ,SAOI,cAAA,eAAA,aAAA,eAPJ,SAOI,cAAA,iBAAA,aAAA,iBAPJ,SAOI,cAAA,eAAA,aAAA,eAPJ,SAOI,YAAA,YAAA,eAAA,YAPJ,SAOI,YAAA,iBAAA,eAAA,iBAPJ,SAOI,YAAA,gBAAA,eAAA,gBAPJ,SAOI,YAAA,eAAA,eAAA,eAPJ,SAOI,YAAA,iBAAA,eAAA,iBAPJ,SAOI,YAAA,eAAA,eAAA,eAPJ,SAOI,YAAA,YAPJ,SAOI,YAAA,iBAPJ,SAOI,YAAA,gBAPJ,SAOI,YAAA,eAPJ,SAOI,YAAA,iBAPJ,SAOI,YAAA,eAPJ,SAOI,cAAA,YAPJ,SAOI,cAAA,iBAPJ,SAOI,cAAA,gBAPJ,SAOI,cAAA,eAPJ,SAOI,cAAA,iBAPJ,SAOI,cAAA,eAPJ,SAOI,eAAA,YAPJ,SAOI,eAAA,iBAPJ,SAOI,eAAA,gBAPJ,SAOI,eAAA,eAPJ,SAOI,eAAA,iBAPJ,SAOI,eAAA,eAPJ,SAOI,aAAA,YAPJ,SAOI,aAAA,iBAPJ,SAOI,aAAA,gBAPJ,SAOI,aAAA,eAPJ,SAOI,aAAA,iBAPJ,SAOI,aAAA,eAPJ,UAOI,IAAA,YAPJ,UAOI,IAAA,iBAPJ,UAOI,IAAA,gBAPJ,UAOI,IAAA,eAPJ,UAOI,IAAA,iBAPJ,UAOI,IAAA,eAPJ,cAOI,QAAA,YAPJ,cAOI,QAAA,iBAPJ,cAOI,QAAA,gBAPJ,cAOI,QAAA,eAPJ,cAOI,QAAA,iBAPJ,cAOI,QAAA,eAPJ,iBAOI,gBAAA,YAAA,WAAA,YAPJ,iBAOI,gBAAA,kBAAA,WAAA,iBAPJ,iBAOI,gBAAA,iBAAA,WAAA,gBAPJ,iBAOI,gBAAA,eAAA,WAAA,eAPJ,iBAOI,gBAAA,iBAAA,WAAA,iBAPJ,iBAOI,gBAAA,eAAA,WAAA,eAPJ,eAOI,WAAA,eAPJ,aAOI,WAAA,gBAPJ,gBAOI,WAAA,kB1DVR,yB0DGI,gBAOI,MAAA,eAPJ,cAOI,MAAA,gBAPJ,eAOI,MAAA,eAPJ,uBAOI,cAAA,kBAAA,WAAA,kBAPJ,qBAOI,cAAA,gBAAA,WAAA,gBAPJ,oBAOI,cAAA,eAAA,WAAA,eAPJ,qBAOI,cAAA,qBAAA,WAAA,qBAPJ,oBAOI,cAAA,eAAA,WAAA,eAPJ,aAOI,QAAA,iBAPJ,mBAOI,QAAA,uBAPJ,YAOI,QAAA,gBAPJ,WAOI,QAAA,eAPJ,kBAOI,QAAA,sBAPJ,YAOI,QAAA,gBAPJ,gBAOI,QAAA,oBAPJ,iBAOI,QAAA,qBAPJ,WAOI,QAAA,eAPJ,kBAOI,QAAA,sBAPJ,WAOI,QAAA,eAPJ,cAOI,KAAA,EAAA,EAAA,eAPJ,aAOI,eAAA,cAPJ,gBAOI,eAAA,iBAPJ,qBAOI,eAAA,sBAPJ,wBAOI,eAAA,yBAPJ,gBAOI,UAAA,YAPJ,gBAOI,UAAA,YAPJ,kBAOI,YAAA,YAPJ,kBAOI,YAAA,YAPJ,cAOI,UAAA,eAPJ,gBAOI,UAAA,iBAPJ,sBAOI,UAAA,uBAPJ,0BAOI,gBAAA,qBAPJ,wBAOI,gBAAA,mBAPJ,2BAOI,gBAAA,iBAPJ,4BAOI,gBAAA,wBAPJ,2BAOI,gBAAA,uBAPJ,2BAOI,gBAAA,uBAPJ,sBAOI,YAAA,qBAPJ,oBAOI,YAAA,mBAPJ,uBAOI,YAAA,iBAPJ,yBAOI,YAAA,mBAPJ,wBAOI,YAAA,kBAPJ,wBAOI,cAAA,qBAPJ,sBAOI,cAAA,mBAPJ,yBAOI,cAAA,iBAPJ,0BAOI,cAAA,wBAPJ,yBAOI,cAAA,uBAPJ,0BAOI,cAAA,kBAPJ,oBAOI,WAAA,eAPJ,qBAOI,WAAA,qBAPJ,mBAOI,WAAA,mBAPJ,sBAOI,WAAA,iBAPJ,wBAOI,WAAA,mBAPJ,uBAOI,WAAA,kBAPJ,gBAOI,MAAA,aAPJ,YAOI,MAAA,YAPJ,YAOI,MAAA,YAPJ,YAOI,MAAA,YAPJ,YAOI,MAAA,YAPJ,YAOI,MAAA,YAPJ,YAOI,MAAA,YAPJ,eAOI,MAAA,YAPJ,QAOI,OAAA,YAPJ,QAOI,OAAA,iBAPJ,QAOI,OAAA,gBAPJ,QAOI,OAAA,eAPJ,QAOI,OAAA,iBAPJ,QAOI,OAAA,eAPJ,WAOI,OAAA,eAPJ,SAOI,aAAA,YAAA,YAAA,YAPJ,SAOI,aAAA,iBAAA,YAAA,iBAPJ,SAOI,aAAA,gBAAA,YAAA,gBAPJ,SAOI,aAAA,eAAA,YAAA,eAPJ,SAOI,aAAA,iBAAA,YAAA,iBAPJ,SAOI,aAAA,eAAA,YAAA,eAPJ,YAOI,aAAA,eAAA,YAAA,eAPJ,SAOI,WAAA,YAAA,cAAA,YAPJ,SAOI,WAAA,iBAAA,cAAA,iBAPJ,SAOI,WAAA,gBAAA,cAAA,gBAPJ,SAOI,WAAA,eAAA,cAAA,eAPJ,SAOI,WAAA,iBAAA,cAAA,iBAPJ,SAOI,WAAA,eAAA,cAAA,eAPJ,YAOI,WAAA,eAAA,cAAA,eAPJ,SAOI,WAAA,YAPJ,SAOI,WAAA,iBAPJ,SAOI,WAAA,gBAPJ,SAOI,WAAA,eAPJ,SAOI,WAAA,iBAPJ,SAOI,WAAA,eAPJ,YAOI,WAAA,eAPJ,SAOI,aAAA,YAPJ,SAOI,aAAA,iBAPJ,SAOI,aAAA,gBAPJ,SAOI,aAAA,eAPJ,SAOI,aAAA,iBAPJ,SAOI,aAAA,eAPJ,YAOI,aAAA,eAPJ,SAOI,cAAA,YAPJ,SAOI,cAAA,iBAPJ,SAOI,cAAA,gBAPJ,SAOI,cAAA,eAPJ,SAOI,cAAA,iBAPJ,SAOI,cAAA,eAPJ,YAOI,cAAA,eAPJ,SAOI,YAAA,YAPJ,SAOI,YAAA,iBAPJ,SAOI,YAAA,gBAPJ,SAOI,YAAA,eAPJ,SAOI,YAAA,iBAPJ,SAOI,YAAA,eAPJ,YAOI,YAAA,eAPJ,QAOI,QAAA,YAPJ,QAOI,QAAA,iBAPJ,QAOI,QAAA,gBAPJ,QAOI,QAAA,eAPJ,QAOI,QAAA,iBAPJ,QAOI,QAAA,eAPJ,SAOI,cAAA,YAAA,aAAA,YAPJ,SAOI,cAAA,iBAAA,aAAA,iBAPJ,SAOI,cAAA,gBAAA,aAAA,gBAPJ,SAOI,cAAA,eAAA,aAAA,eAPJ,SAOI,cAAA,iBAAA,aAAA,iBAPJ,SAOI,cAAA,eAAA,aAAA,eAPJ,SAOI,YAAA,YAAA,eAAA,YAPJ,SAOI,YAAA,iBAAA,eAAA,iBAPJ,SAOI,YAAA,gBAAA,eAAA,gBAPJ,SAOI,YAAA,eAAA,eAAA,eAPJ,SAOI,YAAA,iBAAA,eAAA,iBAPJ,SAOI,YAAA,eAAA,eAAA,eAPJ,SAOI,YAAA,YAPJ,SAOI,YAAA,iBAPJ,SAOI,YAAA,gBAPJ,SAOI,YAAA,eAPJ,SAOI,YAAA,iBAPJ,SAOI,YAAA,eAPJ,SAOI,cAAA,YAPJ,SAOI,cAAA,iBAPJ,SAOI,cAAA,gBAPJ,SAOI,cAAA,eAPJ,SAOI,cAAA,iBAPJ,SAOI,cAAA,eAPJ,SAOI,eAAA,YAPJ,SAOI,eAAA,iBAPJ,SAOI,eAAA,gBAPJ,SAOI,eAAA,eAPJ,SAOI,eAAA,iBAPJ,SAOI,eAAA,eAPJ,SAOI,aAAA,YAPJ,SAOI,aAAA,iBAPJ,SAOI,aAAA,gBAPJ,SAOI,aAAA,eAPJ,SAOI,aAAA,iBAPJ,SAOI,aAAA,eAPJ,UAOI,IAAA,YAPJ,UAOI,IAAA,iBAPJ,UAOI,IAAA,gBAPJ,UAOI,IAAA,eAPJ,UAOI,IAAA,iBAPJ,UAOI,IAAA,eAPJ,cAOI,QAAA,YAPJ,cAOI,QAAA,iBAPJ,cAOI,QAAA,gBAPJ,cAOI,QAAA,eAPJ,cAOI,QAAA,iBAPJ,cAOI,QAAA,eAPJ,iBAOI,gBAAA,YAAA,WAAA,YAPJ,iBAOI,gBAAA,kBAAA,WAAA,iBAPJ,iBAOI,gBAAA,iBAAA,WAAA,gBAPJ,iBAOI,gBAAA,eAAA,WAAA,eAPJ,iBAOI,gBAAA,iBAAA,WAAA,iBAPJ,iBAOI,gBAAA,eAAA,WAAA,eAPJ,eAOI,WAAA,eAPJ,aAOI,WAAA,gBAPJ,gBAOI,WAAA,kB1DVR,0B0DGI,gBAOI,MAAA,eAPJ,cAOI,MAAA,gBAPJ,eAOI,MAAA,eAPJ,uBAOI,cAAA,kBAAA,WAAA,kBAPJ,qBAOI,cAAA,gBAAA,WAAA,gBAPJ,oBAOI,cAAA,eAAA,WAAA,eAPJ,qBAOI,cAAA,qBAAA,WAAA,qBAPJ,oBAOI,cAAA,eAAA,WAAA,eAPJ,aAOI,QAAA,iBAPJ,mBAOI,QAAA,uBAPJ,YAOI,QAAA,gBAPJ,WAOI,QAAA,eAPJ,kBAOI,QAAA,sBAPJ,YAOI,QAAA,gBAPJ,gBAOI,QAAA,oBAPJ,iBAOI,QAAA,qBAPJ,WAOI,QAAA,eAPJ,kBAOI,QAAA,sBAPJ,WAOI,QAAA,eAPJ,cAOI,KAAA,EAAA,EAAA,eAPJ,aAOI,eAAA,cAPJ,gBAOI,eAAA,iBAPJ,qBAOI,eAAA,sBAPJ,wBAOI,eAAA,yBAPJ,gBAOI,UAAA,YAPJ,gBAOI,UAAA,YAPJ,kBAOI,YAAA,YAPJ,kBAOI,YAAA,YAPJ,cAOI,UAAA,eAPJ,gBAOI,UAAA,iBAPJ,sBAOI,UAAA,uBAPJ,0BAOI,gBAAA,qBAPJ,wBAOI,gBAAA,mBAPJ,2BAOI,gBAAA,iBAPJ,4BAOI,gBAAA,wBAPJ,2BAOI,gBAAA,uBAPJ,2BAOI,gBAAA,uBAPJ,sBAOI,YAAA,qBAPJ,oBAOI,YAAA,mBAPJ,uBAOI,YAAA,iBAPJ,yBAOI,YAAA,mBAPJ,wBAOI,YAAA,kBAPJ,wBAOI,cAAA,qBAPJ,sBAOI,cAAA,mBAPJ,yBAOI,cAAA,iBAPJ,0BAOI,cAAA,wBAPJ,yBAOI,cAAA,uBAPJ,0BAOI,cAAA,kBAPJ,oBAOI,WAAA,eAPJ,qBAOI,WAAA,qBAPJ,mBAOI,WAAA,mBAPJ,sBAOI,WAAA,iBAPJ,wBAOI,WAAA,mBAPJ,uBAOI,WAAA,kBAPJ,gBAOI,MAAA,aAPJ,YAOI,MAAA,YAPJ,YAOI,MAAA,YAPJ,YAOI,MAAA,YAPJ,YAOI,MAAA,YAPJ,YAOI,MAAA,YAPJ,YAOI,MAAA,YAPJ,eAOI,MAAA,YAPJ,QAOI,OAAA,YAPJ,QAOI,OAAA,iBAPJ,QAOI,OAAA,gBAPJ,QAOI,OAAA,eAPJ,QAOI,OAAA,iBAPJ,QAOI,OAAA,eAPJ,WAOI,OAAA,eAPJ,SAOI,aAAA,YAAA,YAAA,YAPJ,SAOI,aAAA,iBAAA,YAAA,iBAPJ,SAOI,aAAA,gBAAA,YAAA,gBAPJ,SAOI,aAAA,eAAA,YAAA,eAPJ,SAOI,aAAA,iBAAA,YAAA,iBAPJ,SAOI,aAAA,eAAA,YAAA,eAPJ,YAOI,aAAA,eAAA,YAAA,eAPJ,SAOI,WAAA,YAAA,cAAA,YAPJ,SAOI,WAAA,iBAAA,cAAA,iBAPJ,SAOI,WAAA,gBAAA,cAAA,gBAPJ,SAOI,WAAA,eAAA,cAAA,eAPJ,SAOI,WAAA,iBAAA,cAAA,iBAPJ,SAOI,WAAA,eAAA,cAAA,eAPJ,YAOI,WAAA,eAAA,cAAA,eAPJ,SAOI,WAAA,YAPJ,SAOI,WAAA,iBAPJ,SAOI,WAAA,gBAPJ,SAOI,WAAA,eAPJ,SAOI,WAAA,iBAPJ,SAOI,WAAA,eAPJ,YAOI,WAAA,eAPJ,SAOI,aAAA,YAPJ,SAOI,aAAA,iBAPJ,SAOI,aAAA,gBAPJ,SAOI,aAAA,eAPJ,SAOI,aAAA,iBAPJ,SAOI,aAAA,eAPJ,YAOI,aAAA,eAPJ,SAOI,cAAA,YAPJ,SAOI,cAAA,iBAPJ,SAOI,cAAA,gBAPJ,SAOI,cAAA,eAPJ,SAOI,cAAA,iBAPJ,SAOI,cAAA,eAPJ,YAOI,cAAA,eAPJ,SAOI,YAAA,YAPJ,SAOI,YAAA,iBAPJ,SAOI,YAAA,gBAPJ,SAOI,YAAA,eAPJ,SAOI,YAAA,iBAPJ,SAOI,YAAA,eAPJ,YAOI,YAAA,eAPJ,QAOI,QAAA,YAPJ,QAOI,QAAA,iBAPJ,QAOI,QAAA,gBAPJ,QAOI,QAAA,eAPJ,QAOI,QAAA,iBAPJ,QAOI,QAAA,eAPJ,SAOI,cAAA,YAAA,aAAA,YAPJ,SAOI,cAAA,iBAAA,aAAA,iBAPJ,SAOI,cAAA,gBAAA,aAAA,gBAPJ,SAOI,cAAA,eAAA,aAAA,eAPJ,SAOI,cAAA,iBAAA,aAAA,iBAPJ,SAOI,cAAA,eAAA,aAAA,eAPJ,SAOI,YAAA,YAAA,eAAA,YAPJ,SAOI,YAAA,iBAAA,eAAA,iBAPJ,SAOI,YAAA,gBAAA,eAAA,gBAPJ,SAOI,YAAA,eAAA,eAAA,eAPJ,SAOI,YAAA,iBAAA,eAAA,iBAPJ,SAOI,YAAA,eAAA,eAAA,eAPJ,SAOI,YAAA,YAPJ,SAOI,YAAA,iBAPJ,SAOI,YAAA,gBAPJ,SAOI,YAAA,eAPJ,SAOI,YAAA,iBAPJ,SAOI,YAAA,eAPJ,SAOI,cAAA,YAPJ,SAOI,cAAA,iBAPJ,SAOI,cAAA,gBAPJ,SAOI,cAAA,eAPJ,SAOI,cAAA,iBAPJ,SAOI,cAAA,eAPJ,SAOI,eAAA,YAPJ,SAOI,eAAA,iBAPJ,SAOI,eAAA,gBAPJ,SAOI,eAAA,eAPJ,SAOI,eAAA,iBAPJ,SAOI,eAAA,eAPJ,SAOI,aAAA,YAPJ,SAOI,aAAA,iBAPJ,SAOI,aAAA,gBAPJ,SAOI,aAAA,eAPJ,SAOI,aAAA,iBAPJ,SAOI,aAAA,eAPJ,UAOI,IAAA,YAPJ,UAOI,IAAA,iBAPJ,UAOI,IAAA,gBAPJ,UAOI,IAAA,eAPJ,UAOI,IAAA,iBAPJ,UAOI,IAAA,eAPJ,cAOI,QAAA,YAPJ,cAOI,QAAA,iBAPJ,cAOI,QAAA,gBAPJ,cAOI,QAAA,eAPJ,cAOI,QAAA,iBAPJ,cAOI,QAAA,eAPJ,iBAOI,gBAAA,YAAA,WAAA,YAPJ,iBAOI,gBAAA,kBAAA,WAAA,iBAPJ,iBAOI,gBAAA,iBAAA,WAAA,gBAPJ,iBAOI,gBAAA,eAAA,WAAA,eAPJ,iBAOI,gBAAA,iBAAA,WAAA,iBAPJ,iBAOI,gBAAA,eAAA,WAAA,eAPJ,eAOI,WAAA,eAPJ,aAOI,WAAA,gBAPJ,gBAOI,WAAA,kB1DVR,0B0DGI,iBAOI,MAAA,eAPJ,eAOI,MAAA,gBAPJ,gBAOI,MAAA,eAPJ,wBAOI,cAAA,kBAAA,WAAA,kBAPJ,sBAOI,cAAA,gBAAA,WAAA,gBAPJ,qBAOI,cAAA,eAAA,WAAA,eAPJ,sBAOI,cAAA,qBAAA,WAAA,qBAPJ,qBAOI,cAAA,eAAA,WAAA,eAPJ,cAOI,QAAA,iBAPJ,oBAOI,QAAA,uBAPJ,aAOI,QAAA,gBAPJ,YAOI,QAAA,eAPJ,mBAOI,QAAA,sBAPJ,aAOI,QAAA,gBAPJ,iBAOI,QAAA,oBAPJ,kBAOI,QAAA,qBAPJ,YAOI,QAAA,eAPJ,mBAOI,QAAA,sBAPJ,YAOI,QAAA,eAPJ,eAOI,KAAA,EAAA,EAAA,eAPJ,cAOI,eAAA,cAPJ,iBAOI,eAAA,iBAPJ,sBAOI,eAAA,sBAPJ,yBAOI,eAAA,yBAPJ,iBAOI,UAAA,YAPJ,iBAOI,UAAA,YAPJ,mBAOI,YAAA,YAPJ,mBAOI,YAAA,YAPJ,eAOI,UAAA,eAPJ,iBAOI,UAAA,iBAPJ,uBAOI,UAAA,uBAPJ,2BAOI,gBAAA,qBAPJ,yBAOI,gBAAA,mBAPJ,4BAOI,gBAAA,iBAPJ,6BAOI,gBAAA,wBAPJ,4BAOI,gBAAA,uBAPJ,4BAOI,gBAAA,uBAPJ,uBAOI,YAAA,qBAPJ,qBAOI,YAAA,mBAPJ,wBAOI,YAAA,iBAPJ,0BAOI,YAAA,mBAPJ,yBAOI,YAAA,kBAPJ,yBAOI,cAAA,qBAPJ,uBAOI,cAAA,mBAPJ,0BAOI,cAAA,iBAPJ,2BAOI,cAAA,wBAPJ,0BAOI,cAAA,uBAPJ,2BAOI,cAAA,kBAPJ,qBAOI,WAAA,eAPJ,sBAOI,WAAA,qBAPJ,oBAOI,WAAA,mBAPJ,uBAOI,WAAA,iBAPJ,yBAOI,WAAA,mBAPJ,wBAOI,WAAA,kBAPJ,iBAOI,MAAA,aAPJ,aAOI,MAAA,YAPJ,aAOI,MAAA,YAPJ,aAOI,MAAA,YAPJ,aAOI,MAAA,YAPJ,aAOI,MAAA,YAPJ,aAOI,MAAA,YAPJ,gBAOI,MAAA,YAPJ,SAOI,OAAA,YAPJ,SAOI,OAAA,iBAPJ,SAOI,OAAA,gBAPJ,SAOI,OAAA,eAPJ,SAOI,OAAA,iBAPJ,SAOI,OAAA,eAPJ,YAOI,OAAA,eAPJ,UAOI,aAAA,YAAA,YAAA,YAPJ,UAOI,aAAA,iBAAA,YAAA,iBAPJ,UAOI,aAAA,gBAAA,YAAA,gBAPJ,UAOI,aAAA,eAAA,YAAA,eAPJ,UAOI,aAAA,iBAAA,YAAA,iBAPJ,UAOI,aAAA,eAAA,YAAA,eAPJ,aAOI,aAAA,eAAA,YAAA,eAPJ,UAOI,WAAA,YAAA,cAAA,YAPJ,UAOI,WAAA,iBAAA,cAAA,iBAPJ,UAOI,WAAA,gBAAA,cAAA,gBAPJ,UAOI,WAAA,eAAA,cAAA,eAPJ,UAOI,WAAA,iBAAA,cAAA,iBAPJ,UAOI,WAAA,eAAA,cAAA,eAPJ,aAOI,WAAA,eAAA,cAAA,eAPJ,UAOI,WAAA,YAPJ,UAOI,WAAA,iBAPJ,UAOI,WAAA,gBAPJ,UAOI,WAAA,eAPJ,UAOI,WAAA,iBAPJ,UAOI,WAAA,eAPJ,aAOI,WAAA,eAPJ,UAOI,aAAA,YAPJ,UAOI,aAAA,iBAPJ,UAOI,aAAA,gBAPJ,UAOI,aAAA,eAPJ,UAOI,aAAA,iBAPJ,UAOI,aAAA,eAPJ,aAOI,aAAA,eAPJ,UAOI,cAAA,YAPJ,UAOI,cAAA,iBAPJ,UAOI,cAAA,gBAPJ,UAOI,cAAA,eAPJ,UAOI,cAAA,iBAPJ,UAOI,cAAA,eAPJ,aAOI,cAAA,eAPJ,UAOI,YAAA,YAPJ,UAOI,YAAA,iBAPJ,UAOI,YAAA,gBAPJ,UAOI,YAAA,eAPJ,UAOI,YAAA,iBAPJ,UAOI,YAAA,eAPJ,aAOI,YAAA,eAPJ,SAOI,QAAA,YAPJ,SAOI,QAAA,iBAPJ,SAOI,QAAA,gBAPJ,SAOI,QAAA,eAPJ,SAOI,QAAA,iBAPJ,SAOI,QAAA,eAPJ,UAOI,cAAA,YAAA,aAAA,YAPJ,UAOI,cAAA,iBAAA,aAAA,iBAPJ,UAOI,cAAA,gBAAA,aAAA,gBAPJ,UAOI,cAAA,eAAA,aAAA,eAPJ,UAOI,cAAA,iBAAA,aAAA,iBAPJ,UAOI,cAAA,eAAA,aAAA,eAPJ,UAOI,YAAA,YAAA,eAAA,YAPJ,UAOI,YAAA,iBAAA,eAAA,iBAPJ,UAOI,YAAA,gBAAA,eAAA,gBAPJ,UAOI,YAAA,eAAA,eAAA,eAPJ,UAOI,YAAA,iBAAA,eAAA,iBAPJ,UAOI,YAAA,eAAA,eAAA,eAPJ,UAOI,YAAA,YAPJ,UAOI,YAAA,iBAPJ,UAOI,YAAA,gBAPJ,UAOI,YAAA,eAPJ,UAOI,YAAA,iBAPJ,UAOI,YAAA,eAPJ,UAOI,cAAA,YAPJ,UAOI,cAAA,iBAPJ,UAOI,cAAA,gBAPJ,UAOI,cAAA,eAPJ,UAOI,cAAA,iBAPJ,UAOI,cAAA,eAPJ,UAOI,eAAA,YAPJ,UAOI,eAAA,iBAPJ,UAOI,eAAA,gBAPJ,UAOI,eAAA,eAPJ,UAOI,eAAA,iBAPJ,UAOI,eAAA,eAPJ,UAOI,aAAA,YAPJ,UAOI,aAAA,iBAPJ,UAOI,aAAA,gBAPJ,UAOI,aAAA,eAPJ,UAOI,aAAA,iBAPJ,UAOI,aAAA,eAPJ,WAOI,IAAA,YAPJ,WAOI,IAAA,iBAPJ,WAOI,IAAA,gBAPJ,WAOI,IAAA,eAPJ,WAOI,IAAA,iBAPJ,WAOI,IAAA,eAPJ,eAOI,QAAA,YAPJ,eAOI,QAAA,iBAPJ,eAOI,QAAA,gBAPJ,eAOI,QAAA,eAPJ,eAOI,QAAA,iBAPJ,eAOI,QAAA,eAPJ,kBAOI,gBAAA,YAAA,WAAA,YAPJ,kBAOI,gBAAA,kBAAA,WAAA,iBAPJ,kBAOI,gBAAA,iBAAA,WAAA,gBAPJ,kBAOI,gBAAA,eAAA,WAAA,eAPJ,kBAOI,gBAAA,iBAAA,WAAA,iBAPJ,kBAOI,gBAAA,eAAA,WAAA,eAPJ,gBAOI,WAAA,eAPJ,cAOI,WAAA,gBAPJ,iBAOI,WAAA,kBCtDZ,0BD+CQ,MAOI,UAAA,iBAPJ,MAOI,UAAA,eAPJ,MAOI,UAAA,kBAPJ,MAOI,UAAA,kBCnCZ,aD4BQ,gBAOI,QAAA,iBAPJ,sBAOI,QAAA,uBAPJ,eAOI,QAAA,gBAPJ,cAOI,QAAA,eAPJ,qBAOI,QAAA,sBAPJ,eAOI,QAAA,gBAPJ,mBAOI,QAAA,oBAPJ,oBAOI,QAAA,qBAPJ,cAOI,QAAA,eAPJ,qBAOI,QAAA,sBAPJ,cAOI,QAAA","sourcesContent":["@mixin bsBanner($file) {\n /*!\n * Bootstrap #{$file} v5.3.2 (https://getbootstrap.com/)\n * Copyright 2011-2023 The Bootstrap Authors\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n */\n}\n",":root,\n[data-bs-theme=\"light\"] {\n // Note: Custom variable values only support SassScript inside `#{}`.\n\n // Colors\n //\n // Generate palettes for full colors, grays, and theme colors.\n\n @each $color, $value in $colors {\n --#{$prefix}#{$color}: #{$value};\n }\n\n @each $color, $value in $grays {\n --#{$prefix}gray-#{$color}: #{$value};\n }\n\n @each $color, $value in $theme-colors {\n --#{$prefix}#{$color}: #{$value};\n }\n\n @each $color, $value in $theme-colors-rgb {\n --#{$prefix}#{$color}-rgb: #{$value};\n }\n\n @each $color, $value in $theme-colors-text {\n --#{$prefix}#{$color}-text-emphasis: #{$value};\n }\n\n @each $color, $value in $theme-colors-bg-subtle {\n --#{$prefix}#{$color}-bg-subtle: #{$value};\n }\n\n @each $color, $value in $theme-colors-border-subtle {\n --#{$prefix}#{$color}-border-subtle: #{$value};\n }\n\n --#{$prefix}white-rgb: #{to-rgb($white)};\n --#{$prefix}black-rgb: #{to-rgb($black)};\n\n // Fonts\n\n // Note: Use `inspect` for lists so that quoted items keep the quotes.\n // See https://github.com/sass/sass/issues/2383#issuecomment-336349172\n --#{$prefix}font-sans-serif: #{inspect($font-family-sans-serif)};\n --#{$prefix}font-monospace: #{inspect($font-family-monospace)};\n --#{$prefix}gradient: #{$gradient};\n\n // Root and body\n // scss-docs-start root-body-variables\n @if $font-size-root != null {\n --#{$prefix}root-font-size: #{$font-size-root};\n }\n --#{$prefix}body-font-family: #{inspect($font-family-base)};\n @include rfs($font-size-base, --#{$prefix}body-font-size);\n --#{$prefix}body-font-weight: #{$font-weight-base};\n --#{$prefix}body-line-height: #{$line-height-base};\n @if $body-text-align != null {\n --#{$prefix}body-text-align: #{$body-text-align};\n }\n\n --#{$prefix}body-color: #{$body-color};\n --#{$prefix}body-color-rgb: #{to-rgb($body-color)};\n --#{$prefix}body-bg: #{$body-bg};\n --#{$prefix}body-bg-rgb: #{to-rgb($body-bg)};\n\n --#{$prefix}emphasis-color: #{$body-emphasis-color};\n --#{$prefix}emphasis-color-rgb: #{to-rgb($body-emphasis-color)};\n\n --#{$prefix}secondary-color: #{$body-secondary-color};\n --#{$prefix}secondary-color-rgb: #{to-rgb($body-secondary-color)};\n --#{$prefix}secondary-bg: #{$body-secondary-bg};\n --#{$prefix}secondary-bg-rgb: #{to-rgb($body-secondary-bg)};\n\n --#{$prefix}tertiary-color: #{$body-tertiary-color};\n --#{$prefix}tertiary-color-rgb: #{to-rgb($body-tertiary-color)};\n --#{$prefix}tertiary-bg: #{$body-tertiary-bg};\n --#{$prefix}tertiary-bg-rgb: #{to-rgb($body-tertiary-bg)};\n // scss-docs-end root-body-variables\n\n --#{$prefix}heading-color: #{$headings-color};\n\n --#{$prefix}link-color: #{$link-color};\n --#{$prefix}link-color-rgb: #{to-rgb($link-color)};\n --#{$prefix}link-decoration: #{$link-decoration};\n\n --#{$prefix}link-hover-color: #{$link-hover-color};\n --#{$prefix}link-hover-color-rgb: #{to-rgb($link-hover-color)};\n\n @if $link-hover-decoration != null {\n --#{$prefix}link-hover-decoration: #{$link-hover-decoration};\n }\n\n --#{$prefix}code-color: #{$code-color};\n --#{$prefix}highlight-color: #{$mark-color};\n --#{$prefix}highlight-bg: #{$mark-bg};\n\n // scss-docs-start root-border-var\n --#{$prefix}border-width: #{$border-width};\n --#{$prefix}border-style: #{$border-style};\n --#{$prefix}border-color: #{$border-color};\n --#{$prefix}border-color-translucent: #{$border-color-translucent};\n\n --#{$prefix}border-radius: #{$border-radius};\n --#{$prefix}border-radius-sm: #{$border-radius-sm};\n --#{$prefix}border-radius-lg: #{$border-radius-lg};\n --#{$prefix}border-radius-xl: #{$border-radius-xl};\n --#{$prefix}border-radius-xxl: #{$border-radius-xxl};\n --#{$prefix}border-radius-2xl: var(--#{$prefix}border-radius-xxl); // Deprecated in v5.3.0 for consistency\n --#{$prefix}border-radius-pill: #{$border-radius-pill};\n // scss-docs-end root-border-var\n\n --#{$prefix}box-shadow: #{$box-shadow};\n --#{$prefix}box-shadow-sm: #{$box-shadow-sm};\n --#{$prefix}box-shadow-lg: #{$box-shadow-lg};\n --#{$prefix}box-shadow-inset: #{$box-shadow-inset};\n\n // Focus styles\n // scss-docs-start root-focus-variables\n --#{$prefix}focus-ring-width: #{$focus-ring-width};\n --#{$prefix}focus-ring-opacity: #{$focus-ring-opacity};\n --#{$prefix}focus-ring-color: #{$focus-ring-color};\n // scss-docs-end root-focus-variables\n\n // scss-docs-start root-form-validation-variables\n --#{$prefix}form-valid-color: #{$form-valid-color};\n --#{$prefix}form-valid-border-color: #{$form-valid-border-color};\n --#{$prefix}form-invalid-color: #{$form-invalid-color};\n --#{$prefix}form-invalid-border-color: #{$form-invalid-border-color};\n // scss-docs-end root-form-validation-variables\n}\n\n@if $enable-dark-mode {\n @include color-mode(dark, true) {\n color-scheme: dark;\n\n // scss-docs-start root-dark-mode-vars\n --#{$prefix}body-color: #{$body-color-dark};\n --#{$prefix}body-color-rgb: #{to-rgb($body-color-dark)};\n --#{$prefix}body-bg: #{$body-bg-dark};\n --#{$prefix}body-bg-rgb: #{to-rgb($body-bg-dark)};\n\n --#{$prefix}emphasis-color: #{$body-emphasis-color-dark};\n --#{$prefix}emphasis-color-rgb: #{to-rgb($body-emphasis-color-dark)};\n\n --#{$prefix}secondary-color: #{$body-secondary-color-dark};\n --#{$prefix}secondary-color-rgb: #{to-rgb($body-secondary-color-dark)};\n --#{$prefix}secondary-bg: #{$body-secondary-bg-dark};\n --#{$prefix}secondary-bg-rgb: #{to-rgb($body-secondary-bg-dark)};\n\n --#{$prefix}tertiary-color: #{$body-tertiary-color-dark};\n --#{$prefix}tertiary-color-rgb: #{to-rgb($body-tertiary-color-dark)};\n --#{$prefix}tertiary-bg: #{$body-tertiary-bg-dark};\n --#{$prefix}tertiary-bg-rgb: #{to-rgb($body-tertiary-bg-dark)};\n\n @each $color, $value in $theme-colors-text-dark {\n --#{$prefix}#{$color}-text-emphasis: #{$value};\n }\n\n @each $color, $value in $theme-colors-bg-subtle-dark {\n --#{$prefix}#{$color}-bg-subtle: #{$value};\n }\n\n @each $color, $value in $theme-colors-border-subtle-dark {\n --#{$prefix}#{$color}-border-subtle: #{$value};\n }\n\n --#{$prefix}heading-color: #{$headings-color-dark};\n\n --#{$prefix}link-color: #{$link-color-dark};\n --#{$prefix}link-hover-color: #{$link-hover-color-dark};\n --#{$prefix}link-color-rgb: #{to-rgb($link-color-dark)};\n --#{$prefix}link-hover-color-rgb: #{to-rgb($link-hover-color-dark)};\n\n --#{$prefix}code-color: #{$code-color-dark};\n --#{$prefix}highlight-color: #{$mark-color-dark};\n --#{$prefix}highlight-bg: #{$mark-bg-dark};\n\n --#{$prefix}border-color: #{$border-color-dark};\n --#{$prefix}border-color-translucent: #{$border-color-translucent-dark};\n\n --#{$prefix}form-valid-color: #{$form-valid-color-dark};\n --#{$prefix}form-valid-border-color: #{$form-valid-border-color-dark};\n --#{$prefix}form-invalid-color: #{$form-invalid-color-dark};\n --#{$prefix}form-invalid-border-color: #{$form-invalid-border-color-dark};\n // scss-docs-end root-dark-mode-vars\n }\n}\n","@charset \"UTF-8\";\n/*!\n * Bootstrap v5.3.2 (https://getbootstrap.com/)\n * Copyright 2011-2023 The Bootstrap Authors\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n */\n:root,\n[data-bs-theme=light] {\n --bs-blue: #0d6efd;\n --bs-indigo: #6610f2;\n --bs-purple: #6f42c1;\n --bs-pink: #d63384;\n --bs-red: #dc3545;\n --bs-orange: #fd7e14;\n --bs-yellow: #ffc107;\n --bs-green: #198754;\n --bs-teal: #20c997;\n --bs-cyan: #0dcaf0;\n --bs-black: #000;\n --bs-white: #fff;\n --bs-gray: #6c757d;\n --bs-gray-dark: #343a40;\n --bs-gray-100: #f8f9fa;\n --bs-gray-200: #e9ecef;\n --bs-gray-300: #dee2e6;\n --bs-gray-400: #ced4da;\n --bs-gray-500: #adb5bd;\n --bs-gray-600: #6c757d;\n --bs-gray-700: #495057;\n --bs-gray-800: #343a40;\n --bs-gray-900: #212529;\n --bs-primary: #0d6efd;\n --bs-secondary: #6c757d;\n --bs-success: #198754;\n --bs-info: #0dcaf0;\n --bs-warning: #ffc107;\n --bs-danger: #dc3545;\n --bs-light: #f8f9fa;\n --bs-dark: #212529;\n --bs-primary-rgb: 13, 110, 253;\n --bs-secondary-rgb: 108, 117, 125;\n --bs-success-rgb: 25, 135, 84;\n --bs-info-rgb: 13, 202, 240;\n --bs-warning-rgb: 255, 193, 7;\n --bs-danger-rgb: 220, 53, 69;\n --bs-light-rgb: 248, 249, 250;\n --bs-dark-rgb: 33, 37, 41;\n --bs-primary-text-emphasis: #052c65;\n --bs-secondary-text-emphasis: #2b2f32;\n --bs-success-text-emphasis: #0a3622;\n --bs-info-text-emphasis: #055160;\n --bs-warning-text-emphasis: #664d03;\n --bs-danger-text-emphasis: #58151c;\n --bs-light-text-emphasis: #495057;\n --bs-dark-text-emphasis: #495057;\n --bs-primary-bg-subtle: #cfe2ff;\n --bs-secondary-bg-subtle: #e2e3e5;\n --bs-success-bg-subtle: #d1e7dd;\n --bs-info-bg-subtle: #cff4fc;\n --bs-warning-bg-subtle: #fff3cd;\n --bs-danger-bg-subtle: #f8d7da;\n --bs-light-bg-subtle: #fcfcfd;\n --bs-dark-bg-subtle: #ced4da;\n --bs-primary-border-subtle: #9ec5fe;\n --bs-secondary-border-subtle: #c4c8cb;\n --bs-success-border-subtle: #a3cfbb;\n --bs-info-border-subtle: #9eeaf9;\n --bs-warning-border-subtle: #ffe69c;\n --bs-danger-border-subtle: #f1aeb5;\n --bs-light-border-subtle: #e9ecef;\n --bs-dark-border-subtle: #adb5bd;\n --bs-white-rgb: 255, 255, 255;\n --bs-black-rgb: 0, 0, 0;\n --bs-font-sans-serif: system-ui, -apple-system, \"Segoe UI\", Roboto, \"Helvetica Neue\", \"Noto Sans\", \"Liberation Sans\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n --bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));\n --bs-body-font-family: var(--bs-font-sans-serif);\n --bs-body-font-size: 1rem;\n --bs-body-font-weight: 400;\n --bs-body-line-height: 1.5;\n --bs-body-color: #212529;\n --bs-body-color-rgb: 33, 37, 41;\n --bs-body-bg: #fff;\n --bs-body-bg-rgb: 255, 255, 255;\n --bs-emphasis-color: #000;\n --bs-emphasis-color-rgb: 0, 0, 0;\n --bs-secondary-color: rgba(33, 37, 41, 0.75);\n --bs-secondary-color-rgb: 33, 37, 41;\n --bs-secondary-bg: #e9ecef;\n --bs-secondary-bg-rgb: 233, 236, 239;\n --bs-tertiary-color: rgba(33, 37, 41, 0.5);\n --bs-tertiary-color-rgb: 33, 37, 41;\n --bs-tertiary-bg: #f8f9fa;\n --bs-tertiary-bg-rgb: 248, 249, 250;\n --bs-heading-color: inherit;\n --bs-link-color: #0d6efd;\n --bs-link-color-rgb: 13, 110, 253;\n --bs-link-decoration: underline;\n --bs-link-hover-color: #0a58ca;\n --bs-link-hover-color-rgb: 10, 88, 202;\n --bs-code-color: #d63384;\n --bs-highlight-color: #212529;\n --bs-highlight-bg: #fff3cd;\n --bs-border-width: 1px;\n --bs-border-style: solid;\n --bs-border-color: #dee2e6;\n --bs-border-color-translucent: rgba(0, 0, 0, 0.175);\n --bs-border-radius: 0.375rem;\n --bs-border-radius-sm: 0.25rem;\n --bs-border-radius-lg: 0.5rem;\n --bs-border-radius-xl: 1rem;\n --bs-border-radius-xxl: 2rem;\n --bs-border-radius-2xl: var(--bs-border-radius-xxl);\n --bs-border-radius-pill: 50rem;\n --bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);\n --bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);\n --bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175);\n --bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075);\n --bs-focus-ring-width: 0.25rem;\n --bs-focus-ring-opacity: 0.25;\n --bs-focus-ring-color: rgba(13, 110, 253, 0.25);\n --bs-form-valid-color: #198754;\n --bs-form-valid-border-color: #198754;\n --bs-form-invalid-color: #dc3545;\n --bs-form-invalid-border-color: #dc3545;\n}\n\n[data-bs-theme=dark] {\n color-scheme: dark;\n --bs-body-color: #dee2e6;\n --bs-body-color-rgb: 222, 226, 230;\n --bs-body-bg: #212529;\n --bs-body-bg-rgb: 33, 37, 41;\n --bs-emphasis-color: #fff;\n --bs-emphasis-color-rgb: 255, 255, 255;\n --bs-secondary-color: rgba(222, 226, 230, 0.75);\n --bs-secondary-color-rgb: 222, 226, 230;\n --bs-secondary-bg: #343a40;\n --bs-secondary-bg-rgb: 52, 58, 64;\n --bs-tertiary-color: rgba(222, 226, 230, 0.5);\n --bs-tertiary-color-rgb: 222, 226, 230;\n --bs-tertiary-bg: #2b3035;\n --bs-tertiary-bg-rgb: 43, 48, 53;\n --bs-primary-text-emphasis: #6ea8fe;\n --bs-secondary-text-emphasis: #a7acb1;\n --bs-success-text-emphasis: #75b798;\n --bs-info-text-emphasis: #6edff6;\n --bs-warning-text-emphasis: #ffda6a;\n --bs-danger-text-emphasis: #ea868f;\n --bs-light-text-emphasis: #f8f9fa;\n --bs-dark-text-emphasis: #dee2e6;\n --bs-primary-bg-subtle: #031633;\n --bs-secondary-bg-subtle: #161719;\n --bs-success-bg-subtle: #051b11;\n --bs-info-bg-subtle: #032830;\n --bs-warning-bg-subtle: #332701;\n --bs-danger-bg-subtle: #2c0b0e;\n --bs-light-bg-subtle: #343a40;\n --bs-dark-bg-subtle: #1a1d20;\n --bs-primary-border-subtle: #084298;\n --bs-secondary-border-subtle: #41464b;\n --bs-success-border-subtle: #0f5132;\n --bs-info-border-subtle: #087990;\n --bs-warning-border-subtle: #997404;\n --bs-danger-border-subtle: #842029;\n --bs-light-border-subtle: #495057;\n --bs-dark-border-subtle: #343a40;\n --bs-heading-color: inherit;\n --bs-link-color: #6ea8fe;\n --bs-link-hover-color: #8bb9fe;\n --bs-link-color-rgb: 110, 168, 254;\n --bs-link-hover-color-rgb: 139, 185, 254;\n --bs-code-color: #e685b5;\n --bs-highlight-color: #dee2e6;\n --bs-highlight-bg: #664d03;\n --bs-border-color: #495057;\n --bs-border-color-translucent: rgba(255, 255, 255, 0.15);\n --bs-form-valid-color: #75b798;\n --bs-form-valid-border-color: #75b798;\n --bs-form-invalid-color: #ea868f;\n --bs-form-invalid-border-color: #ea868f;\n}\n\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n@media (prefers-reduced-motion: no-preference) {\n :root {\n scroll-behavior: smooth;\n }\n}\n\nbody {\n margin: 0;\n font-family: var(--bs-body-font-family);\n font-size: var(--bs-body-font-size);\n font-weight: var(--bs-body-font-weight);\n line-height: var(--bs-body-line-height);\n color: var(--bs-body-color);\n text-align: var(--bs-body-text-align);\n background-color: var(--bs-body-bg);\n -webkit-text-size-adjust: 100%;\n -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\n\nhr {\n margin: 1rem 0;\n color: inherit;\n border: 0;\n border-top: var(--bs-border-width) solid;\n opacity: 0.25;\n}\n\nh6, .h6, h5, .h5, h4, .h4, h3, .h3, h2, .h2, h1, .h1 {\n margin-top: 0;\n margin-bottom: 0.5rem;\n font-weight: 500;\n line-height: 1.2;\n color: var(--bs-heading-color);\n}\n\nh1, .h1 {\n font-size: calc(1.375rem + 1.5vw);\n}\n@media (min-width: 1200px) {\n h1, .h1 {\n font-size: 2.5rem;\n }\n}\n\nh2, .h2 {\n font-size: calc(1.325rem + 0.9vw);\n}\n@media (min-width: 1200px) {\n h2, .h2 {\n font-size: 2rem;\n }\n}\n\nh3, .h3 {\n font-size: calc(1.3rem + 0.6vw);\n}\n@media (min-width: 1200px) {\n h3, .h3 {\n font-size: 1.75rem;\n }\n}\n\nh4, .h4 {\n font-size: calc(1.275rem + 0.3vw);\n}\n@media (min-width: 1200px) {\n h4, .h4 {\n font-size: 1.5rem;\n }\n}\n\nh5, .h5 {\n font-size: 1.25rem;\n}\n\nh6, .h6 {\n font-size: 1rem;\n}\n\np {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nabbr[title] {\n -webkit-text-decoration: underline dotted;\n text-decoration: underline dotted;\n cursor: help;\n -webkit-text-decoration-skip-ink: none;\n text-decoration-skip-ink: none;\n}\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\nol,\nul {\n padding-left: 2rem;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: 700;\n}\n\ndd {\n margin-bottom: 0.5rem;\n margin-left: 0;\n}\n\nblockquote {\n margin: 0 0 1rem;\n}\n\nb,\nstrong {\n font-weight: bolder;\n}\n\nsmall, .small {\n font-size: 0.875em;\n}\n\nmark, .mark {\n padding: 0.1875em;\n color: var(--bs-highlight-color);\n background-color: var(--bs-highlight-bg);\n}\n\nsub,\nsup {\n position: relative;\n font-size: 0.75em;\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -0.25em;\n}\n\nsup {\n top: -0.5em;\n}\n\na {\n color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1));\n text-decoration: underline;\n}\na:hover {\n --bs-link-color-rgb: var(--bs-link-hover-color-rgb);\n}\n\na:not([href]):not([class]), a:not([href]):not([class]):hover {\n color: inherit;\n text-decoration: none;\n}\n\npre,\ncode,\nkbd,\nsamp {\n font-family: var(--bs-font-monospace);\n font-size: 1em;\n}\n\npre {\n display: block;\n margin-top: 0;\n margin-bottom: 1rem;\n overflow: auto;\n font-size: 0.875em;\n}\npre code {\n font-size: inherit;\n color: inherit;\n word-break: normal;\n}\n\ncode {\n font-size: 0.875em;\n color: var(--bs-code-color);\n word-wrap: break-word;\n}\na > code {\n color: inherit;\n}\n\nkbd {\n padding: 0.1875rem 0.375rem;\n font-size: 0.875em;\n color: var(--bs-body-bg);\n background-color: var(--bs-body-color);\n border-radius: 0.25rem;\n}\nkbd kbd {\n padding: 0;\n font-size: 1em;\n}\n\nfigure {\n margin: 0 0 1rem;\n}\n\nimg,\nsvg {\n vertical-align: middle;\n}\n\ntable {\n caption-side: bottom;\n border-collapse: collapse;\n}\n\ncaption {\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n color: var(--bs-secondary-color);\n text-align: left;\n}\n\nth {\n text-align: inherit;\n text-align: -webkit-match-parent;\n}\n\nthead,\ntbody,\ntfoot,\ntr,\ntd,\nth {\n border-color: inherit;\n border-style: solid;\n border-width: 0;\n}\n\nlabel {\n display: inline-block;\n}\n\nbutton {\n border-radius: 0;\n}\n\nbutton:focus:not(:focus-visible) {\n outline: 0;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0;\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\n\nbutton,\nselect {\n text-transform: none;\n}\n\n[role=button] {\n cursor: pointer;\n}\n\nselect {\n word-wrap: normal;\n}\nselect:disabled {\n opacity: 1;\n}\n\n[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator {\n display: none !important;\n}\n\nbutton,\n[type=button],\n[type=reset],\n[type=submit] {\n -webkit-appearance: button;\n}\nbutton:not(:disabled),\n[type=button]:not(:disabled),\n[type=reset]:not(:disabled),\n[type=submit]:not(:disabled) {\n cursor: pointer;\n}\n\n::-moz-focus-inner {\n padding: 0;\n border-style: none;\n}\n\ntextarea {\n resize: vertical;\n}\n\nfieldset {\n min-width: 0;\n padding: 0;\n margin: 0;\n border: 0;\n}\n\nlegend {\n float: left;\n width: 100%;\n padding: 0;\n margin-bottom: 0.5rem;\n font-size: calc(1.275rem + 0.3vw);\n line-height: inherit;\n}\n@media (min-width: 1200px) {\n legend {\n font-size: 1.5rem;\n }\n}\nlegend + * {\n clear: left;\n}\n\n::-webkit-datetime-edit-fields-wrapper,\n::-webkit-datetime-edit-text,\n::-webkit-datetime-edit-minute,\n::-webkit-datetime-edit-hour-field,\n::-webkit-datetime-edit-day-field,\n::-webkit-datetime-edit-month-field,\n::-webkit-datetime-edit-year-field {\n padding: 0;\n}\n\n::-webkit-inner-spin-button {\n height: auto;\n}\n\n[type=search] {\n -webkit-appearance: textfield;\n outline-offset: -2px;\n}\n\n/* rtl:raw:\n[type=\"tel\"],\n[type=\"url\"],\n[type=\"email\"],\n[type=\"number\"] {\n direction: ltr;\n}\n*/\n::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n::-webkit-color-swatch-wrapper {\n padding: 0;\n}\n\n::-webkit-file-upload-button {\n font: inherit;\n -webkit-appearance: button;\n}\n\n::file-selector-button {\n font: inherit;\n -webkit-appearance: button;\n}\n\noutput {\n display: inline-block;\n}\n\niframe {\n border: 0;\n}\n\nsummary {\n display: list-item;\n cursor: pointer;\n}\n\nprogress {\n vertical-align: baseline;\n}\n\n[hidden] {\n display: none !important;\n}\n\n.lead {\n font-size: 1.25rem;\n font-weight: 300;\n}\n\n.display-1 {\n font-size: calc(1.625rem + 4.5vw);\n font-weight: 300;\n line-height: 1.2;\n}\n@media (min-width: 1200px) {\n .display-1 {\n font-size: 5rem;\n }\n}\n\n.display-2 {\n font-size: calc(1.575rem + 3.9vw);\n font-weight: 300;\n line-height: 1.2;\n}\n@media (min-width: 1200px) {\n .display-2 {\n font-size: 4.5rem;\n }\n}\n\n.display-3 {\n font-size: calc(1.525rem + 3.3vw);\n font-weight: 300;\n line-height: 1.2;\n}\n@media (min-width: 1200px) {\n .display-3 {\n font-size: 4rem;\n }\n}\n\n.display-4 {\n font-size: calc(1.475rem + 2.7vw);\n font-weight: 300;\n line-height: 1.2;\n}\n@media (min-width: 1200px) {\n .display-4 {\n font-size: 3.5rem;\n }\n}\n\n.display-5 {\n font-size: calc(1.425rem + 2.1vw);\n font-weight: 300;\n line-height: 1.2;\n}\n@media (min-width: 1200px) {\n .display-5 {\n font-size: 3rem;\n }\n}\n\n.display-6 {\n font-size: calc(1.375rem + 1.5vw);\n font-weight: 300;\n line-height: 1.2;\n}\n@media (min-width: 1200px) {\n .display-6 {\n font-size: 2.5rem;\n }\n}\n\n.list-unstyled {\n padding-left: 0;\n list-style: none;\n}\n\n.list-inline {\n padding-left: 0;\n list-style: none;\n}\n\n.list-inline-item {\n display: inline-block;\n}\n.list-inline-item:not(:last-child) {\n margin-right: 0.5rem;\n}\n\n.initialism {\n font-size: 0.875em;\n text-transform: uppercase;\n}\n\n.blockquote {\n margin-bottom: 1rem;\n font-size: 1.25rem;\n}\n.blockquote > :last-child {\n margin-bottom: 0;\n}\n\n.blockquote-footer {\n margin-top: -1rem;\n margin-bottom: 1rem;\n font-size: 0.875em;\n color: #6c757d;\n}\n.blockquote-footer::before {\n content: \"— \";\n}\n\n.img-fluid {\n max-width: 100%;\n height: auto;\n}\n\n.img-thumbnail {\n padding: 0.25rem;\n background-color: var(--bs-body-bg);\n border: var(--bs-border-width) solid var(--bs-border-color);\n border-radius: var(--bs-border-radius);\n max-width: 100%;\n height: auto;\n}\n\n.figure {\n display: inline-block;\n}\n\n.figure-img {\n margin-bottom: 0.5rem;\n line-height: 1;\n}\n\n.figure-caption {\n font-size: 0.875em;\n color: var(--bs-secondary-color);\n}\n\n.container,\n.container-fluid,\n.container-xxl,\n.container-xl,\n.container-lg,\n.container-md,\n.container-sm {\n --bs-gutter-x: 1.5rem;\n --bs-gutter-y: 0;\n width: 100%;\n padding-right: calc(var(--bs-gutter-x) * 0.5);\n padding-left: calc(var(--bs-gutter-x) * 0.5);\n margin-right: auto;\n margin-left: auto;\n}\n\n@media (min-width: 576px) {\n .container-sm, .container {\n max-width: 540px;\n }\n}\n@media (min-width: 768px) {\n .container-md, .container-sm, .container {\n max-width: 720px;\n }\n}\n@media (min-width: 992px) {\n .container-lg, .container-md, .container-sm, .container {\n max-width: 960px;\n }\n}\n@media (min-width: 1200px) {\n .container-xl, .container-lg, .container-md, .container-sm, .container {\n max-width: 1140px;\n }\n}\n@media (min-width: 1400px) {\n .container-xxl, .container-xl, .container-lg, .container-md, .container-sm, .container {\n max-width: 1320px;\n }\n}\n:root {\n --bs-breakpoint-xs: 0;\n --bs-breakpoint-sm: 576px;\n --bs-breakpoint-md: 768px;\n --bs-breakpoint-lg: 992px;\n --bs-breakpoint-xl: 1200px;\n --bs-breakpoint-xxl: 1400px;\n}\n\n.row {\n --bs-gutter-x: 1.5rem;\n --bs-gutter-y: 0;\n display: flex;\n flex-wrap: wrap;\n margin-top: calc(-1 * var(--bs-gutter-y));\n margin-right: calc(-0.5 * var(--bs-gutter-x));\n margin-left: calc(-0.5 * var(--bs-gutter-x));\n}\n.row > * {\n flex-shrink: 0;\n width: 100%;\n max-width: 100%;\n padding-right: calc(var(--bs-gutter-x) * 0.5);\n padding-left: calc(var(--bs-gutter-x) * 0.5);\n margin-top: var(--bs-gutter-y);\n}\n\n.col {\n flex: 1 0 0%;\n}\n\n.row-cols-auto > * {\n flex: 0 0 auto;\n width: auto;\n}\n\n.row-cols-1 > * {\n flex: 0 0 auto;\n width: 100%;\n}\n\n.row-cols-2 > * {\n flex: 0 0 auto;\n width: 50%;\n}\n\n.row-cols-3 > * {\n flex: 0 0 auto;\n width: 33.33333333%;\n}\n\n.row-cols-4 > * {\n flex: 0 0 auto;\n width: 25%;\n}\n\n.row-cols-5 > * {\n flex: 0 0 auto;\n width: 20%;\n}\n\n.row-cols-6 > * {\n flex: 0 0 auto;\n width: 16.66666667%;\n}\n\n.col-auto {\n flex: 0 0 auto;\n width: auto;\n}\n\n.col-1 {\n flex: 0 0 auto;\n width: 8.33333333%;\n}\n\n.col-2 {\n flex: 0 0 auto;\n width: 16.66666667%;\n}\n\n.col-3 {\n flex: 0 0 auto;\n width: 25%;\n}\n\n.col-4 {\n flex: 0 0 auto;\n width: 33.33333333%;\n}\n\n.col-5 {\n flex: 0 0 auto;\n width: 41.66666667%;\n}\n\n.col-6 {\n flex: 0 0 auto;\n width: 50%;\n}\n\n.col-7 {\n flex: 0 0 auto;\n width: 58.33333333%;\n}\n\n.col-8 {\n flex: 0 0 auto;\n width: 66.66666667%;\n}\n\n.col-9 {\n flex: 0 0 auto;\n width: 75%;\n}\n\n.col-10 {\n flex: 0 0 auto;\n width: 83.33333333%;\n}\n\n.col-11 {\n flex: 0 0 auto;\n width: 91.66666667%;\n}\n\n.col-12 {\n flex: 0 0 auto;\n width: 100%;\n}\n\n.offset-1 {\n margin-left: 8.33333333%;\n}\n\n.offset-2 {\n margin-left: 16.66666667%;\n}\n\n.offset-3 {\n margin-left: 25%;\n}\n\n.offset-4 {\n margin-left: 33.33333333%;\n}\n\n.offset-5 {\n margin-left: 41.66666667%;\n}\n\n.offset-6 {\n margin-left: 50%;\n}\n\n.offset-7 {\n margin-left: 58.33333333%;\n}\n\n.offset-8 {\n margin-left: 66.66666667%;\n}\n\n.offset-9 {\n margin-left: 75%;\n}\n\n.offset-10 {\n margin-left: 83.33333333%;\n}\n\n.offset-11 {\n margin-left: 91.66666667%;\n}\n\n.g-0,\n.gx-0 {\n --bs-gutter-x: 0;\n}\n\n.g-0,\n.gy-0 {\n --bs-gutter-y: 0;\n}\n\n.g-1,\n.gx-1 {\n --bs-gutter-x: 0.25rem;\n}\n\n.g-1,\n.gy-1 {\n --bs-gutter-y: 0.25rem;\n}\n\n.g-2,\n.gx-2 {\n --bs-gutter-x: 0.5rem;\n}\n\n.g-2,\n.gy-2 {\n --bs-gutter-y: 0.5rem;\n}\n\n.g-3,\n.gx-3 {\n --bs-gutter-x: 1rem;\n}\n\n.g-3,\n.gy-3 {\n --bs-gutter-y: 1rem;\n}\n\n.g-4,\n.gx-4 {\n --bs-gutter-x: 1.5rem;\n}\n\n.g-4,\n.gy-4 {\n --bs-gutter-y: 1.5rem;\n}\n\n.g-5,\n.gx-5 {\n --bs-gutter-x: 3rem;\n}\n\n.g-5,\n.gy-5 {\n --bs-gutter-y: 3rem;\n}\n\n@media (min-width: 576px) {\n .col-sm {\n flex: 1 0 0%;\n }\n .row-cols-sm-auto > * {\n flex: 0 0 auto;\n width: auto;\n }\n .row-cols-sm-1 > * {\n flex: 0 0 auto;\n width: 100%;\n }\n .row-cols-sm-2 > * {\n flex: 0 0 auto;\n width: 50%;\n }\n .row-cols-sm-3 > * {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .row-cols-sm-4 > * {\n flex: 0 0 auto;\n width: 25%;\n }\n .row-cols-sm-5 > * {\n flex: 0 0 auto;\n width: 20%;\n }\n .row-cols-sm-6 > * {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-sm-auto {\n flex: 0 0 auto;\n width: auto;\n }\n .col-sm-1 {\n flex: 0 0 auto;\n width: 8.33333333%;\n }\n .col-sm-2 {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-sm-3 {\n flex: 0 0 auto;\n width: 25%;\n }\n .col-sm-4 {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .col-sm-5 {\n flex: 0 0 auto;\n width: 41.66666667%;\n }\n .col-sm-6 {\n flex: 0 0 auto;\n width: 50%;\n }\n .col-sm-7 {\n flex: 0 0 auto;\n width: 58.33333333%;\n }\n .col-sm-8 {\n flex: 0 0 auto;\n width: 66.66666667%;\n }\n .col-sm-9 {\n flex: 0 0 auto;\n width: 75%;\n }\n .col-sm-10 {\n flex: 0 0 auto;\n width: 83.33333333%;\n }\n .col-sm-11 {\n flex: 0 0 auto;\n width: 91.66666667%;\n }\n .col-sm-12 {\n flex: 0 0 auto;\n width: 100%;\n }\n .offset-sm-0 {\n margin-left: 0;\n }\n .offset-sm-1 {\n margin-left: 8.33333333%;\n }\n .offset-sm-2 {\n margin-left: 16.66666667%;\n }\n .offset-sm-3 {\n margin-left: 25%;\n }\n .offset-sm-4 {\n margin-left: 33.33333333%;\n }\n .offset-sm-5 {\n margin-left: 41.66666667%;\n }\n .offset-sm-6 {\n margin-left: 50%;\n }\n .offset-sm-7 {\n margin-left: 58.33333333%;\n }\n .offset-sm-8 {\n margin-left: 66.66666667%;\n }\n .offset-sm-9 {\n margin-left: 75%;\n }\n .offset-sm-10 {\n margin-left: 83.33333333%;\n }\n .offset-sm-11 {\n margin-left: 91.66666667%;\n }\n .g-sm-0,\n .gx-sm-0 {\n --bs-gutter-x: 0;\n }\n .g-sm-0,\n .gy-sm-0 {\n --bs-gutter-y: 0;\n }\n .g-sm-1,\n .gx-sm-1 {\n --bs-gutter-x: 0.25rem;\n }\n .g-sm-1,\n .gy-sm-1 {\n --bs-gutter-y: 0.25rem;\n }\n .g-sm-2,\n .gx-sm-2 {\n --bs-gutter-x: 0.5rem;\n }\n .g-sm-2,\n .gy-sm-2 {\n --bs-gutter-y: 0.5rem;\n }\n .g-sm-3,\n .gx-sm-3 {\n --bs-gutter-x: 1rem;\n }\n .g-sm-3,\n .gy-sm-3 {\n --bs-gutter-y: 1rem;\n }\n .g-sm-4,\n .gx-sm-4 {\n --bs-gutter-x: 1.5rem;\n }\n .g-sm-4,\n .gy-sm-4 {\n --bs-gutter-y: 1.5rem;\n }\n .g-sm-5,\n .gx-sm-5 {\n --bs-gutter-x: 3rem;\n }\n .g-sm-5,\n .gy-sm-5 {\n --bs-gutter-y: 3rem;\n }\n}\n@media (min-width: 768px) {\n .col-md {\n flex: 1 0 0%;\n }\n .row-cols-md-auto > * {\n flex: 0 0 auto;\n width: auto;\n }\n .row-cols-md-1 > * {\n flex: 0 0 auto;\n width: 100%;\n }\n .row-cols-md-2 > * {\n flex: 0 0 auto;\n width: 50%;\n }\n .row-cols-md-3 > * {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .row-cols-md-4 > * {\n flex: 0 0 auto;\n width: 25%;\n }\n .row-cols-md-5 > * {\n flex: 0 0 auto;\n width: 20%;\n }\n .row-cols-md-6 > * {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-md-auto {\n flex: 0 0 auto;\n width: auto;\n }\n .col-md-1 {\n flex: 0 0 auto;\n width: 8.33333333%;\n }\n .col-md-2 {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-md-3 {\n flex: 0 0 auto;\n width: 25%;\n }\n .col-md-4 {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .col-md-5 {\n flex: 0 0 auto;\n width: 41.66666667%;\n }\n .col-md-6 {\n flex: 0 0 auto;\n width: 50%;\n }\n .col-md-7 {\n flex: 0 0 auto;\n width: 58.33333333%;\n }\n .col-md-8 {\n flex: 0 0 auto;\n width: 66.66666667%;\n }\n .col-md-9 {\n flex: 0 0 auto;\n width: 75%;\n }\n .col-md-10 {\n flex: 0 0 auto;\n width: 83.33333333%;\n }\n .col-md-11 {\n flex: 0 0 auto;\n width: 91.66666667%;\n }\n .col-md-12 {\n flex: 0 0 auto;\n width: 100%;\n }\n .offset-md-0 {\n margin-left: 0;\n }\n .offset-md-1 {\n margin-left: 8.33333333%;\n }\n .offset-md-2 {\n margin-left: 16.66666667%;\n }\n .offset-md-3 {\n margin-left: 25%;\n }\n .offset-md-4 {\n margin-left: 33.33333333%;\n }\n .offset-md-5 {\n margin-left: 41.66666667%;\n }\n .offset-md-6 {\n margin-left: 50%;\n }\n .offset-md-7 {\n margin-left: 58.33333333%;\n }\n .offset-md-8 {\n margin-left: 66.66666667%;\n }\n .offset-md-9 {\n margin-left: 75%;\n }\n .offset-md-10 {\n margin-left: 83.33333333%;\n }\n .offset-md-11 {\n margin-left: 91.66666667%;\n }\n .g-md-0,\n .gx-md-0 {\n --bs-gutter-x: 0;\n }\n .g-md-0,\n .gy-md-0 {\n --bs-gutter-y: 0;\n }\n .g-md-1,\n .gx-md-1 {\n --bs-gutter-x: 0.25rem;\n }\n .g-md-1,\n .gy-md-1 {\n --bs-gutter-y: 0.25rem;\n }\n .g-md-2,\n .gx-md-2 {\n --bs-gutter-x: 0.5rem;\n }\n .g-md-2,\n .gy-md-2 {\n --bs-gutter-y: 0.5rem;\n }\n .g-md-3,\n .gx-md-3 {\n --bs-gutter-x: 1rem;\n }\n .g-md-3,\n .gy-md-3 {\n --bs-gutter-y: 1rem;\n }\n .g-md-4,\n .gx-md-4 {\n --bs-gutter-x: 1.5rem;\n }\n .g-md-4,\n .gy-md-4 {\n --bs-gutter-y: 1.5rem;\n }\n .g-md-5,\n .gx-md-5 {\n --bs-gutter-x: 3rem;\n }\n .g-md-5,\n .gy-md-5 {\n --bs-gutter-y: 3rem;\n }\n}\n@media (min-width: 992px) {\n .col-lg {\n flex: 1 0 0%;\n }\n .row-cols-lg-auto > * {\n flex: 0 0 auto;\n width: auto;\n }\n .row-cols-lg-1 > * {\n flex: 0 0 auto;\n width: 100%;\n }\n .row-cols-lg-2 > * {\n flex: 0 0 auto;\n width: 50%;\n }\n .row-cols-lg-3 > * {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .row-cols-lg-4 > * {\n flex: 0 0 auto;\n width: 25%;\n }\n .row-cols-lg-5 > * {\n flex: 0 0 auto;\n width: 20%;\n }\n .row-cols-lg-6 > * {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-lg-auto {\n flex: 0 0 auto;\n width: auto;\n }\n .col-lg-1 {\n flex: 0 0 auto;\n width: 8.33333333%;\n }\n .col-lg-2 {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-lg-3 {\n flex: 0 0 auto;\n width: 25%;\n }\n .col-lg-4 {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .col-lg-5 {\n flex: 0 0 auto;\n width: 41.66666667%;\n }\n .col-lg-6 {\n flex: 0 0 auto;\n width: 50%;\n }\n .col-lg-7 {\n flex: 0 0 auto;\n width: 58.33333333%;\n }\n .col-lg-8 {\n flex: 0 0 auto;\n width: 66.66666667%;\n }\n .col-lg-9 {\n flex: 0 0 auto;\n width: 75%;\n }\n .col-lg-10 {\n flex: 0 0 auto;\n width: 83.33333333%;\n }\n .col-lg-11 {\n flex: 0 0 auto;\n width: 91.66666667%;\n }\n .col-lg-12 {\n flex: 0 0 auto;\n width: 100%;\n }\n .offset-lg-0 {\n margin-left: 0;\n }\n .offset-lg-1 {\n margin-left: 8.33333333%;\n }\n .offset-lg-2 {\n margin-left: 16.66666667%;\n }\n .offset-lg-3 {\n margin-left: 25%;\n }\n .offset-lg-4 {\n margin-left: 33.33333333%;\n }\n .offset-lg-5 {\n margin-left: 41.66666667%;\n }\n .offset-lg-6 {\n margin-left: 50%;\n }\n .offset-lg-7 {\n margin-left: 58.33333333%;\n }\n .offset-lg-8 {\n margin-left: 66.66666667%;\n }\n .offset-lg-9 {\n margin-left: 75%;\n }\n .offset-lg-10 {\n margin-left: 83.33333333%;\n }\n .offset-lg-11 {\n margin-left: 91.66666667%;\n }\n .g-lg-0,\n .gx-lg-0 {\n --bs-gutter-x: 0;\n }\n .g-lg-0,\n .gy-lg-0 {\n --bs-gutter-y: 0;\n }\n .g-lg-1,\n .gx-lg-1 {\n --bs-gutter-x: 0.25rem;\n }\n .g-lg-1,\n .gy-lg-1 {\n --bs-gutter-y: 0.25rem;\n }\n .g-lg-2,\n .gx-lg-2 {\n --bs-gutter-x: 0.5rem;\n }\n .g-lg-2,\n .gy-lg-2 {\n --bs-gutter-y: 0.5rem;\n }\n .g-lg-3,\n .gx-lg-3 {\n --bs-gutter-x: 1rem;\n }\n .g-lg-3,\n .gy-lg-3 {\n --bs-gutter-y: 1rem;\n }\n .g-lg-4,\n .gx-lg-4 {\n --bs-gutter-x: 1.5rem;\n }\n .g-lg-4,\n .gy-lg-4 {\n --bs-gutter-y: 1.5rem;\n }\n .g-lg-5,\n .gx-lg-5 {\n --bs-gutter-x: 3rem;\n }\n .g-lg-5,\n .gy-lg-5 {\n --bs-gutter-y: 3rem;\n }\n}\n@media (min-width: 1200px) {\n .col-xl {\n flex: 1 0 0%;\n }\n .row-cols-xl-auto > * {\n flex: 0 0 auto;\n width: auto;\n }\n .row-cols-xl-1 > * {\n flex: 0 0 auto;\n width: 100%;\n }\n .row-cols-xl-2 > * {\n flex: 0 0 auto;\n width: 50%;\n }\n .row-cols-xl-3 > * {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .row-cols-xl-4 > * {\n flex: 0 0 auto;\n width: 25%;\n }\n .row-cols-xl-5 > * {\n flex: 0 0 auto;\n width: 20%;\n }\n .row-cols-xl-6 > * {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-xl-auto {\n flex: 0 0 auto;\n width: auto;\n }\n .col-xl-1 {\n flex: 0 0 auto;\n width: 8.33333333%;\n }\n .col-xl-2 {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-xl-3 {\n flex: 0 0 auto;\n width: 25%;\n }\n .col-xl-4 {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .col-xl-5 {\n flex: 0 0 auto;\n width: 41.66666667%;\n }\n .col-xl-6 {\n flex: 0 0 auto;\n width: 50%;\n }\n .col-xl-7 {\n flex: 0 0 auto;\n width: 58.33333333%;\n }\n .col-xl-8 {\n flex: 0 0 auto;\n width: 66.66666667%;\n }\n .col-xl-9 {\n flex: 0 0 auto;\n width: 75%;\n }\n .col-xl-10 {\n flex: 0 0 auto;\n width: 83.33333333%;\n }\n .col-xl-11 {\n flex: 0 0 auto;\n width: 91.66666667%;\n }\n .col-xl-12 {\n flex: 0 0 auto;\n width: 100%;\n }\n .offset-xl-0 {\n margin-left: 0;\n }\n .offset-xl-1 {\n margin-left: 8.33333333%;\n }\n .offset-xl-2 {\n margin-left: 16.66666667%;\n }\n .offset-xl-3 {\n margin-left: 25%;\n }\n .offset-xl-4 {\n margin-left: 33.33333333%;\n }\n .offset-xl-5 {\n margin-left: 41.66666667%;\n }\n .offset-xl-6 {\n margin-left: 50%;\n }\n .offset-xl-7 {\n margin-left: 58.33333333%;\n }\n .offset-xl-8 {\n margin-left: 66.66666667%;\n }\n .offset-xl-9 {\n margin-left: 75%;\n }\n .offset-xl-10 {\n margin-left: 83.33333333%;\n }\n .offset-xl-11 {\n margin-left: 91.66666667%;\n }\n .g-xl-0,\n .gx-xl-0 {\n --bs-gutter-x: 0;\n }\n .g-xl-0,\n .gy-xl-0 {\n --bs-gutter-y: 0;\n }\n .g-xl-1,\n .gx-xl-1 {\n --bs-gutter-x: 0.25rem;\n }\n .g-xl-1,\n .gy-xl-1 {\n --bs-gutter-y: 0.25rem;\n }\n .g-xl-2,\n .gx-xl-2 {\n --bs-gutter-x: 0.5rem;\n }\n .g-xl-2,\n .gy-xl-2 {\n --bs-gutter-y: 0.5rem;\n }\n .g-xl-3,\n .gx-xl-3 {\n --bs-gutter-x: 1rem;\n }\n .g-xl-3,\n .gy-xl-3 {\n --bs-gutter-y: 1rem;\n }\n .g-xl-4,\n .gx-xl-4 {\n --bs-gutter-x: 1.5rem;\n }\n .g-xl-4,\n .gy-xl-4 {\n --bs-gutter-y: 1.5rem;\n }\n .g-xl-5,\n .gx-xl-5 {\n --bs-gutter-x: 3rem;\n }\n .g-xl-5,\n .gy-xl-5 {\n --bs-gutter-y: 3rem;\n }\n}\n@media (min-width: 1400px) {\n .col-xxl {\n flex: 1 0 0%;\n }\n .row-cols-xxl-auto > * {\n flex: 0 0 auto;\n width: auto;\n }\n .row-cols-xxl-1 > * {\n flex: 0 0 auto;\n width: 100%;\n }\n .row-cols-xxl-2 > * {\n flex: 0 0 auto;\n width: 50%;\n }\n .row-cols-xxl-3 > * {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .row-cols-xxl-4 > * {\n flex: 0 0 auto;\n width: 25%;\n }\n .row-cols-xxl-5 > * {\n flex: 0 0 auto;\n width: 20%;\n }\n .row-cols-xxl-6 > * {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-xxl-auto {\n flex: 0 0 auto;\n width: auto;\n }\n .col-xxl-1 {\n flex: 0 0 auto;\n width: 8.33333333%;\n }\n .col-xxl-2 {\n flex: 0 0 auto;\n width: 16.66666667%;\n }\n .col-xxl-3 {\n flex: 0 0 auto;\n width: 25%;\n }\n .col-xxl-4 {\n flex: 0 0 auto;\n width: 33.33333333%;\n }\n .col-xxl-5 {\n flex: 0 0 auto;\n width: 41.66666667%;\n }\n .col-xxl-6 {\n flex: 0 0 auto;\n width: 50%;\n }\n .col-xxl-7 {\n flex: 0 0 auto;\n width: 58.33333333%;\n }\n .col-xxl-8 {\n flex: 0 0 auto;\n width: 66.66666667%;\n }\n .col-xxl-9 {\n flex: 0 0 auto;\n width: 75%;\n }\n .col-xxl-10 {\n flex: 0 0 auto;\n width: 83.33333333%;\n }\n .col-xxl-11 {\n flex: 0 0 auto;\n width: 91.66666667%;\n }\n .col-xxl-12 {\n flex: 0 0 auto;\n width: 100%;\n }\n .offset-xxl-0 {\n margin-left: 0;\n }\n .offset-xxl-1 {\n margin-left: 8.33333333%;\n }\n .offset-xxl-2 {\n margin-left: 16.66666667%;\n }\n .offset-xxl-3 {\n margin-left: 25%;\n }\n .offset-xxl-4 {\n margin-left: 33.33333333%;\n }\n .offset-xxl-5 {\n margin-left: 41.66666667%;\n }\n .offset-xxl-6 {\n margin-left: 50%;\n }\n .offset-xxl-7 {\n margin-left: 58.33333333%;\n }\n .offset-xxl-8 {\n margin-left: 66.66666667%;\n }\n .offset-xxl-9 {\n margin-left: 75%;\n }\n .offset-xxl-10 {\n margin-left: 83.33333333%;\n }\n .offset-xxl-11 {\n margin-left: 91.66666667%;\n }\n .g-xxl-0,\n .gx-xxl-0 {\n --bs-gutter-x: 0;\n }\n .g-xxl-0,\n .gy-xxl-0 {\n --bs-gutter-y: 0;\n }\n .g-xxl-1,\n .gx-xxl-1 {\n --bs-gutter-x: 0.25rem;\n }\n .g-xxl-1,\n .gy-xxl-1 {\n --bs-gutter-y: 0.25rem;\n }\n .g-xxl-2,\n .gx-xxl-2 {\n --bs-gutter-x: 0.5rem;\n }\n .g-xxl-2,\n .gy-xxl-2 {\n --bs-gutter-y: 0.5rem;\n }\n .g-xxl-3,\n .gx-xxl-3 {\n --bs-gutter-x: 1rem;\n }\n .g-xxl-3,\n .gy-xxl-3 {\n --bs-gutter-y: 1rem;\n }\n .g-xxl-4,\n .gx-xxl-4 {\n --bs-gutter-x: 1.5rem;\n }\n .g-xxl-4,\n .gy-xxl-4 {\n --bs-gutter-y: 1.5rem;\n }\n .g-xxl-5,\n .gx-xxl-5 {\n --bs-gutter-x: 3rem;\n }\n .g-xxl-5,\n .gy-xxl-5 {\n --bs-gutter-y: 3rem;\n }\n}\n.table {\n --bs-table-color-type: initial;\n --bs-table-bg-type: initial;\n --bs-table-color-state: initial;\n --bs-table-bg-state: initial;\n --bs-table-color: var(--bs-emphasis-color);\n --bs-table-bg: var(--bs-body-bg);\n --bs-table-border-color: var(--bs-border-color);\n --bs-table-accent-bg: transparent;\n --bs-table-striped-color: var(--bs-emphasis-color);\n --bs-table-striped-bg: rgba(var(--bs-emphasis-color-rgb), 0.05);\n --bs-table-active-color: var(--bs-emphasis-color);\n --bs-table-active-bg: rgba(var(--bs-emphasis-color-rgb), 0.1);\n --bs-table-hover-color: var(--bs-emphasis-color);\n --bs-table-hover-bg: rgba(var(--bs-emphasis-color-rgb), 0.075);\n width: 100%;\n margin-bottom: 1rem;\n vertical-align: top;\n border-color: var(--bs-table-border-color);\n}\n.table > :not(caption) > * > * {\n padding: 0.5rem 0.5rem;\n color: var(--bs-table-color-state, var(--bs-table-color-type, var(--bs-table-color)));\n background-color: var(--bs-table-bg);\n border-bottom-width: var(--bs-border-width);\n box-shadow: inset 0 0 0 9999px var(--bs-table-bg-state, var(--bs-table-bg-type, var(--bs-table-accent-bg)));\n}\n.table > tbody {\n vertical-align: inherit;\n}\n.table > thead {\n vertical-align: bottom;\n}\n\n.table-group-divider {\n border-top: calc(var(--bs-border-width) * 2) solid currentcolor;\n}\n\n.caption-top {\n caption-side: top;\n}\n\n.table-sm > :not(caption) > * > * {\n padding: 0.25rem 0.25rem;\n}\n\n.table-bordered > :not(caption) > * {\n border-width: var(--bs-border-width) 0;\n}\n.table-bordered > :not(caption) > * > * {\n border-width: 0 var(--bs-border-width);\n}\n\n.table-borderless > :not(caption) > * > * {\n border-bottom-width: 0;\n}\n.table-borderless > :not(:first-child) {\n border-top-width: 0;\n}\n\n.table-striped > tbody > tr:nth-of-type(odd) > * {\n --bs-table-color-type: var(--bs-table-striped-color);\n --bs-table-bg-type: var(--bs-table-striped-bg);\n}\n\n.table-striped-columns > :not(caption) > tr > :nth-child(even) {\n --bs-table-color-type: var(--bs-table-striped-color);\n --bs-table-bg-type: var(--bs-table-striped-bg);\n}\n\n.table-active {\n --bs-table-color-state: var(--bs-table-active-color);\n --bs-table-bg-state: var(--bs-table-active-bg);\n}\n\n.table-hover > tbody > tr:hover > * {\n --bs-table-color-state: var(--bs-table-hover-color);\n --bs-table-bg-state: var(--bs-table-hover-bg);\n}\n\n.table-primary {\n --bs-table-color: #000;\n --bs-table-bg: #cfe2ff;\n --bs-table-border-color: #a6b5cc;\n --bs-table-striped-bg: #c5d7f2;\n --bs-table-striped-color: #000;\n --bs-table-active-bg: #bacbe6;\n --bs-table-active-color: #000;\n --bs-table-hover-bg: #bfd1ec;\n --bs-table-hover-color: #000;\n color: var(--bs-table-color);\n border-color: var(--bs-table-border-color);\n}\n\n.table-secondary {\n --bs-table-color: #000;\n --bs-table-bg: #e2e3e5;\n --bs-table-border-color: #b5b6b7;\n --bs-table-striped-bg: #d7d8da;\n --bs-table-striped-color: #000;\n --bs-table-active-bg: #cbccce;\n --bs-table-active-color: #000;\n --bs-table-hover-bg: #d1d2d4;\n --bs-table-hover-color: #000;\n color: var(--bs-table-color);\n border-color: var(--bs-table-border-color);\n}\n\n.table-success {\n --bs-table-color: #000;\n --bs-table-bg: #d1e7dd;\n --bs-table-border-color: #a7b9b1;\n --bs-table-striped-bg: #c7dbd2;\n --bs-table-striped-color: #000;\n --bs-table-active-bg: #bcd0c7;\n --bs-table-active-color: #000;\n --bs-table-hover-bg: #c1d6cc;\n --bs-table-hover-color: #000;\n color: var(--bs-table-color);\n border-color: var(--bs-table-border-color);\n}\n\n.table-info {\n --bs-table-color: #000;\n --bs-table-bg: #cff4fc;\n --bs-table-border-color: #a6c3ca;\n --bs-table-striped-bg: #c5e8ef;\n --bs-table-striped-color: #000;\n --bs-table-active-bg: #badce3;\n --bs-table-active-color: #000;\n --bs-table-hover-bg: #bfe2e9;\n --bs-table-hover-color: #000;\n color: var(--bs-table-color);\n border-color: var(--bs-table-border-color);\n}\n\n.table-warning {\n --bs-table-color: #000;\n --bs-table-bg: #fff3cd;\n --bs-table-border-color: #ccc2a4;\n --bs-table-striped-bg: #f2e7c3;\n --bs-table-striped-color: #000;\n --bs-table-active-bg: #e6dbb9;\n --bs-table-active-color: #000;\n --bs-table-hover-bg: #ece1be;\n --bs-table-hover-color: #000;\n color: var(--bs-table-color);\n border-color: var(--bs-table-border-color);\n}\n\n.table-danger {\n --bs-table-color: #000;\n --bs-table-bg: #f8d7da;\n --bs-table-border-color: #c6acae;\n --bs-table-striped-bg: #eccccf;\n --bs-table-striped-color: #000;\n --bs-table-active-bg: #dfc2c4;\n --bs-table-active-color: #000;\n --bs-table-hover-bg: #e5c7ca;\n --bs-table-hover-color: #000;\n color: var(--bs-table-color);\n border-color: var(--bs-table-border-color);\n}\n\n.table-light {\n --bs-table-color: #000;\n --bs-table-bg: #f8f9fa;\n --bs-table-border-color: #c6c7c8;\n --bs-table-striped-bg: #ecedee;\n --bs-table-striped-color: #000;\n --bs-table-active-bg: #dfe0e1;\n --bs-table-active-color: #000;\n --bs-table-hover-bg: #e5e6e7;\n --bs-table-hover-color: #000;\n color: var(--bs-table-color);\n border-color: var(--bs-table-border-color);\n}\n\n.table-dark {\n --bs-table-color: #fff;\n --bs-table-bg: #212529;\n --bs-table-border-color: #4d5154;\n --bs-table-striped-bg: #2c3034;\n --bs-table-striped-color: #fff;\n --bs-table-active-bg: #373b3e;\n --bs-table-active-color: #fff;\n --bs-table-hover-bg: #323539;\n --bs-table-hover-color: #fff;\n color: var(--bs-table-color);\n border-color: var(--bs-table-border-color);\n}\n\n.table-responsive {\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n}\n\n@media (max-width: 575.98px) {\n .table-responsive-sm {\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n }\n}\n@media (max-width: 767.98px) {\n .table-responsive-md {\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n }\n}\n@media (max-width: 991.98px) {\n .table-responsive-lg {\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n }\n}\n@media (max-width: 1199.98px) {\n .table-responsive-xl {\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n }\n}\n@media (max-width: 1399.98px) {\n .table-responsive-xxl {\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n }\n}\n.form-label {\n margin-bottom: 0.5rem;\n}\n\n.col-form-label {\n padding-top: calc(0.375rem + var(--bs-border-width));\n padding-bottom: calc(0.375rem + var(--bs-border-width));\n margin-bottom: 0;\n font-size: inherit;\n line-height: 1.5;\n}\n\n.col-form-label-lg {\n padding-top: calc(0.5rem + var(--bs-border-width));\n padding-bottom: calc(0.5rem + var(--bs-border-width));\n font-size: 1.25rem;\n}\n\n.col-form-label-sm {\n padding-top: calc(0.25rem + var(--bs-border-width));\n padding-bottom: calc(0.25rem + var(--bs-border-width));\n font-size: 0.875rem;\n}\n\n.form-text {\n margin-top: 0.25rem;\n font-size: 0.875em;\n color: var(--bs-secondary-color);\n}\n\n.form-control {\n display: block;\n width: 100%;\n padding: 0.375rem 0.75rem;\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n color: var(--bs-body-color);\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n background-color: var(--bs-body-bg);\n background-clip: padding-box;\n border: var(--bs-border-width) solid var(--bs-border-color);\n border-radius: var(--bs-border-radius);\n transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n@media (prefers-reduced-motion: reduce) {\n .form-control {\n transition: none;\n }\n}\n.form-control[type=file] {\n overflow: hidden;\n}\n.form-control[type=file]:not(:disabled):not([readonly]) {\n cursor: pointer;\n}\n.form-control:focus {\n color: var(--bs-body-color);\n background-color: var(--bs-body-bg);\n border-color: #86b7fe;\n outline: 0;\n box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);\n}\n.form-control::-webkit-date-and-time-value {\n min-width: 85px;\n height: 1.5em;\n margin: 0;\n}\n.form-control::-webkit-datetime-edit {\n display: block;\n padding: 0;\n}\n.form-control::-moz-placeholder {\n color: var(--bs-secondary-color);\n opacity: 1;\n}\n.form-control::placeholder {\n color: var(--bs-secondary-color);\n opacity: 1;\n}\n.form-control:disabled {\n background-color: var(--bs-secondary-bg);\n opacity: 1;\n}\n.form-control::-webkit-file-upload-button {\n padding: 0.375rem 0.75rem;\n margin: -0.375rem -0.75rem;\n -webkit-margin-end: 0.75rem;\n margin-inline-end: 0.75rem;\n color: var(--bs-body-color);\n background-color: var(--bs-tertiary-bg);\n pointer-events: none;\n border-color: inherit;\n border-style: solid;\n border-width: 0;\n border-inline-end-width: var(--bs-border-width);\n border-radius: 0;\n -webkit-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n.form-control::file-selector-button {\n padding: 0.375rem 0.75rem;\n margin: -0.375rem -0.75rem;\n -webkit-margin-end: 0.75rem;\n margin-inline-end: 0.75rem;\n color: var(--bs-body-color);\n background-color: var(--bs-tertiary-bg);\n pointer-events: none;\n border-color: inherit;\n border-style: solid;\n border-width: 0;\n border-inline-end-width: var(--bs-border-width);\n border-radius: 0;\n transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n@media (prefers-reduced-motion: reduce) {\n .form-control::-webkit-file-upload-button {\n -webkit-transition: none;\n transition: none;\n }\n .form-control::file-selector-button {\n transition: none;\n }\n}\n.form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button {\n background-color: var(--bs-secondary-bg);\n}\n.form-control:hover:not(:disabled):not([readonly])::file-selector-button {\n background-color: var(--bs-secondary-bg);\n}\n\n.form-control-plaintext {\n display: block;\n width: 100%;\n padding: 0.375rem 0;\n margin-bottom: 0;\n line-height: 1.5;\n color: var(--bs-body-color);\n background-color: transparent;\n border: solid transparent;\n border-width: var(--bs-border-width) 0;\n}\n.form-control-plaintext:focus {\n outline: 0;\n}\n.form-control-plaintext.form-control-sm, .form-control-plaintext.form-control-lg {\n padding-right: 0;\n padding-left: 0;\n}\n\n.form-control-sm {\n min-height: calc(1.5em + 0.5rem + calc(var(--bs-border-width) * 2));\n padding: 0.25rem 0.5rem;\n font-size: 0.875rem;\n border-radius: var(--bs-border-radius-sm);\n}\n.form-control-sm::-webkit-file-upload-button {\n padding: 0.25rem 0.5rem;\n margin: -0.25rem -0.5rem;\n -webkit-margin-end: 0.5rem;\n margin-inline-end: 0.5rem;\n}\n.form-control-sm::file-selector-button {\n padding: 0.25rem 0.5rem;\n margin: -0.25rem -0.5rem;\n -webkit-margin-end: 0.5rem;\n margin-inline-end: 0.5rem;\n}\n\n.form-control-lg {\n min-height: calc(1.5em + 1rem + calc(var(--bs-border-width) * 2));\n padding: 0.5rem 1rem;\n font-size: 1.25rem;\n border-radius: var(--bs-border-radius-lg);\n}\n.form-control-lg::-webkit-file-upload-button {\n padding: 0.5rem 1rem;\n margin: -0.5rem -1rem;\n -webkit-margin-end: 1rem;\n margin-inline-end: 1rem;\n}\n.form-control-lg::file-selector-button {\n padding: 0.5rem 1rem;\n margin: -0.5rem -1rem;\n -webkit-margin-end: 1rem;\n margin-inline-end: 1rem;\n}\n\ntextarea.form-control {\n min-height: calc(1.5em + 0.75rem + calc(var(--bs-border-width) * 2));\n}\ntextarea.form-control-sm {\n min-height: calc(1.5em + 0.5rem + calc(var(--bs-border-width) * 2));\n}\ntextarea.form-control-lg {\n min-height: calc(1.5em + 1rem + calc(var(--bs-border-width) * 2));\n}\n\n.form-control-color {\n width: 3rem;\n height: calc(1.5em + 0.75rem + calc(var(--bs-border-width) * 2));\n padding: 0.375rem;\n}\n.form-control-color:not(:disabled):not([readonly]) {\n cursor: pointer;\n}\n.form-control-color::-moz-color-swatch {\n border: 0 !important;\n border-radius: var(--bs-border-radius);\n}\n.form-control-color::-webkit-color-swatch {\n border: 0 !important;\n border-radius: var(--bs-border-radius);\n}\n.form-control-color.form-control-sm {\n height: calc(1.5em + 0.5rem + calc(var(--bs-border-width) * 2));\n}\n.form-control-color.form-control-lg {\n height: calc(1.5em + 1rem + calc(var(--bs-border-width) * 2));\n}\n\n.form-select {\n --bs-form-select-bg-img: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e\");\n display: block;\n width: 100%;\n padding: 0.375rem 2.25rem 0.375rem 0.75rem;\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n color: var(--bs-body-color);\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n background-color: var(--bs-body-bg);\n background-image: var(--bs-form-select-bg-img), var(--bs-form-select-bg-icon, none);\n background-repeat: no-repeat;\n background-position: right 0.75rem center;\n background-size: 16px 12px;\n border: var(--bs-border-width) solid var(--bs-border-color);\n border-radius: var(--bs-border-radius);\n transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n@media (prefers-reduced-motion: reduce) {\n .form-select {\n transition: none;\n }\n}\n.form-select:focus {\n border-color: #86b7fe;\n outline: 0;\n box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);\n}\n.form-select[multiple], .form-select[size]:not([size=\"1\"]) {\n padding-right: 0.75rem;\n background-image: none;\n}\n.form-select:disabled {\n background-color: var(--bs-secondary-bg);\n}\n.form-select:-moz-focusring {\n color: transparent;\n text-shadow: 0 0 0 var(--bs-body-color);\n}\n\n.form-select-sm {\n padding-top: 0.25rem;\n padding-bottom: 0.25rem;\n padding-left: 0.5rem;\n font-size: 0.875rem;\n border-radius: var(--bs-border-radius-sm);\n}\n\n.form-select-lg {\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n padding-left: 1rem;\n font-size: 1.25rem;\n border-radius: var(--bs-border-radius-lg);\n}\n\n[data-bs-theme=dark] .form-select {\n --bs-form-select-bg-img: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23dee2e6' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e\");\n}\n\n.form-check {\n display: block;\n min-height: 1.5rem;\n padding-left: 1.5em;\n margin-bottom: 0.125rem;\n}\n.form-check .form-check-input {\n float: left;\n margin-left: -1.5em;\n}\n\n.form-check-reverse {\n padding-right: 1.5em;\n padding-left: 0;\n text-align: right;\n}\n.form-check-reverse .form-check-input {\n float: right;\n margin-right: -1.5em;\n margin-left: 0;\n}\n\n.form-check-input {\n --bs-form-check-bg: var(--bs-body-bg);\n flex-shrink: 0;\n width: 1em;\n height: 1em;\n margin-top: 0.25em;\n vertical-align: top;\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n background-color: var(--bs-form-check-bg);\n background-image: var(--bs-form-check-bg-image);\n background-repeat: no-repeat;\n background-position: center;\n background-size: contain;\n border: var(--bs-border-width) solid var(--bs-border-color);\n -webkit-print-color-adjust: exact;\n color-adjust: exact;\n print-color-adjust: exact;\n}\n.form-check-input[type=checkbox] {\n border-radius: 0.25em;\n}\n.form-check-input[type=radio] {\n border-radius: 50%;\n}\n.form-check-input:active {\n filter: brightness(90%);\n}\n.form-check-input:focus {\n border-color: #86b7fe;\n outline: 0;\n box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);\n}\n.form-check-input:checked {\n background-color: #0d6efd;\n border-color: #0d6efd;\n}\n.form-check-input:checked[type=checkbox] {\n --bs-form-check-bg-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e\");\n}\n.form-check-input:checked[type=radio] {\n --bs-form-check-bg-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e\");\n}\n.form-check-input[type=checkbox]:indeterminate {\n background-color: #0d6efd;\n border-color: #0d6efd;\n --bs-form-check-bg-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e\");\n}\n.form-check-input:disabled {\n pointer-events: none;\n filter: none;\n opacity: 0.5;\n}\n.form-check-input[disabled] ~ .form-check-label, .form-check-input:disabled ~ .form-check-label {\n cursor: default;\n opacity: 0.5;\n}\n\n.form-switch {\n padding-left: 2.5em;\n}\n.form-switch .form-check-input {\n --bs-form-switch-bg: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e\");\n width: 2em;\n margin-left: -2.5em;\n background-image: var(--bs-form-switch-bg);\n background-position: left center;\n border-radius: 2em;\n transition: background-position 0.15s ease-in-out;\n}\n@media (prefers-reduced-motion: reduce) {\n .form-switch .form-check-input {\n transition: none;\n }\n}\n.form-switch .form-check-input:focus {\n --bs-form-switch-bg: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%2386b7fe'/%3e%3c/svg%3e\");\n}\n.form-switch .form-check-input:checked {\n background-position: right center;\n --bs-form-switch-bg: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e\");\n}\n.form-switch.form-check-reverse {\n padding-right: 2.5em;\n padding-left: 0;\n}\n.form-switch.form-check-reverse .form-check-input {\n margin-right: -2.5em;\n margin-left: 0;\n}\n\n.form-check-inline {\n display: inline-block;\n margin-right: 1rem;\n}\n\n.btn-check {\n position: absolute;\n clip: rect(0, 0, 0, 0);\n pointer-events: none;\n}\n.btn-check[disabled] + .btn, .btn-check:disabled + .btn {\n pointer-events: none;\n filter: none;\n opacity: 0.65;\n}\n\n[data-bs-theme=dark] .form-switch .form-check-input:not(:checked):not(:focus) {\n --bs-form-switch-bg: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%28255, 255, 255, 0.25%29'/%3e%3c/svg%3e\");\n}\n\n.form-range {\n width: 100%;\n height: 1.5rem;\n padding: 0;\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n background-color: transparent;\n}\n.form-range:focus {\n outline: 0;\n}\n.form-range:focus::-webkit-slider-thumb {\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.25rem rgba(13, 110, 253, 0.25);\n}\n.form-range:focus::-moz-range-thumb {\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.25rem rgba(13, 110, 253, 0.25);\n}\n.form-range::-moz-focus-outer {\n border: 0;\n}\n.form-range::-webkit-slider-thumb {\n width: 1rem;\n height: 1rem;\n margin-top: -0.25rem;\n -webkit-appearance: none;\n appearance: none;\n background-color: #0d6efd;\n border: 0;\n border-radius: 1rem;\n -webkit-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n@media (prefers-reduced-motion: reduce) {\n .form-range::-webkit-slider-thumb {\n -webkit-transition: none;\n transition: none;\n }\n}\n.form-range::-webkit-slider-thumb:active {\n background-color: #b6d4fe;\n}\n.form-range::-webkit-slider-runnable-track {\n width: 100%;\n height: 0.5rem;\n color: transparent;\n cursor: pointer;\n background-color: var(--bs-secondary-bg);\n border-color: transparent;\n border-radius: 1rem;\n}\n.form-range::-moz-range-thumb {\n width: 1rem;\n height: 1rem;\n -moz-appearance: none;\n appearance: none;\n background-color: #0d6efd;\n border: 0;\n border-radius: 1rem;\n -moz-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n@media (prefers-reduced-motion: reduce) {\n .form-range::-moz-range-thumb {\n -moz-transition: none;\n transition: none;\n }\n}\n.form-range::-moz-range-thumb:active {\n background-color: #b6d4fe;\n}\n.form-range::-moz-range-track {\n width: 100%;\n height: 0.5rem;\n color: transparent;\n cursor: pointer;\n background-color: var(--bs-secondary-bg);\n border-color: transparent;\n border-radius: 1rem;\n}\n.form-range:disabled {\n pointer-events: none;\n}\n.form-range:disabled::-webkit-slider-thumb {\n background-color: var(--bs-secondary-color);\n}\n.form-range:disabled::-moz-range-thumb {\n background-color: var(--bs-secondary-color);\n}\n\n.form-floating {\n position: relative;\n}\n.form-floating > .form-control,\n.form-floating > .form-control-plaintext,\n.form-floating > .form-select {\n height: calc(3.5rem + calc(var(--bs-border-width) * 2));\n min-height: calc(3.5rem + calc(var(--bs-border-width) * 2));\n line-height: 1.25;\n}\n.form-floating > label {\n position: absolute;\n top: 0;\n left: 0;\n z-index: 2;\n height: 100%;\n padding: 1rem 0.75rem;\n overflow: hidden;\n text-align: start;\n text-overflow: ellipsis;\n white-space: nowrap;\n pointer-events: none;\n border: var(--bs-border-width) solid transparent;\n transform-origin: 0 0;\n transition: opacity 0.1s ease-in-out, transform 0.1s ease-in-out;\n}\n@media (prefers-reduced-motion: reduce) {\n .form-floating > label {\n transition: none;\n }\n}\n.form-floating > .form-control,\n.form-floating > .form-control-plaintext {\n padding: 1rem 0.75rem;\n}\n.form-floating > .form-control::-moz-placeholder, .form-floating > .form-control-plaintext::-moz-placeholder {\n color: transparent;\n}\n.form-floating > .form-control::placeholder,\n.form-floating > .form-control-plaintext::placeholder {\n color: transparent;\n}\n.form-floating > .form-control:not(:-moz-placeholder-shown), .form-floating > .form-control-plaintext:not(:-moz-placeholder-shown) {\n padding-top: 1.625rem;\n padding-bottom: 0.625rem;\n}\n.form-floating > .form-control:focus, .form-floating > .form-control:not(:placeholder-shown),\n.form-floating > .form-control-plaintext:focus,\n.form-floating > .form-control-plaintext:not(:placeholder-shown) {\n padding-top: 1.625rem;\n padding-bottom: 0.625rem;\n}\n.form-floating > .form-control:-webkit-autofill,\n.form-floating > .form-control-plaintext:-webkit-autofill {\n padding-top: 1.625rem;\n padding-bottom: 0.625rem;\n}\n.form-floating > .form-select {\n padding-top: 1.625rem;\n padding-bottom: 0.625rem;\n}\n.form-floating > .form-control:not(:-moz-placeholder-shown) ~ label {\n color: rgba(var(--bs-body-color-rgb), 0.65);\n transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem);\n}\n.form-floating > .form-control:focus ~ label,\n.form-floating > .form-control:not(:placeholder-shown) ~ label,\n.form-floating > .form-control-plaintext ~ label,\n.form-floating > .form-select ~ label {\n color: rgba(var(--bs-body-color-rgb), 0.65);\n transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem);\n}\n.form-floating > .form-control:not(:-moz-placeholder-shown) ~ label::after {\n position: absolute;\n inset: 1rem 0.375rem;\n z-index: -1;\n height: 1.5em;\n content: \"\";\n background-color: var(--bs-body-bg);\n border-radius: var(--bs-border-radius);\n}\n.form-floating > .form-control:focus ~ label::after,\n.form-floating > .form-control:not(:placeholder-shown) ~ label::after,\n.form-floating > .form-control-plaintext ~ label::after,\n.form-floating > .form-select ~ label::after {\n position: absolute;\n inset: 1rem 0.375rem;\n z-index: -1;\n height: 1.5em;\n content: \"\";\n background-color: var(--bs-body-bg);\n border-radius: var(--bs-border-radius);\n}\n.form-floating > .form-control:-webkit-autofill ~ label {\n color: rgba(var(--bs-body-color-rgb), 0.65);\n transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem);\n}\n.form-floating > .form-control-plaintext ~ label {\n border-width: var(--bs-border-width) 0;\n}\n.form-floating > :disabled ~ label,\n.form-floating > .form-control:disabled ~ label {\n color: #6c757d;\n}\n.form-floating > :disabled ~ label::after,\n.form-floating > .form-control:disabled ~ label::after {\n background-color: var(--bs-secondary-bg);\n}\n\n.input-group {\n position: relative;\n display: flex;\n flex-wrap: wrap;\n align-items: stretch;\n width: 100%;\n}\n.input-group > .form-control,\n.input-group > .form-select,\n.input-group > .form-floating {\n position: relative;\n flex: 1 1 auto;\n width: 1%;\n min-width: 0;\n}\n.input-group > .form-control:focus,\n.input-group > .form-select:focus,\n.input-group > .form-floating:focus-within {\n z-index: 5;\n}\n.input-group .btn {\n position: relative;\n z-index: 2;\n}\n.input-group .btn:focus {\n z-index: 5;\n}\n\n.input-group-text {\n display: flex;\n align-items: center;\n padding: 0.375rem 0.75rem;\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n color: var(--bs-body-color);\n text-align: center;\n white-space: nowrap;\n background-color: var(--bs-tertiary-bg);\n border: var(--bs-border-width) solid var(--bs-border-color);\n border-radius: var(--bs-border-radius);\n}\n\n.input-group-lg > .form-control,\n.input-group-lg > .form-select,\n.input-group-lg > .input-group-text,\n.input-group-lg > .btn {\n padding: 0.5rem 1rem;\n font-size: 1.25rem;\n border-radius: var(--bs-border-radius-lg);\n}\n\n.input-group-sm > .form-control,\n.input-group-sm > .form-select,\n.input-group-sm > .input-group-text,\n.input-group-sm > .btn {\n padding: 0.25rem 0.5rem;\n font-size: 0.875rem;\n border-radius: var(--bs-border-radius-sm);\n}\n\n.input-group-lg > .form-select,\n.input-group-sm > .form-select {\n padding-right: 3rem;\n}\n\n.input-group:not(.has-validation) > :not(:last-child):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating),\n.input-group:not(.has-validation) > .dropdown-toggle:nth-last-child(n+3),\n.input-group:not(.has-validation) > .form-floating:not(:last-child) > .form-control,\n.input-group:not(.has-validation) > .form-floating:not(:last-child) > .form-select {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n.input-group.has-validation > :nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating),\n.input-group.has-validation > .dropdown-toggle:nth-last-child(n+4),\n.input-group.has-validation > .form-floating:nth-last-child(n+3) > .form-control,\n.input-group.has-validation > .form-floating:nth-last-child(n+3) > .form-select {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n.input-group > :not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback) {\n margin-left: calc(var(--bs-border-width) * -1);\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n.input-group > .form-floating:not(:first-child) > .form-control,\n.input-group > .form-floating:not(:first-child) > .form-select {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.valid-feedback {\n display: none;\n width: 100%;\n margin-top: 0.25rem;\n font-size: 0.875em;\n color: var(--bs-form-valid-color);\n}\n\n.valid-tooltip {\n position: absolute;\n top: 100%;\n z-index: 5;\n display: none;\n max-width: 100%;\n padding: 0.25rem 0.5rem;\n margin-top: 0.1rem;\n font-size: 0.875rem;\n color: #fff;\n background-color: var(--bs-success);\n border-radius: var(--bs-border-radius);\n}\n\n.was-validated :valid ~ .valid-feedback,\n.was-validated :valid ~ .valid-tooltip,\n.is-valid ~ .valid-feedback,\n.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .form-control:valid, .form-control.is-valid {\n border-color: var(--bs-form-valid-border-color);\n padding-right: calc(1.5em + 0.75rem);\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e\");\n background-repeat: no-repeat;\n background-position: right calc(0.375em + 0.1875rem) center;\n background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);\n}\n.was-validated .form-control:valid:focus, .form-control.is-valid:focus {\n border-color: var(--bs-form-valid-border-color);\n box-shadow: 0 0 0 0.25rem rgba(var(--bs-success-rgb), 0.25);\n}\n\n.was-validated textarea.form-control:valid, textarea.form-control.is-valid {\n padding-right: calc(1.5em + 0.75rem);\n background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem);\n}\n\n.was-validated .form-select:valid, .form-select.is-valid {\n border-color: var(--bs-form-valid-border-color);\n}\n.was-validated .form-select:valid:not([multiple]):not([size]), .was-validated .form-select:valid:not([multiple])[size=\"1\"], .form-select.is-valid:not([multiple]):not([size]), .form-select.is-valid:not([multiple])[size=\"1\"] {\n --bs-form-select-bg-icon: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e\");\n padding-right: 4.125rem;\n background-position: right 0.75rem center, center right 2.25rem;\n background-size: 16px 12px, calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);\n}\n.was-validated .form-select:valid:focus, .form-select.is-valid:focus {\n border-color: var(--bs-form-valid-border-color);\n box-shadow: 0 0 0 0.25rem rgba(var(--bs-success-rgb), 0.25);\n}\n\n.was-validated .form-control-color:valid, .form-control-color.is-valid {\n width: calc(3rem + calc(1.5em + 0.75rem));\n}\n\n.was-validated .form-check-input:valid, .form-check-input.is-valid {\n border-color: var(--bs-form-valid-border-color);\n}\n.was-validated .form-check-input:valid:checked, .form-check-input.is-valid:checked {\n background-color: var(--bs-form-valid-color);\n}\n.was-validated .form-check-input:valid:focus, .form-check-input.is-valid:focus {\n box-shadow: 0 0 0 0.25rem rgba(var(--bs-success-rgb), 0.25);\n}\n.was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label {\n color: var(--bs-form-valid-color);\n}\n\n.form-check-inline .form-check-input ~ .valid-feedback {\n margin-left: 0.5em;\n}\n\n.was-validated .input-group > .form-control:not(:focus):valid, .input-group > .form-control:not(:focus).is-valid,\n.was-validated .input-group > .form-select:not(:focus):valid,\n.input-group > .form-select:not(:focus).is-valid,\n.was-validated .input-group > .form-floating:not(:focus-within):valid,\n.input-group > .form-floating:not(:focus-within).is-valid {\n z-index: 3;\n}\n\n.invalid-feedback {\n display: none;\n width: 100%;\n margin-top: 0.25rem;\n font-size: 0.875em;\n color: var(--bs-form-invalid-color);\n}\n\n.invalid-tooltip {\n position: absolute;\n top: 100%;\n z-index: 5;\n display: none;\n max-width: 100%;\n padding: 0.25rem 0.5rem;\n margin-top: 0.1rem;\n font-size: 0.875rem;\n color: #fff;\n background-color: var(--bs-danger);\n border-radius: var(--bs-border-radius);\n}\n\n.was-validated :invalid ~ .invalid-feedback,\n.was-validated :invalid ~ .invalid-tooltip,\n.is-invalid ~ .invalid-feedback,\n.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .form-control:invalid, .form-control.is-invalid {\n border-color: var(--bs-form-invalid-border-color);\n padding-right: calc(1.5em + 0.75rem);\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e\");\n background-repeat: no-repeat;\n background-position: right calc(0.375em + 0.1875rem) center;\n background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);\n}\n.was-validated .form-control:invalid:focus, .form-control.is-invalid:focus {\n border-color: var(--bs-form-invalid-border-color);\n box-shadow: 0 0 0 0.25rem rgba(var(--bs-danger-rgb), 0.25);\n}\n\n.was-validated textarea.form-control:invalid, textarea.form-control.is-invalid {\n padding-right: calc(1.5em + 0.75rem);\n background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem);\n}\n\n.was-validated .form-select:invalid, .form-select.is-invalid {\n border-color: var(--bs-form-invalid-border-color);\n}\n.was-validated .form-select:invalid:not([multiple]):not([size]), .was-validated .form-select:invalid:not([multiple])[size=\"1\"], .form-select.is-invalid:not([multiple]):not([size]), .form-select.is-invalid:not([multiple])[size=\"1\"] {\n --bs-form-select-bg-icon: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e\");\n padding-right: 4.125rem;\n background-position: right 0.75rem center, center right 2.25rem;\n background-size: 16px 12px, calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);\n}\n.was-validated .form-select:invalid:focus, .form-select.is-invalid:focus {\n border-color: var(--bs-form-invalid-border-color);\n box-shadow: 0 0 0 0.25rem rgba(var(--bs-danger-rgb), 0.25);\n}\n\n.was-validated .form-control-color:invalid, .form-control-color.is-invalid {\n width: calc(3rem + calc(1.5em + 0.75rem));\n}\n\n.was-validated .form-check-input:invalid, .form-check-input.is-invalid {\n border-color: var(--bs-form-invalid-border-color);\n}\n.was-validated .form-check-input:invalid:checked, .form-check-input.is-invalid:checked {\n background-color: var(--bs-form-invalid-color);\n}\n.was-validated .form-check-input:invalid:focus, .form-check-input.is-invalid:focus {\n box-shadow: 0 0 0 0.25rem rgba(var(--bs-danger-rgb), 0.25);\n}\n.was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label {\n color: var(--bs-form-invalid-color);\n}\n\n.form-check-inline .form-check-input ~ .invalid-feedback {\n margin-left: 0.5em;\n}\n\n.was-validated .input-group > .form-control:not(:focus):invalid, .input-group > .form-control:not(:focus).is-invalid,\n.was-validated .input-group > .form-select:not(:focus):invalid,\n.input-group > .form-select:not(:focus).is-invalid,\n.was-validated .input-group > .form-floating:not(:focus-within):invalid,\n.input-group > .form-floating:not(:focus-within).is-invalid {\n z-index: 4;\n}\n\n.btn {\n --bs-btn-padding-x: 0.75rem;\n --bs-btn-padding-y: 0.375rem;\n --bs-btn-font-family: ;\n --bs-btn-font-size: 1rem;\n --bs-btn-font-weight: 400;\n --bs-btn-line-height: 1.5;\n --bs-btn-color: var(--bs-body-color);\n --bs-btn-bg: transparent;\n --bs-btn-border-width: var(--bs-border-width);\n --bs-btn-border-color: transparent;\n --bs-btn-border-radius: var(--bs-border-radius);\n --bs-btn-hover-border-color: transparent;\n --bs-btn-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);\n --bs-btn-disabled-opacity: 0.65;\n --bs-btn-focus-box-shadow: 0 0 0 0.25rem rgba(var(--bs-btn-focus-shadow-rgb), .5);\n display: inline-block;\n padding: var(--bs-btn-padding-y) var(--bs-btn-padding-x);\n font-family: var(--bs-btn-font-family);\n font-size: var(--bs-btn-font-size);\n font-weight: var(--bs-btn-font-weight);\n line-height: var(--bs-btn-line-height);\n color: var(--bs-btn-color);\n text-align: center;\n text-decoration: none;\n vertical-align: middle;\n cursor: pointer;\n -webkit-user-select: none;\n -moz-user-select: none;\n user-select: none;\n border: var(--bs-btn-border-width) solid var(--bs-btn-border-color);\n border-radius: var(--bs-btn-border-radius);\n background-color: var(--bs-btn-bg);\n transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n@media (prefers-reduced-motion: reduce) {\n .btn {\n transition: none;\n }\n}\n.btn:hover {\n color: var(--bs-btn-hover-color);\n background-color: var(--bs-btn-hover-bg);\n border-color: var(--bs-btn-hover-border-color);\n}\n.btn-check + .btn:hover {\n color: var(--bs-btn-color);\n background-color: var(--bs-btn-bg);\n border-color: var(--bs-btn-border-color);\n}\n.btn:focus-visible {\n color: var(--bs-btn-hover-color);\n background-color: var(--bs-btn-hover-bg);\n border-color: var(--bs-btn-hover-border-color);\n outline: 0;\n box-shadow: var(--bs-btn-focus-box-shadow);\n}\n.btn-check:focus-visible + .btn {\n border-color: var(--bs-btn-hover-border-color);\n outline: 0;\n box-shadow: var(--bs-btn-focus-box-shadow);\n}\n.btn-check:checked + .btn, :not(.btn-check) + .btn:active, .btn:first-child:active, .btn.active, .btn.show {\n color: var(--bs-btn-active-color);\n background-color: var(--bs-btn-active-bg);\n border-color: var(--bs-btn-active-border-color);\n}\n.btn-check:checked + .btn:focus-visible, :not(.btn-check) + .btn:active:focus-visible, .btn:first-child:active:focus-visible, .btn.active:focus-visible, .btn.show:focus-visible {\n box-shadow: var(--bs-btn-focus-box-shadow);\n}\n.btn:disabled, .btn.disabled, fieldset:disabled .btn {\n color: var(--bs-btn-disabled-color);\n pointer-events: none;\n background-color: var(--bs-btn-disabled-bg);\n border-color: var(--bs-btn-disabled-border-color);\n opacity: var(--bs-btn-disabled-opacity);\n}\n\n.btn-primary {\n --bs-btn-color: #fff;\n --bs-btn-bg: #0d6efd;\n --bs-btn-border-color: #0d6efd;\n --bs-btn-hover-color: #fff;\n --bs-btn-hover-bg: #0b5ed7;\n --bs-btn-hover-border-color: #0a58ca;\n --bs-btn-focus-shadow-rgb: 49, 132, 253;\n --bs-btn-active-color: #fff;\n --bs-btn-active-bg: #0a58ca;\n --bs-btn-active-border-color: #0a53be;\n --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n --bs-btn-disabled-color: #fff;\n --bs-btn-disabled-bg: #0d6efd;\n --bs-btn-disabled-border-color: #0d6efd;\n}\n\n.btn-secondary {\n --bs-btn-color: #fff;\n --bs-btn-bg: #6c757d;\n --bs-btn-border-color: #6c757d;\n --bs-btn-hover-color: #fff;\n --bs-btn-hover-bg: #5c636a;\n --bs-btn-hover-border-color: #565e64;\n --bs-btn-focus-shadow-rgb: 130, 138, 145;\n --bs-btn-active-color: #fff;\n --bs-btn-active-bg: #565e64;\n --bs-btn-active-border-color: #51585e;\n --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n --bs-btn-disabled-color: #fff;\n --bs-btn-disabled-bg: #6c757d;\n --bs-btn-disabled-border-color: #6c757d;\n}\n\n.btn-success {\n --bs-btn-color: #fff;\n --bs-btn-bg: #198754;\n --bs-btn-border-color: #198754;\n --bs-btn-hover-color: #fff;\n --bs-btn-hover-bg: #157347;\n --bs-btn-hover-border-color: #146c43;\n --bs-btn-focus-shadow-rgb: 60, 153, 110;\n --bs-btn-active-color: #fff;\n --bs-btn-active-bg: #146c43;\n --bs-btn-active-border-color: #13653f;\n --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n --bs-btn-disabled-color: #fff;\n --bs-btn-disabled-bg: #198754;\n --bs-btn-disabled-border-color: #198754;\n}\n\n.btn-info {\n --bs-btn-color: #000;\n --bs-btn-bg: #0dcaf0;\n --bs-btn-border-color: #0dcaf0;\n --bs-btn-hover-color: #000;\n --bs-btn-hover-bg: #31d2f2;\n --bs-btn-hover-border-color: #25cff2;\n --bs-btn-focus-shadow-rgb: 11, 172, 204;\n --bs-btn-active-color: #000;\n --bs-btn-active-bg: #3dd5f3;\n --bs-btn-active-border-color: #25cff2;\n --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n --bs-btn-disabled-color: #000;\n --bs-btn-disabled-bg: #0dcaf0;\n --bs-btn-disabled-border-color: #0dcaf0;\n}\n\n.btn-warning {\n --bs-btn-color: #000;\n --bs-btn-bg: #ffc107;\n --bs-btn-border-color: #ffc107;\n --bs-btn-hover-color: #000;\n --bs-btn-hover-bg: #ffca2c;\n --bs-btn-hover-border-color: #ffc720;\n --bs-btn-focus-shadow-rgb: 217, 164, 6;\n --bs-btn-active-color: #000;\n --bs-btn-active-bg: #ffcd39;\n --bs-btn-active-border-color: #ffc720;\n --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n --bs-btn-disabled-color: #000;\n --bs-btn-disabled-bg: #ffc107;\n --bs-btn-disabled-border-color: #ffc107;\n}\n\n.btn-danger {\n --bs-btn-color: #fff;\n --bs-btn-bg: #dc3545;\n --bs-btn-border-color: #dc3545;\n --bs-btn-hover-color: #fff;\n --bs-btn-hover-bg: #bb2d3b;\n --bs-btn-hover-border-color: #b02a37;\n --bs-btn-focus-shadow-rgb: 225, 83, 97;\n --bs-btn-active-color: #fff;\n --bs-btn-active-bg: #b02a37;\n --bs-btn-active-border-color: #a52834;\n --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n --bs-btn-disabled-color: #fff;\n --bs-btn-disabled-bg: #dc3545;\n --bs-btn-disabled-border-color: #dc3545;\n}\n\n.btn-light {\n --bs-btn-color: #000;\n --bs-btn-bg: #f8f9fa;\n --bs-btn-border-color: #f8f9fa;\n --bs-btn-hover-color: #000;\n --bs-btn-hover-bg: #d3d4d5;\n --bs-btn-hover-border-color: #c6c7c8;\n --bs-btn-focus-shadow-rgb: 211, 212, 213;\n --bs-btn-active-color: #000;\n --bs-btn-active-bg: #c6c7c8;\n --bs-btn-active-border-color: #babbbc;\n --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n --bs-btn-disabled-color: #000;\n --bs-btn-disabled-bg: #f8f9fa;\n --bs-btn-disabled-border-color: #f8f9fa;\n}\n\n.btn-dark {\n --bs-btn-color: #fff;\n --bs-btn-bg: #212529;\n --bs-btn-border-color: #212529;\n --bs-btn-hover-color: #fff;\n --bs-btn-hover-bg: #424649;\n --bs-btn-hover-border-color: #373b3e;\n --bs-btn-focus-shadow-rgb: 66, 70, 73;\n --bs-btn-active-color: #fff;\n --bs-btn-active-bg: #4d5154;\n --bs-btn-active-border-color: #373b3e;\n --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n --bs-btn-disabled-color: #fff;\n --bs-btn-disabled-bg: #212529;\n --bs-btn-disabled-border-color: #212529;\n}\n\n.btn-outline-primary {\n --bs-btn-color: #0d6efd;\n --bs-btn-border-color: #0d6efd;\n --bs-btn-hover-color: #fff;\n --bs-btn-hover-bg: #0d6efd;\n --bs-btn-hover-border-color: #0d6efd;\n --bs-btn-focus-shadow-rgb: 13, 110, 253;\n --bs-btn-active-color: #fff;\n --bs-btn-active-bg: #0d6efd;\n --bs-btn-active-border-color: #0d6efd;\n --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n --bs-btn-disabled-color: #0d6efd;\n --bs-btn-disabled-bg: transparent;\n --bs-btn-disabled-border-color: #0d6efd;\n --bs-gradient: none;\n}\n\n.btn-outline-secondary {\n --bs-btn-color: #6c757d;\n --bs-btn-border-color: #6c757d;\n --bs-btn-hover-color: #fff;\n --bs-btn-hover-bg: #6c757d;\n --bs-btn-hover-border-color: #6c757d;\n --bs-btn-focus-shadow-rgb: 108, 117, 125;\n --bs-btn-active-color: #fff;\n --bs-btn-active-bg: #6c757d;\n --bs-btn-active-border-color: #6c757d;\n --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n --bs-btn-disabled-color: #6c757d;\n --bs-btn-disabled-bg: transparent;\n --bs-btn-disabled-border-color: #6c757d;\n --bs-gradient: none;\n}\n\n.btn-outline-success {\n --bs-btn-color: #198754;\n --bs-btn-border-color: #198754;\n --bs-btn-hover-color: #fff;\n --bs-btn-hover-bg: #198754;\n --bs-btn-hover-border-color: #198754;\n --bs-btn-focus-shadow-rgb: 25, 135, 84;\n --bs-btn-active-color: #fff;\n --bs-btn-active-bg: #198754;\n --bs-btn-active-border-color: #198754;\n --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n --bs-btn-disabled-color: #198754;\n --bs-btn-disabled-bg: transparent;\n --bs-btn-disabled-border-color: #198754;\n --bs-gradient: none;\n}\n\n.btn-outline-info {\n --bs-btn-color: #0dcaf0;\n --bs-btn-border-color: #0dcaf0;\n --bs-btn-hover-color: #000;\n --bs-btn-hover-bg: #0dcaf0;\n --bs-btn-hover-border-color: #0dcaf0;\n --bs-btn-focus-shadow-rgb: 13, 202, 240;\n --bs-btn-active-color: #000;\n --bs-btn-active-bg: #0dcaf0;\n --bs-btn-active-border-color: #0dcaf0;\n --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n --bs-btn-disabled-color: #0dcaf0;\n --bs-btn-disabled-bg: transparent;\n --bs-btn-disabled-border-color: #0dcaf0;\n --bs-gradient: none;\n}\n\n.btn-outline-warning {\n --bs-btn-color: #ffc107;\n --bs-btn-border-color: #ffc107;\n --bs-btn-hover-color: #000;\n --bs-btn-hover-bg: #ffc107;\n --bs-btn-hover-border-color: #ffc107;\n --bs-btn-focus-shadow-rgb: 255, 193, 7;\n --bs-btn-active-color: #000;\n --bs-btn-active-bg: #ffc107;\n --bs-btn-active-border-color: #ffc107;\n --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n --bs-btn-disabled-color: #ffc107;\n --bs-btn-disabled-bg: transparent;\n --bs-btn-disabled-border-color: #ffc107;\n --bs-gradient: none;\n}\n\n.btn-outline-danger {\n --bs-btn-color: #dc3545;\n --bs-btn-border-color: #dc3545;\n --bs-btn-hover-color: #fff;\n --bs-btn-hover-bg: #dc3545;\n --bs-btn-hover-border-color: #dc3545;\n --bs-btn-focus-shadow-rgb: 220, 53, 69;\n --bs-btn-active-color: #fff;\n --bs-btn-active-bg: #dc3545;\n --bs-btn-active-border-color: #dc3545;\n --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n --bs-btn-disabled-color: #dc3545;\n --bs-btn-disabled-bg: transparent;\n --bs-btn-disabled-border-color: #dc3545;\n --bs-gradient: none;\n}\n\n.btn-outline-light {\n --bs-btn-color: #f8f9fa;\n --bs-btn-border-color: #f8f9fa;\n --bs-btn-hover-color: #000;\n --bs-btn-hover-bg: #f8f9fa;\n --bs-btn-hover-border-color: #f8f9fa;\n --bs-btn-focus-shadow-rgb: 248, 249, 250;\n --bs-btn-active-color: #000;\n --bs-btn-active-bg: #f8f9fa;\n --bs-btn-active-border-color: #f8f9fa;\n --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n --bs-btn-disabled-color: #f8f9fa;\n --bs-btn-disabled-bg: transparent;\n --bs-btn-disabled-border-color: #f8f9fa;\n --bs-gradient: none;\n}\n\n.btn-outline-dark {\n --bs-btn-color: #212529;\n --bs-btn-border-color: #212529;\n --bs-btn-hover-color: #fff;\n --bs-btn-hover-bg: #212529;\n --bs-btn-hover-border-color: #212529;\n --bs-btn-focus-shadow-rgb: 33, 37, 41;\n --bs-btn-active-color: #fff;\n --bs-btn-active-bg: #212529;\n --bs-btn-active-border-color: #212529;\n --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);\n --bs-btn-disabled-color: #212529;\n --bs-btn-disabled-bg: transparent;\n --bs-btn-disabled-border-color: #212529;\n --bs-gradient: none;\n}\n\n.btn-link {\n --bs-btn-font-weight: 400;\n --bs-btn-color: var(--bs-link-color);\n --bs-btn-bg: transparent;\n --bs-btn-border-color: transparent;\n --bs-btn-hover-color: var(--bs-link-hover-color);\n --bs-btn-hover-border-color: transparent;\n --bs-btn-active-color: var(--bs-link-hover-color);\n --bs-btn-active-border-color: transparent;\n --bs-btn-disabled-color: #6c757d;\n --bs-btn-disabled-border-color: transparent;\n --bs-btn-box-shadow: 0 0 0 #000;\n --bs-btn-focus-shadow-rgb: 49, 132, 253;\n text-decoration: underline;\n}\n.btn-link:focus-visible {\n color: var(--bs-btn-color);\n}\n.btn-link:hover {\n color: var(--bs-btn-hover-color);\n}\n\n.btn-lg, .btn-group-lg > .btn {\n --bs-btn-padding-y: 0.5rem;\n --bs-btn-padding-x: 1rem;\n --bs-btn-font-size: 1.25rem;\n --bs-btn-border-radius: var(--bs-border-radius-lg);\n}\n\n.btn-sm, .btn-group-sm > .btn {\n --bs-btn-padding-y: 0.25rem;\n --bs-btn-padding-x: 0.5rem;\n --bs-btn-font-size: 0.875rem;\n --bs-btn-border-radius: var(--bs-border-radius-sm);\n}\n\n.fade {\n transition: opacity 0.15s linear;\n}\n@media (prefers-reduced-motion: reduce) {\n .fade {\n transition: none;\n }\n}\n.fade:not(.show) {\n opacity: 0;\n}\n\n.collapse:not(.show) {\n display: none;\n}\n\n.collapsing {\n height: 0;\n overflow: hidden;\n transition: height 0.35s ease;\n}\n@media (prefers-reduced-motion: reduce) {\n .collapsing {\n transition: none;\n }\n}\n.collapsing.collapse-horizontal {\n width: 0;\n height: auto;\n transition: width 0.35s ease;\n}\n@media (prefers-reduced-motion: reduce) {\n .collapsing.collapse-horizontal {\n transition: none;\n }\n}\n\n.dropup,\n.dropend,\n.dropdown,\n.dropstart,\n.dropup-center,\n.dropdown-center {\n position: relative;\n}\n\n.dropdown-toggle {\n white-space: nowrap;\n}\n.dropdown-toggle::after {\n display: inline-block;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0.3em solid;\n border-right: 0.3em solid transparent;\n border-bottom: 0;\n border-left: 0.3em solid transparent;\n}\n.dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropdown-menu {\n --bs-dropdown-zindex: 1000;\n --bs-dropdown-min-width: 10rem;\n --bs-dropdown-padding-x: 0;\n --bs-dropdown-padding-y: 0.5rem;\n --bs-dropdown-spacer: 0.125rem;\n --bs-dropdown-font-size: 1rem;\n --bs-dropdown-color: var(--bs-body-color);\n --bs-dropdown-bg: var(--bs-body-bg);\n --bs-dropdown-border-color: var(--bs-border-color-translucent);\n --bs-dropdown-border-radius: var(--bs-border-radius);\n --bs-dropdown-border-width: var(--bs-border-width);\n --bs-dropdown-inner-border-radius: calc(var(--bs-border-radius) - var(--bs-border-width));\n --bs-dropdown-divider-bg: var(--bs-border-color-translucent);\n --bs-dropdown-divider-margin-y: 0.5rem;\n --bs-dropdown-box-shadow: var(--bs-box-shadow);\n --bs-dropdown-link-color: var(--bs-body-color);\n --bs-dropdown-link-hover-color: var(--bs-body-color);\n --bs-dropdown-link-hover-bg: var(--bs-tertiary-bg);\n --bs-dropdown-link-active-color: #fff;\n --bs-dropdown-link-active-bg: #0d6efd;\n --bs-dropdown-link-disabled-color: var(--bs-tertiary-color);\n --bs-dropdown-item-padding-x: 1rem;\n --bs-dropdown-item-padding-y: 0.25rem;\n --bs-dropdown-header-color: #6c757d;\n --bs-dropdown-header-padding-x: 1rem;\n --bs-dropdown-header-padding-y: 0.5rem;\n position: absolute;\n z-index: var(--bs-dropdown-zindex);\n display: none;\n min-width: var(--bs-dropdown-min-width);\n padding: var(--bs-dropdown-padding-y) var(--bs-dropdown-padding-x);\n margin: 0;\n font-size: var(--bs-dropdown-font-size);\n color: var(--bs-dropdown-color);\n text-align: left;\n list-style: none;\n background-color: var(--bs-dropdown-bg);\n background-clip: padding-box;\n border: var(--bs-dropdown-border-width) solid var(--bs-dropdown-border-color);\n border-radius: var(--bs-dropdown-border-radius);\n}\n.dropdown-menu[data-bs-popper] {\n top: 100%;\n left: 0;\n margin-top: var(--bs-dropdown-spacer);\n}\n\n.dropdown-menu-start {\n --bs-position: start;\n}\n.dropdown-menu-start[data-bs-popper] {\n right: auto;\n left: 0;\n}\n\n.dropdown-menu-end {\n --bs-position: end;\n}\n.dropdown-menu-end[data-bs-popper] {\n right: 0;\n left: auto;\n}\n\n@media (min-width: 576px) {\n .dropdown-menu-sm-start {\n --bs-position: start;\n }\n .dropdown-menu-sm-start[data-bs-popper] {\n right: auto;\n left: 0;\n }\n .dropdown-menu-sm-end {\n --bs-position: end;\n }\n .dropdown-menu-sm-end[data-bs-popper] {\n right: 0;\n left: auto;\n }\n}\n@media (min-width: 768px) {\n .dropdown-menu-md-start {\n --bs-position: start;\n }\n .dropdown-menu-md-start[data-bs-popper] {\n right: auto;\n left: 0;\n }\n .dropdown-menu-md-end {\n --bs-position: end;\n }\n .dropdown-menu-md-end[data-bs-popper] {\n right: 0;\n left: auto;\n }\n}\n@media (min-width: 992px) {\n .dropdown-menu-lg-start {\n --bs-position: start;\n }\n .dropdown-menu-lg-start[data-bs-popper] {\n right: auto;\n left: 0;\n }\n .dropdown-menu-lg-end {\n --bs-position: end;\n }\n .dropdown-menu-lg-end[data-bs-popper] {\n right: 0;\n left: auto;\n }\n}\n@media (min-width: 1200px) {\n .dropdown-menu-xl-start {\n --bs-position: start;\n }\n .dropdown-menu-xl-start[data-bs-popper] {\n right: auto;\n left: 0;\n }\n .dropdown-menu-xl-end {\n --bs-position: end;\n }\n .dropdown-menu-xl-end[data-bs-popper] {\n right: 0;\n left: auto;\n }\n}\n@media (min-width: 1400px) {\n .dropdown-menu-xxl-start {\n --bs-position: start;\n }\n .dropdown-menu-xxl-start[data-bs-popper] {\n right: auto;\n left: 0;\n }\n .dropdown-menu-xxl-end {\n --bs-position: end;\n }\n .dropdown-menu-xxl-end[data-bs-popper] {\n right: 0;\n left: auto;\n }\n}\n.dropup .dropdown-menu[data-bs-popper] {\n top: auto;\n bottom: 100%;\n margin-top: 0;\n margin-bottom: var(--bs-dropdown-spacer);\n}\n.dropup .dropdown-toggle::after {\n display: inline-block;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0;\n border-right: 0.3em solid transparent;\n border-bottom: 0.3em solid;\n border-left: 0.3em solid transparent;\n}\n.dropup .dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropend .dropdown-menu[data-bs-popper] {\n top: 0;\n right: auto;\n left: 100%;\n margin-top: 0;\n margin-left: var(--bs-dropdown-spacer);\n}\n.dropend .dropdown-toggle::after {\n display: inline-block;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0.3em solid transparent;\n border-right: 0;\n border-bottom: 0.3em solid transparent;\n border-left: 0.3em solid;\n}\n.dropend .dropdown-toggle:empty::after {\n margin-left: 0;\n}\n.dropend .dropdown-toggle::after {\n vertical-align: 0;\n}\n\n.dropstart .dropdown-menu[data-bs-popper] {\n top: 0;\n right: 100%;\n left: auto;\n margin-top: 0;\n margin-right: var(--bs-dropdown-spacer);\n}\n.dropstart .dropdown-toggle::after {\n display: inline-block;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n}\n.dropstart .dropdown-toggle::after {\n display: none;\n}\n.dropstart .dropdown-toggle::before {\n display: inline-block;\n margin-right: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0.3em solid transparent;\n border-right: 0.3em solid;\n border-bottom: 0.3em solid transparent;\n}\n.dropstart .dropdown-toggle:empty::after {\n margin-left: 0;\n}\n.dropstart .dropdown-toggle::before {\n vertical-align: 0;\n}\n\n.dropdown-divider {\n height: 0;\n margin: var(--bs-dropdown-divider-margin-y) 0;\n overflow: hidden;\n border-top: 1px solid var(--bs-dropdown-divider-bg);\n opacity: 1;\n}\n\n.dropdown-item {\n display: block;\n width: 100%;\n padding: var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);\n clear: both;\n font-weight: 400;\n color: var(--bs-dropdown-link-color);\n text-align: inherit;\n text-decoration: none;\n white-space: nowrap;\n background-color: transparent;\n border: 0;\n border-radius: var(--bs-dropdown-item-border-radius, 0);\n}\n.dropdown-item:hover, .dropdown-item:focus {\n color: var(--bs-dropdown-link-hover-color);\n background-color: var(--bs-dropdown-link-hover-bg);\n}\n.dropdown-item.active, .dropdown-item:active {\n color: var(--bs-dropdown-link-active-color);\n text-decoration: none;\n background-color: var(--bs-dropdown-link-active-bg);\n}\n.dropdown-item.disabled, .dropdown-item:disabled {\n color: var(--bs-dropdown-link-disabled-color);\n pointer-events: none;\n background-color: transparent;\n}\n\n.dropdown-menu.show {\n display: block;\n}\n\n.dropdown-header {\n display: block;\n padding: var(--bs-dropdown-header-padding-y) var(--bs-dropdown-header-padding-x);\n margin-bottom: 0;\n font-size: 0.875rem;\n color: var(--bs-dropdown-header-color);\n white-space: nowrap;\n}\n\n.dropdown-item-text {\n display: block;\n padding: var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);\n color: var(--bs-dropdown-link-color);\n}\n\n.dropdown-menu-dark {\n --bs-dropdown-color: #dee2e6;\n --bs-dropdown-bg: #343a40;\n --bs-dropdown-border-color: var(--bs-border-color-translucent);\n --bs-dropdown-box-shadow: ;\n --bs-dropdown-link-color: #dee2e6;\n --bs-dropdown-link-hover-color: #fff;\n --bs-dropdown-divider-bg: var(--bs-border-color-translucent);\n --bs-dropdown-link-hover-bg: rgba(255, 255, 255, 0.15);\n --bs-dropdown-link-active-color: #fff;\n --bs-dropdown-link-active-bg: #0d6efd;\n --bs-dropdown-link-disabled-color: #adb5bd;\n --bs-dropdown-header-color: #adb5bd;\n}\n\n.btn-group,\n.btn-group-vertical {\n position: relative;\n display: inline-flex;\n vertical-align: middle;\n}\n.btn-group > .btn,\n.btn-group-vertical > .btn {\n position: relative;\n flex: 1 1 auto;\n}\n.btn-group > .btn-check:checked + .btn,\n.btn-group > .btn-check:focus + .btn,\n.btn-group > .btn:hover,\n.btn-group > .btn:focus,\n.btn-group > .btn:active,\n.btn-group > .btn.active,\n.btn-group-vertical > .btn-check:checked + .btn,\n.btn-group-vertical > .btn-check:focus + .btn,\n.btn-group-vertical > .btn:hover,\n.btn-group-vertical > .btn:focus,\n.btn-group-vertical > .btn:active,\n.btn-group-vertical > .btn.active {\n z-index: 1;\n}\n\n.btn-toolbar {\n display: flex;\n flex-wrap: wrap;\n justify-content: flex-start;\n}\n.btn-toolbar .input-group {\n width: auto;\n}\n\n.btn-group {\n border-radius: var(--bs-border-radius);\n}\n.btn-group > :not(.btn-check:first-child) + .btn,\n.btn-group > .btn-group:not(:first-child) {\n margin-left: calc(var(--bs-border-width) * -1);\n}\n.btn-group > .btn:not(:last-child):not(.dropdown-toggle),\n.btn-group > .btn.dropdown-toggle-split:first-child,\n.btn-group > .btn-group:not(:last-child) > .btn {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n.btn-group > .btn:nth-child(n+3),\n.btn-group > :not(.btn-check) + .btn,\n.btn-group > .btn-group:not(:first-child) > .btn {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.dropdown-toggle-split {\n padding-right: 0.5625rem;\n padding-left: 0.5625rem;\n}\n.dropdown-toggle-split::after, .dropup .dropdown-toggle-split::after, .dropend .dropdown-toggle-split::after {\n margin-left: 0;\n}\n.dropstart .dropdown-toggle-split::before {\n margin-right: 0;\n}\n\n.btn-sm + .dropdown-toggle-split, .btn-group-sm > .btn + .dropdown-toggle-split {\n padding-right: 0.375rem;\n padding-left: 0.375rem;\n}\n\n.btn-lg + .dropdown-toggle-split, .btn-group-lg > .btn + .dropdown-toggle-split {\n padding-right: 0.75rem;\n padding-left: 0.75rem;\n}\n\n.btn-group-vertical {\n flex-direction: column;\n align-items: flex-start;\n justify-content: center;\n}\n.btn-group-vertical > .btn,\n.btn-group-vertical > .btn-group {\n width: 100%;\n}\n.btn-group-vertical > .btn:not(:first-child),\n.btn-group-vertical > .btn-group:not(:first-child) {\n margin-top: calc(var(--bs-border-width) * -1);\n}\n.btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle),\n.btn-group-vertical > .btn-group:not(:last-child) > .btn {\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n.btn-group-vertical > .btn ~ .btn,\n.btn-group-vertical > .btn-group:not(:first-child) > .btn {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n\n.nav {\n --bs-nav-link-padding-x: 1rem;\n --bs-nav-link-padding-y: 0.5rem;\n --bs-nav-link-font-weight: ;\n --bs-nav-link-color: var(--bs-link-color);\n --bs-nav-link-hover-color: var(--bs-link-hover-color);\n --bs-nav-link-disabled-color: var(--bs-secondary-color);\n display: flex;\n flex-wrap: wrap;\n padding-left: 0;\n margin-bottom: 0;\n list-style: none;\n}\n\n.nav-link {\n display: block;\n padding: var(--bs-nav-link-padding-y) var(--bs-nav-link-padding-x);\n font-size: var(--bs-nav-link-font-size);\n font-weight: var(--bs-nav-link-font-weight);\n color: var(--bs-nav-link-color);\n text-decoration: none;\n background: none;\n border: 0;\n transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out;\n}\n@media (prefers-reduced-motion: reduce) {\n .nav-link {\n transition: none;\n }\n}\n.nav-link:hover, .nav-link:focus {\n color: var(--bs-nav-link-hover-color);\n}\n.nav-link:focus-visible {\n outline: 0;\n box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);\n}\n.nav-link.disabled, .nav-link:disabled {\n color: var(--bs-nav-link-disabled-color);\n pointer-events: none;\n cursor: default;\n}\n\n.nav-tabs {\n --bs-nav-tabs-border-width: var(--bs-border-width);\n --bs-nav-tabs-border-color: var(--bs-border-color);\n --bs-nav-tabs-border-radius: var(--bs-border-radius);\n --bs-nav-tabs-link-hover-border-color: var(--bs-secondary-bg) var(--bs-secondary-bg) var(--bs-border-color);\n --bs-nav-tabs-link-active-color: var(--bs-emphasis-color);\n --bs-nav-tabs-link-active-bg: var(--bs-body-bg);\n --bs-nav-tabs-link-active-border-color: var(--bs-border-color) var(--bs-border-color) var(--bs-body-bg);\n border-bottom: var(--bs-nav-tabs-border-width) solid var(--bs-nav-tabs-border-color);\n}\n.nav-tabs .nav-link {\n margin-bottom: calc(-1 * var(--bs-nav-tabs-border-width));\n border: var(--bs-nav-tabs-border-width) solid transparent;\n border-top-left-radius: var(--bs-nav-tabs-border-radius);\n border-top-right-radius: var(--bs-nav-tabs-border-radius);\n}\n.nav-tabs .nav-link:hover, .nav-tabs .nav-link:focus {\n isolation: isolate;\n border-color: var(--bs-nav-tabs-link-hover-border-color);\n}\n.nav-tabs .nav-link.active,\n.nav-tabs .nav-item.show .nav-link {\n color: var(--bs-nav-tabs-link-active-color);\n background-color: var(--bs-nav-tabs-link-active-bg);\n border-color: var(--bs-nav-tabs-link-active-border-color);\n}\n.nav-tabs .dropdown-menu {\n margin-top: calc(-1 * var(--bs-nav-tabs-border-width));\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n\n.nav-pills {\n --bs-nav-pills-border-radius: var(--bs-border-radius);\n --bs-nav-pills-link-active-color: #fff;\n --bs-nav-pills-link-active-bg: #0d6efd;\n}\n.nav-pills .nav-link {\n border-radius: var(--bs-nav-pills-border-radius);\n}\n.nav-pills .nav-link.active,\n.nav-pills .show > .nav-link {\n color: var(--bs-nav-pills-link-active-color);\n background-color: var(--bs-nav-pills-link-active-bg);\n}\n\n.nav-underline {\n --bs-nav-underline-gap: 1rem;\n --bs-nav-underline-border-width: 0.125rem;\n --bs-nav-underline-link-active-color: var(--bs-emphasis-color);\n gap: var(--bs-nav-underline-gap);\n}\n.nav-underline .nav-link {\n padding-right: 0;\n padding-left: 0;\n border-bottom: var(--bs-nav-underline-border-width) solid transparent;\n}\n.nav-underline .nav-link:hover, .nav-underline .nav-link:focus {\n border-bottom-color: currentcolor;\n}\n.nav-underline .nav-link.active,\n.nav-underline .show > .nav-link {\n font-weight: 700;\n color: var(--bs-nav-underline-link-active-color);\n border-bottom-color: currentcolor;\n}\n\n.nav-fill > .nav-link,\n.nav-fill .nav-item {\n flex: 1 1 auto;\n text-align: center;\n}\n\n.nav-justified > .nav-link,\n.nav-justified .nav-item {\n flex-basis: 0;\n flex-grow: 1;\n text-align: center;\n}\n\n.nav-fill .nav-item .nav-link,\n.nav-justified .nav-item .nav-link {\n width: 100%;\n}\n\n.tab-content > .tab-pane {\n display: none;\n}\n.tab-content > .active {\n display: block;\n}\n\n.navbar {\n --bs-navbar-padding-x: 0;\n --bs-navbar-padding-y: 0.5rem;\n --bs-navbar-color: rgba(var(--bs-emphasis-color-rgb), 0.65);\n --bs-navbar-hover-color: rgba(var(--bs-emphasis-color-rgb), 0.8);\n --bs-navbar-disabled-color: rgba(var(--bs-emphasis-color-rgb), 0.3);\n --bs-navbar-active-color: rgba(var(--bs-emphasis-color-rgb), 1);\n --bs-navbar-brand-padding-y: 0.3125rem;\n --bs-navbar-brand-margin-end: 1rem;\n --bs-navbar-brand-font-size: 1.25rem;\n --bs-navbar-brand-color: rgba(var(--bs-emphasis-color-rgb), 1);\n --bs-navbar-brand-hover-color: rgba(var(--bs-emphasis-color-rgb), 1);\n --bs-navbar-nav-link-padding-x: 0.5rem;\n --bs-navbar-toggler-padding-y: 0.25rem;\n --bs-navbar-toggler-padding-x: 0.75rem;\n --bs-navbar-toggler-font-size: 1.25rem;\n --bs-navbar-toggler-icon-bg: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%2833, 37, 41, 0.75%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e\");\n --bs-navbar-toggler-border-color: rgba(var(--bs-emphasis-color-rgb), 0.15);\n --bs-navbar-toggler-border-radius: var(--bs-border-radius);\n --bs-navbar-toggler-focus-width: 0.25rem;\n --bs-navbar-toggler-transition: box-shadow 0.15s ease-in-out;\n position: relative;\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n padding: var(--bs-navbar-padding-y) var(--bs-navbar-padding-x);\n}\n.navbar > .container,\n.navbar > .container-fluid,\n.navbar > .container-sm,\n.navbar > .container-md,\n.navbar > .container-lg,\n.navbar > .container-xl,\n.navbar > .container-xxl {\n display: flex;\n flex-wrap: inherit;\n align-items: center;\n justify-content: space-between;\n}\n.navbar-brand {\n padding-top: var(--bs-navbar-brand-padding-y);\n padding-bottom: var(--bs-navbar-brand-padding-y);\n margin-right: var(--bs-navbar-brand-margin-end);\n font-size: var(--bs-navbar-brand-font-size);\n color: var(--bs-navbar-brand-color);\n text-decoration: none;\n white-space: nowrap;\n}\n.navbar-brand:hover, .navbar-brand:focus {\n color: var(--bs-navbar-brand-hover-color);\n}\n\n.navbar-nav {\n --bs-nav-link-padding-x: 0;\n --bs-nav-link-padding-y: 0.5rem;\n --bs-nav-link-font-weight: ;\n --bs-nav-link-color: var(--bs-navbar-color);\n --bs-nav-link-hover-color: var(--bs-navbar-hover-color);\n --bs-nav-link-disabled-color: var(--bs-navbar-disabled-color);\n display: flex;\n flex-direction: column;\n padding-left: 0;\n margin-bottom: 0;\n list-style: none;\n}\n.navbar-nav .nav-link.active, .navbar-nav .nav-link.show {\n color: var(--bs-navbar-active-color);\n}\n.navbar-nav .dropdown-menu {\n position: static;\n}\n\n.navbar-text {\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n color: var(--bs-navbar-color);\n}\n.navbar-text a,\n.navbar-text a:hover,\n.navbar-text a:focus {\n color: var(--bs-navbar-active-color);\n}\n\n.navbar-collapse {\n flex-basis: 100%;\n flex-grow: 1;\n align-items: center;\n}\n\n.navbar-toggler {\n padding: var(--bs-navbar-toggler-padding-y) var(--bs-navbar-toggler-padding-x);\n font-size: var(--bs-navbar-toggler-font-size);\n line-height: 1;\n color: var(--bs-navbar-color);\n background-color: transparent;\n border: var(--bs-border-width) solid var(--bs-navbar-toggler-border-color);\n border-radius: var(--bs-navbar-toggler-border-radius);\n transition: var(--bs-navbar-toggler-transition);\n}\n@media (prefers-reduced-motion: reduce) {\n .navbar-toggler {\n transition: none;\n }\n}\n.navbar-toggler:hover {\n text-decoration: none;\n}\n.navbar-toggler:focus {\n text-decoration: none;\n outline: 0;\n box-shadow: 0 0 0 var(--bs-navbar-toggler-focus-width);\n}\n\n.navbar-toggler-icon {\n display: inline-block;\n width: 1.5em;\n height: 1.5em;\n vertical-align: middle;\n background-image: var(--bs-navbar-toggler-icon-bg);\n background-repeat: no-repeat;\n background-position: center;\n background-size: 100%;\n}\n\n.navbar-nav-scroll {\n max-height: var(--bs-scroll-height, 75vh);\n overflow-y: auto;\n}\n\n@media (min-width: 576px) {\n .navbar-expand-sm {\n flex-wrap: nowrap;\n justify-content: flex-start;\n }\n .navbar-expand-sm .navbar-nav {\n flex-direction: row;\n }\n .navbar-expand-sm .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-sm .navbar-nav .nav-link {\n padding-right: var(--bs-navbar-nav-link-padding-x);\n padding-left: var(--bs-navbar-nav-link-padding-x);\n }\n .navbar-expand-sm .navbar-nav-scroll {\n overflow: visible;\n }\n .navbar-expand-sm .navbar-collapse {\n display: flex !important;\n flex-basis: auto;\n }\n .navbar-expand-sm .navbar-toggler {\n display: none;\n }\n .navbar-expand-sm .offcanvas {\n position: static;\n z-index: auto;\n flex-grow: 1;\n width: auto !important;\n height: auto !important;\n visibility: visible !important;\n background-color: transparent !important;\n border: 0 !important;\n transform: none !important;\n transition: none;\n }\n .navbar-expand-sm .offcanvas .offcanvas-header {\n display: none;\n }\n .navbar-expand-sm .offcanvas .offcanvas-body {\n display: flex;\n flex-grow: 0;\n padding: 0;\n overflow-y: visible;\n }\n}\n@media (min-width: 768px) {\n .navbar-expand-md {\n flex-wrap: nowrap;\n justify-content: flex-start;\n }\n .navbar-expand-md .navbar-nav {\n flex-direction: row;\n }\n .navbar-expand-md .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-md .navbar-nav .nav-link {\n padding-right: var(--bs-navbar-nav-link-padding-x);\n padding-left: var(--bs-navbar-nav-link-padding-x);\n }\n .navbar-expand-md .navbar-nav-scroll {\n overflow: visible;\n }\n .navbar-expand-md .navbar-collapse {\n display: flex !important;\n flex-basis: auto;\n }\n .navbar-expand-md .navbar-toggler {\n display: none;\n }\n .navbar-expand-md .offcanvas {\n position: static;\n z-index: auto;\n flex-grow: 1;\n width: auto !important;\n height: auto !important;\n visibility: visible !important;\n background-color: transparent !important;\n border: 0 !important;\n transform: none !important;\n transition: none;\n }\n .navbar-expand-md .offcanvas .offcanvas-header {\n display: none;\n }\n .navbar-expand-md .offcanvas .offcanvas-body {\n display: flex;\n flex-grow: 0;\n padding: 0;\n overflow-y: visible;\n }\n}\n@media (min-width: 992px) {\n .navbar-expand-lg {\n flex-wrap: nowrap;\n justify-content: flex-start;\n }\n .navbar-expand-lg .navbar-nav {\n flex-direction: row;\n }\n .navbar-expand-lg .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-lg .navbar-nav .nav-link {\n padding-right: var(--bs-navbar-nav-link-padding-x);\n padding-left: var(--bs-navbar-nav-link-padding-x);\n }\n .navbar-expand-lg .navbar-nav-scroll {\n overflow: visible;\n }\n .navbar-expand-lg .navbar-collapse {\n display: flex !important;\n flex-basis: auto;\n }\n .navbar-expand-lg .navbar-toggler {\n display: none;\n }\n .navbar-expand-lg .offcanvas {\n position: static;\n z-index: auto;\n flex-grow: 1;\n width: auto !important;\n height: auto !important;\n visibility: visible !important;\n background-color: transparent !important;\n border: 0 !important;\n transform: none !important;\n transition: none;\n }\n .navbar-expand-lg .offcanvas .offcanvas-header {\n display: none;\n }\n .navbar-expand-lg .offcanvas .offcanvas-body {\n display: flex;\n flex-grow: 0;\n padding: 0;\n overflow-y: visible;\n }\n}\n@media (min-width: 1200px) {\n .navbar-expand-xl {\n flex-wrap: nowrap;\n justify-content: flex-start;\n }\n .navbar-expand-xl .navbar-nav {\n flex-direction: row;\n }\n .navbar-expand-xl .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-xl .navbar-nav .nav-link {\n padding-right: var(--bs-navbar-nav-link-padding-x);\n padding-left: var(--bs-navbar-nav-link-padding-x);\n }\n .navbar-expand-xl .navbar-nav-scroll {\n overflow: visible;\n }\n .navbar-expand-xl .navbar-collapse {\n display: flex !important;\n flex-basis: auto;\n }\n .navbar-expand-xl .navbar-toggler {\n display: none;\n }\n .navbar-expand-xl .offcanvas {\n position: static;\n z-index: auto;\n flex-grow: 1;\n width: auto !important;\n height: auto !important;\n visibility: visible !important;\n background-color: transparent !important;\n border: 0 !important;\n transform: none !important;\n transition: none;\n }\n .navbar-expand-xl .offcanvas .offcanvas-header {\n display: none;\n }\n .navbar-expand-xl .offcanvas .offcanvas-body {\n display: flex;\n flex-grow: 0;\n padding: 0;\n overflow-y: visible;\n }\n}\n@media (min-width: 1400px) {\n .navbar-expand-xxl {\n flex-wrap: nowrap;\n justify-content: flex-start;\n }\n .navbar-expand-xxl .navbar-nav {\n flex-direction: row;\n }\n .navbar-expand-xxl .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-xxl .navbar-nav .nav-link {\n padding-right: var(--bs-navbar-nav-link-padding-x);\n padding-left: var(--bs-navbar-nav-link-padding-x);\n }\n .navbar-expand-xxl .navbar-nav-scroll {\n overflow: visible;\n }\n .navbar-expand-xxl .navbar-collapse {\n display: flex !important;\n flex-basis: auto;\n }\n .navbar-expand-xxl .navbar-toggler {\n display: none;\n }\n .navbar-expand-xxl .offcanvas {\n position: static;\n z-index: auto;\n flex-grow: 1;\n width: auto !important;\n height: auto !important;\n visibility: visible !important;\n background-color: transparent !important;\n border: 0 !important;\n transform: none !important;\n transition: none;\n }\n .navbar-expand-xxl .offcanvas .offcanvas-header {\n display: none;\n }\n .navbar-expand-xxl .offcanvas .offcanvas-body {\n display: flex;\n flex-grow: 0;\n padding: 0;\n overflow-y: visible;\n }\n}\n.navbar-expand {\n flex-wrap: nowrap;\n justify-content: flex-start;\n}\n.navbar-expand .navbar-nav {\n flex-direction: row;\n}\n.navbar-expand .navbar-nav .dropdown-menu {\n position: absolute;\n}\n.navbar-expand .navbar-nav .nav-link {\n padding-right: var(--bs-navbar-nav-link-padding-x);\n padding-left: var(--bs-navbar-nav-link-padding-x);\n}\n.navbar-expand .navbar-nav-scroll {\n overflow: visible;\n}\n.navbar-expand .navbar-collapse {\n display: flex !important;\n flex-basis: auto;\n}\n.navbar-expand .navbar-toggler {\n display: none;\n}\n.navbar-expand .offcanvas {\n position: static;\n z-index: auto;\n flex-grow: 1;\n width: auto !important;\n height: auto !important;\n visibility: visible !important;\n background-color: transparent !important;\n border: 0 !important;\n transform: none !important;\n transition: none;\n}\n.navbar-expand .offcanvas .offcanvas-header {\n display: none;\n}\n.navbar-expand .offcanvas .offcanvas-body {\n display: flex;\n flex-grow: 0;\n padding: 0;\n overflow-y: visible;\n}\n\n.navbar-dark,\n.navbar[data-bs-theme=dark] {\n --bs-navbar-color: rgba(255, 255, 255, 0.55);\n --bs-navbar-hover-color: rgba(255, 255, 255, 0.75);\n --bs-navbar-disabled-color: rgba(255, 255, 255, 0.25);\n --bs-navbar-active-color: #fff;\n --bs-navbar-brand-color: #fff;\n --bs-navbar-brand-hover-color: #fff;\n --bs-navbar-toggler-border-color: rgba(255, 255, 255, 0.1);\n --bs-navbar-toggler-icon-bg: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e\");\n}\n\n[data-bs-theme=dark] .navbar-toggler-icon {\n --bs-navbar-toggler-icon-bg: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e\");\n}\n\n.card {\n --bs-card-spacer-y: 1rem;\n --bs-card-spacer-x: 1rem;\n --bs-card-title-spacer-y: 0.5rem;\n --bs-card-title-color: ;\n --bs-card-subtitle-color: ;\n --bs-card-border-width: var(--bs-border-width);\n --bs-card-border-color: var(--bs-border-color-translucent);\n --bs-card-border-radius: var(--bs-border-radius);\n --bs-card-box-shadow: ;\n --bs-card-inner-border-radius: calc(var(--bs-border-radius) - (var(--bs-border-width)));\n --bs-card-cap-padding-y: 0.5rem;\n --bs-card-cap-padding-x: 1rem;\n --bs-card-cap-bg: rgba(var(--bs-body-color-rgb), 0.03);\n --bs-card-cap-color: ;\n --bs-card-height: ;\n --bs-card-color: ;\n --bs-card-bg: var(--bs-body-bg);\n --bs-card-img-overlay-padding: 1rem;\n --bs-card-group-margin: 0.75rem;\n position: relative;\n display: flex;\n flex-direction: column;\n min-width: 0;\n height: var(--bs-card-height);\n color: var(--bs-body-color);\n word-wrap: break-word;\n background-color: var(--bs-card-bg);\n background-clip: border-box;\n border: var(--bs-card-border-width) solid var(--bs-card-border-color);\n border-radius: var(--bs-card-border-radius);\n}\n.card > hr {\n margin-right: 0;\n margin-left: 0;\n}\n.card > .list-group {\n border-top: inherit;\n border-bottom: inherit;\n}\n.card > .list-group:first-child {\n border-top-width: 0;\n border-top-left-radius: var(--bs-card-inner-border-radius);\n border-top-right-radius: var(--bs-card-inner-border-radius);\n}\n.card > .list-group:last-child {\n border-bottom-width: 0;\n border-bottom-right-radius: var(--bs-card-inner-border-radius);\n border-bottom-left-radius: var(--bs-card-inner-border-radius);\n}\n.card > .card-header + .list-group,\n.card > .list-group + .card-footer {\n border-top: 0;\n}\n\n.card-body {\n flex: 1 1 auto;\n padding: var(--bs-card-spacer-y) var(--bs-card-spacer-x);\n color: var(--bs-card-color);\n}\n\n.card-title {\n margin-bottom: var(--bs-card-title-spacer-y);\n color: var(--bs-card-title-color);\n}\n\n.card-subtitle {\n margin-top: calc(-0.5 * var(--bs-card-title-spacer-y));\n margin-bottom: 0;\n color: var(--bs-card-subtitle-color);\n}\n\n.card-text:last-child {\n margin-bottom: 0;\n}\n\n.card-link + .card-link {\n margin-left: var(--bs-card-spacer-x);\n}\n\n.card-header {\n padding: var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x);\n margin-bottom: 0;\n color: var(--bs-card-cap-color);\n background-color: var(--bs-card-cap-bg);\n border-bottom: var(--bs-card-border-width) solid var(--bs-card-border-color);\n}\n.card-header:first-child {\n border-radius: var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius) 0 0;\n}\n\n.card-footer {\n padding: var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x);\n color: var(--bs-card-cap-color);\n background-color: var(--bs-card-cap-bg);\n border-top: var(--bs-card-border-width) solid var(--bs-card-border-color);\n}\n.card-footer:last-child {\n border-radius: 0 0 var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius);\n}\n\n.card-header-tabs {\n margin-right: calc(-0.5 * var(--bs-card-cap-padding-x));\n margin-bottom: calc(-1 * var(--bs-card-cap-padding-y));\n margin-left: calc(-0.5 * var(--bs-card-cap-padding-x));\n border-bottom: 0;\n}\n.card-header-tabs .nav-link.active {\n background-color: var(--bs-card-bg);\n border-bottom-color: var(--bs-card-bg);\n}\n\n.card-header-pills {\n margin-right: calc(-0.5 * var(--bs-card-cap-padding-x));\n margin-left: calc(-0.5 * var(--bs-card-cap-padding-x));\n}\n\n.card-img-overlay {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n padding: var(--bs-card-img-overlay-padding);\n border-radius: var(--bs-card-inner-border-radius);\n}\n\n.card-img,\n.card-img-top,\n.card-img-bottom {\n width: 100%;\n}\n\n.card-img,\n.card-img-top {\n border-top-left-radius: var(--bs-card-inner-border-radius);\n border-top-right-radius: var(--bs-card-inner-border-radius);\n}\n\n.card-img,\n.card-img-bottom {\n border-bottom-right-radius: var(--bs-card-inner-border-radius);\n border-bottom-left-radius: var(--bs-card-inner-border-radius);\n}\n\n.card-group > .card {\n margin-bottom: var(--bs-card-group-margin);\n}\n@media (min-width: 576px) {\n .card-group {\n display: flex;\n flex-flow: row wrap;\n }\n .card-group > .card {\n flex: 1 0 0%;\n margin-bottom: 0;\n }\n .card-group > .card + .card {\n margin-left: 0;\n border-left: 0;\n }\n .card-group > .card:not(:last-child) {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n }\n .card-group > .card:not(:last-child) .card-img-top,\n .card-group > .card:not(:last-child) .card-header {\n border-top-right-radius: 0;\n }\n .card-group > .card:not(:last-child) .card-img-bottom,\n .card-group > .card:not(:last-child) .card-footer {\n border-bottom-right-radius: 0;\n }\n .card-group > .card:not(:first-child) {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n }\n .card-group > .card:not(:first-child) .card-img-top,\n .card-group > .card:not(:first-child) .card-header {\n border-top-left-radius: 0;\n }\n .card-group > .card:not(:first-child) .card-img-bottom,\n .card-group > .card:not(:first-child) .card-footer {\n border-bottom-left-radius: 0;\n }\n}\n\n.accordion {\n --bs-accordion-color: var(--bs-body-color);\n --bs-accordion-bg: var(--bs-body-bg);\n --bs-accordion-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, border-radius 0.15s ease;\n --bs-accordion-border-color: var(--bs-border-color);\n --bs-accordion-border-width: var(--bs-border-width);\n --bs-accordion-border-radius: var(--bs-border-radius);\n --bs-accordion-inner-border-radius: calc(var(--bs-border-radius) - (var(--bs-border-width)));\n --bs-accordion-btn-padding-x: 1.25rem;\n --bs-accordion-btn-padding-y: 1rem;\n --bs-accordion-btn-color: var(--bs-body-color);\n --bs-accordion-btn-bg: var(--bs-accordion-bg);\n --bs-accordion-btn-icon: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23212529'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e\");\n --bs-accordion-btn-icon-width: 1.25rem;\n --bs-accordion-btn-icon-transform: rotate(-180deg);\n --bs-accordion-btn-icon-transition: transform 0.2s ease-in-out;\n --bs-accordion-btn-active-icon: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23052c65'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e\");\n --bs-accordion-btn-focus-border-color: #86b7fe;\n --bs-accordion-btn-focus-box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);\n --bs-accordion-body-padding-x: 1.25rem;\n --bs-accordion-body-padding-y: 1rem;\n --bs-accordion-active-color: var(--bs-primary-text-emphasis);\n --bs-accordion-active-bg: var(--bs-primary-bg-subtle);\n}\n\n.accordion-button {\n position: relative;\n display: flex;\n align-items: center;\n width: 100%;\n padding: var(--bs-accordion-btn-padding-y) var(--bs-accordion-btn-padding-x);\n font-size: 1rem;\n color: var(--bs-accordion-btn-color);\n text-align: left;\n background-color: var(--bs-accordion-btn-bg);\n border: 0;\n border-radius: 0;\n overflow-anchor: none;\n transition: var(--bs-accordion-transition);\n}\n@media (prefers-reduced-motion: reduce) {\n .accordion-button {\n transition: none;\n }\n}\n.accordion-button:not(.collapsed) {\n color: var(--bs-accordion-active-color);\n background-color: var(--bs-accordion-active-bg);\n box-shadow: inset 0 calc(-1 * var(--bs-accordion-border-width)) 0 var(--bs-accordion-border-color);\n}\n.accordion-button:not(.collapsed)::after {\n background-image: var(--bs-accordion-btn-active-icon);\n transform: var(--bs-accordion-btn-icon-transform);\n}\n.accordion-button::after {\n flex-shrink: 0;\n width: var(--bs-accordion-btn-icon-width);\n height: var(--bs-accordion-btn-icon-width);\n margin-left: auto;\n content: \"\";\n background-image: var(--bs-accordion-btn-icon);\n background-repeat: no-repeat;\n background-size: var(--bs-accordion-btn-icon-width);\n transition: var(--bs-accordion-btn-icon-transition);\n}\n@media (prefers-reduced-motion: reduce) {\n .accordion-button::after {\n transition: none;\n }\n}\n.accordion-button:hover {\n z-index: 2;\n}\n.accordion-button:focus {\n z-index: 3;\n border-color: var(--bs-accordion-btn-focus-border-color);\n outline: 0;\n box-shadow: var(--bs-accordion-btn-focus-box-shadow);\n}\n\n.accordion-header {\n margin-bottom: 0;\n}\n\n.accordion-item {\n color: var(--bs-accordion-color);\n background-color: var(--bs-accordion-bg);\n border: var(--bs-accordion-border-width) solid var(--bs-accordion-border-color);\n}\n.accordion-item:first-of-type {\n border-top-left-radius: var(--bs-accordion-border-radius);\n border-top-right-radius: var(--bs-accordion-border-radius);\n}\n.accordion-item:first-of-type .accordion-button {\n border-top-left-radius: var(--bs-accordion-inner-border-radius);\n border-top-right-radius: var(--bs-accordion-inner-border-radius);\n}\n.accordion-item:not(:first-of-type) {\n border-top: 0;\n}\n.accordion-item:last-of-type {\n border-bottom-right-radius: var(--bs-accordion-border-radius);\n border-bottom-left-radius: var(--bs-accordion-border-radius);\n}\n.accordion-item:last-of-type .accordion-button.collapsed {\n border-bottom-right-radius: var(--bs-accordion-inner-border-radius);\n border-bottom-left-radius: var(--bs-accordion-inner-border-radius);\n}\n.accordion-item:last-of-type .accordion-collapse {\n border-bottom-right-radius: var(--bs-accordion-border-radius);\n border-bottom-left-radius: var(--bs-accordion-border-radius);\n}\n\n.accordion-body {\n padding: var(--bs-accordion-body-padding-y) var(--bs-accordion-body-padding-x);\n}\n\n.accordion-flush .accordion-collapse {\n border-width: 0;\n}\n.accordion-flush .accordion-item {\n border-right: 0;\n border-left: 0;\n border-radius: 0;\n}\n.accordion-flush .accordion-item:first-child {\n border-top: 0;\n}\n.accordion-flush .accordion-item:last-child {\n border-bottom: 0;\n}\n.accordion-flush .accordion-item .accordion-button, .accordion-flush .accordion-item .accordion-button.collapsed {\n border-radius: 0;\n}\n\n[data-bs-theme=dark] .accordion-button::after {\n --bs-accordion-btn-icon: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236ea8fe'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e\");\n --bs-accordion-btn-active-icon: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236ea8fe'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e\");\n}\n\n.breadcrumb {\n --bs-breadcrumb-padding-x: 0;\n --bs-breadcrumb-padding-y: 0;\n --bs-breadcrumb-margin-bottom: 1rem;\n --bs-breadcrumb-bg: ;\n --bs-breadcrumb-border-radius: ;\n --bs-breadcrumb-divider-color: var(--bs-secondary-color);\n --bs-breadcrumb-item-padding-x: 0.5rem;\n --bs-breadcrumb-item-active-color: var(--bs-secondary-color);\n display: flex;\n flex-wrap: wrap;\n padding: var(--bs-breadcrumb-padding-y) var(--bs-breadcrumb-padding-x);\n margin-bottom: var(--bs-breadcrumb-margin-bottom);\n font-size: var(--bs-breadcrumb-font-size);\n list-style: none;\n background-color: var(--bs-breadcrumb-bg);\n border-radius: var(--bs-breadcrumb-border-radius);\n}\n\n.breadcrumb-item + .breadcrumb-item {\n padding-left: var(--bs-breadcrumb-item-padding-x);\n}\n.breadcrumb-item + .breadcrumb-item::before {\n float: left;\n padding-right: var(--bs-breadcrumb-item-padding-x);\n color: var(--bs-breadcrumb-divider-color);\n content: var(--bs-breadcrumb-divider, \"/\") /* rtl: var(--bs-breadcrumb-divider, \"/\") */;\n}\n.breadcrumb-item.active {\n color: var(--bs-breadcrumb-item-active-color);\n}\n\n.pagination {\n --bs-pagination-padding-x: 0.75rem;\n --bs-pagination-padding-y: 0.375rem;\n --bs-pagination-font-size: 1rem;\n --bs-pagination-color: var(--bs-link-color);\n --bs-pagination-bg: var(--bs-body-bg);\n --bs-pagination-border-width: var(--bs-border-width);\n --bs-pagination-border-color: var(--bs-border-color);\n --bs-pagination-border-radius: var(--bs-border-radius);\n --bs-pagination-hover-color: var(--bs-link-hover-color);\n --bs-pagination-hover-bg: var(--bs-tertiary-bg);\n --bs-pagination-hover-border-color: var(--bs-border-color);\n --bs-pagination-focus-color: var(--bs-link-hover-color);\n --bs-pagination-focus-bg: var(--bs-secondary-bg);\n --bs-pagination-focus-box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);\n --bs-pagination-active-color: #fff;\n --bs-pagination-active-bg: #0d6efd;\n --bs-pagination-active-border-color: #0d6efd;\n --bs-pagination-disabled-color: var(--bs-secondary-color);\n --bs-pagination-disabled-bg: var(--bs-secondary-bg);\n --bs-pagination-disabled-border-color: var(--bs-border-color);\n display: flex;\n padding-left: 0;\n list-style: none;\n}\n\n.page-link {\n position: relative;\n display: block;\n padding: var(--bs-pagination-padding-y) var(--bs-pagination-padding-x);\n font-size: var(--bs-pagination-font-size);\n color: var(--bs-pagination-color);\n text-decoration: none;\n background-color: var(--bs-pagination-bg);\n border: var(--bs-pagination-border-width) solid var(--bs-pagination-border-color);\n transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n@media (prefers-reduced-motion: reduce) {\n .page-link {\n transition: none;\n }\n}\n.page-link:hover {\n z-index: 2;\n color: var(--bs-pagination-hover-color);\n background-color: var(--bs-pagination-hover-bg);\n border-color: var(--bs-pagination-hover-border-color);\n}\n.page-link:focus {\n z-index: 3;\n color: var(--bs-pagination-focus-color);\n background-color: var(--bs-pagination-focus-bg);\n outline: 0;\n box-shadow: var(--bs-pagination-focus-box-shadow);\n}\n.page-link.active, .active > .page-link {\n z-index: 3;\n color: var(--bs-pagination-active-color);\n background-color: var(--bs-pagination-active-bg);\n border-color: var(--bs-pagination-active-border-color);\n}\n.page-link.disabled, .disabled > .page-link {\n color: var(--bs-pagination-disabled-color);\n pointer-events: none;\n background-color: var(--bs-pagination-disabled-bg);\n border-color: var(--bs-pagination-disabled-border-color);\n}\n\n.page-item:not(:first-child) .page-link {\n margin-left: calc(var(--bs-border-width) * -1);\n}\n.page-item:first-child .page-link {\n border-top-left-radius: var(--bs-pagination-border-radius);\n border-bottom-left-radius: var(--bs-pagination-border-radius);\n}\n.page-item:last-child .page-link {\n border-top-right-radius: var(--bs-pagination-border-radius);\n border-bottom-right-radius: var(--bs-pagination-border-radius);\n}\n\n.pagination-lg {\n --bs-pagination-padding-x: 1.5rem;\n --bs-pagination-padding-y: 0.75rem;\n --bs-pagination-font-size: 1.25rem;\n --bs-pagination-border-radius: var(--bs-border-radius-lg);\n}\n\n.pagination-sm {\n --bs-pagination-padding-x: 0.5rem;\n --bs-pagination-padding-y: 0.25rem;\n --bs-pagination-font-size: 0.875rem;\n --bs-pagination-border-radius: var(--bs-border-radius-sm);\n}\n\n.badge {\n --bs-badge-padding-x: 0.65em;\n --bs-badge-padding-y: 0.35em;\n --bs-badge-font-size: 0.75em;\n --bs-badge-font-weight: 700;\n --bs-badge-color: #fff;\n --bs-badge-border-radius: var(--bs-border-radius);\n display: inline-block;\n padding: var(--bs-badge-padding-y) var(--bs-badge-padding-x);\n font-size: var(--bs-badge-font-size);\n font-weight: var(--bs-badge-font-weight);\n line-height: 1;\n color: var(--bs-badge-color);\n text-align: center;\n white-space: nowrap;\n vertical-align: baseline;\n border-radius: var(--bs-badge-border-radius);\n}\n.badge:empty {\n display: none;\n}\n\n.btn .badge {\n position: relative;\n top: -1px;\n}\n\n.alert {\n --bs-alert-bg: transparent;\n --bs-alert-padding-x: 1rem;\n --bs-alert-padding-y: 1rem;\n --bs-alert-margin-bottom: 1rem;\n --bs-alert-color: inherit;\n --bs-alert-border-color: transparent;\n --bs-alert-border: var(--bs-border-width) solid var(--bs-alert-border-color);\n --bs-alert-border-radius: var(--bs-border-radius);\n --bs-alert-link-color: inherit;\n position: relative;\n padding: var(--bs-alert-padding-y) var(--bs-alert-padding-x);\n margin-bottom: var(--bs-alert-margin-bottom);\n color: var(--bs-alert-color);\n background-color: var(--bs-alert-bg);\n border: var(--bs-alert-border);\n border-radius: var(--bs-alert-border-radius);\n}\n\n.alert-heading {\n color: inherit;\n}\n\n.alert-link {\n font-weight: 700;\n color: var(--bs-alert-link-color);\n}\n\n.alert-dismissible {\n padding-right: 3rem;\n}\n.alert-dismissible .btn-close {\n position: absolute;\n top: 0;\n right: 0;\n z-index: 2;\n padding: 1.25rem 1rem;\n}\n\n.alert-primary {\n --bs-alert-color: var(--bs-primary-text-emphasis);\n --bs-alert-bg: var(--bs-primary-bg-subtle);\n --bs-alert-border-color: var(--bs-primary-border-subtle);\n --bs-alert-link-color: var(--bs-primary-text-emphasis);\n}\n\n.alert-secondary {\n --bs-alert-color: var(--bs-secondary-text-emphasis);\n --bs-alert-bg: var(--bs-secondary-bg-subtle);\n --bs-alert-border-color: var(--bs-secondary-border-subtle);\n --bs-alert-link-color: var(--bs-secondary-text-emphasis);\n}\n\n.alert-success {\n --bs-alert-color: var(--bs-success-text-emphasis);\n --bs-alert-bg: var(--bs-success-bg-subtle);\n --bs-alert-border-color: var(--bs-success-border-subtle);\n --bs-alert-link-color: var(--bs-success-text-emphasis);\n}\n\n.alert-info {\n --bs-alert-color: var(--bs-info-text-emphasis);\n --bs-alert-bg: var(--bs-info-bg-subtle);\n --bs-alert-border-color: var(--bs-info-border-subtle);\n --bs-alert-link-color: var(--bs-info-text-emphasis);\n}\n\n.alert-warning {\n --bs-alert-color: var(--bs-warning-text-emphasis);\n --bs-alert-bg: var(--bs-warning-bg-subtle);\n --bs-alert-border-color: var(--bs-warning-border-subtle);\n --bs-alert-link-color: var(--bs-warning-text-emphasis);\n}\n\n.alert-danger {\n --bs-alert-color: var(--bs-danger-text-emphasis);\n --bs-alert-bg: var(--bs-danger-bg-subtle);\n --bs-alert-border-color: var(--bs-danger-border-subtle);\n --bs-alert-link-color: var(--bs-danger-text-emphasis);\n}\n\n.alert-light {\n --bs-alert-color: var(--bs-light-text-emphasis);\n --bs-alert-bg: var(--bs-light-bg-subtle);\n --bs-alert-border-color: var(--bs-light-border-subtle);\n --bs-alert-link-color: var(--bs-light-text-emphasis);\n}\n\n.alert-dark {\n --bs-alert-color: var(--bs-dark-text-emphasis);\n --bs-alert-bg: var(--bs-dark-bg-subtle);\n --bs-alert-border-color: var(--bs-dark-border-subtle);\n --bs-alert-link-color: var(--bs-dark-text-emphasis);\n}\n\n@keyframes progress-bar-stripes {\n 0% {\n background-position-x: 1rem;\n }\n}\n.progress,\n.progress-stacked {\n --bs-progress-height: 1rem;\n --bs-progress-font-size: 0.75rem;\n --bs-progress-bg: var(--bs-secondary-bg);\n --bs-progress-border-radius: var(--bs-border-radius);\n --bs-progress-box-shadow: var(--bs-box-shadow-inset);\n --bs-progress-bar-color: #fff;\n --bs-progress-bar-bg: #0d6efd;\n --bs-progress-bar-transition: width 0.6s ease;\n display: flex;\n height: var(--bs-progress-height);\n overflow: hidden;\n font-size: var(--bs-progress-font-size);\n background-color: var(--bs-progress-bg);\n border-radius: var(--bs-progress-border-radius);\n}\n\n.progress-bar {\n display: flex;\n flex-direction: column;\n justify-content: center;\n overflow: hidden;\n color: var(--bs-progress-bar-color);\n text-align: center;\n white-space: nowrap;\n background-color: var(--bs-progress-bar-bg);\n transition: var(--bs-progress-bar-transition);\n}\n@media (prefers-reduced-motion: reduce) {\n .progress-bar {\n transition: none;\n }\n}\n\n.progress-bar-striped {\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-size: var(--bs-progress-height) var(--bs-progress-height);\n}\n\n.progress-stacked > .progress {\n overflow: visible;\n}\n\n.progress-stacked > .progress > .progress-bar {\n width: 100%;\n}\n\n.progress-bar-animated {\n animation: 1s linear infinite progress-bar-stripes;\n}\n@media (prefers-reduced-motion: reduce) {\n .progress-bar-animated {\n animation: none;\n }\n}\n\n.list-group {\n --bs-list-group-color: var(--bs-body-color);\n --bs-list-group-bg: var(--bs-body-bg);\n --bs-list-group-border-color: var(--bs-border-color);\n --bs-list-group-border-width: var(--bs-border-width);\n --bs-list-group-border-radius: var(--bs-border-radius);\n --bs-list-group-item-padding-x: 1rem;\n --bs-list-group-item-padding-y: 0.5rem;\n --bs-list-group-action-color: var(--bs-secondary-color);\n --bs-list-group-action-hover-color: var(--bs-emphasis-color);\n --bs-list-group-action-hover-bg: var(--bs-tertiary-bg);\n --bs-list-group-action-active-color: var(--bs-body-color);\n --bs-list-group-action-active-bg: var(--bs-secondary-bg);\n --bs-list-group-disabled-color: var(--bs-secondary-color);\n --bs-list-group-disabled-bg: var(--bs-body-bg);\n --bs-list-group-active-color: #fff;\n --bs-list-group-active-bg: #0d6efd;\n --bs-list-group-active-border-color: #0d6efd;\n display: flex;\n flex-direction: column;\n padding-left: 0;\n margin-bottom: 0;\n border-radius: var(--bs-list-group-border-radius);\n}\n\n.list-group-numbered {\n list-style-type: none;\n counter-reset: section;\n}\n.list-group-numbered > .list-group-item::before {\n content: counters(section, \".\") \". \";\n counter-increment: section;\n}\n\n.list-group-item-action {\n width: 100%;\n color: var(--bs-list-group-action-color);\n text-align: inherit;\n}\n.list-group-item-action:hover, .list-group-item-action:focus {\n z-index: 1;\n color: var(--bs-list-group-action-hover-color);\n text-decoration: none;\n background-color: var(--bs-list-group-action-hover-bg);\n}\n.list-group-item-action:active {\n color: var(--bs-list-group-action-active-color);\n background-color: var(--bs-list-group-action-active-bg);\n}\n\n.list-group-item {\n position: relative;\n display: block;\n padding: var(--bs-list-group-item-padding-y) var(--bs-list-group-item-padding-x);\n color: var(--bs-list-group-color);\n text-decoration: none;\n background-color: var(--bs-list-group-bg);\n border: var(--bs-list-group-border-width) solid var(--bs-list-group-border-color);\n}\n.list-group-item:first-child {\n border-top-left-radius: inherit;\n border-top-right-radius: inherit;\n}\n.list-group-item:last-child {\n border-bottom-right-radius: inherit;\n border-bottom-left-radius: inherit;\n}\n.list-group-item.disabled, .list-group-item:disabled {\n color: var(--bs-list-group-disabled-color);\n pointer-events: none;\n background-color: var(--bs-list-group-disabled-bg);\n}\n.list-group-item.active {\n z-index: 2;\n color: var(--bs-list-group-active-color);\n background-color: var(--bs-list-group-active-bg);\n border-color: var(--bs-list-group-active-border-color);\n}\n.list-group-item + .list-group-item {\n border-top-width: 0;\n}\n.list-group-item + .list-group-item.active {\n margin-top: calc(-1 * var(--bs-list-group-border-width));\n border-top-width: var(--bs-list-group-border-width);\n}\n\n.list-group-horizontal {\n flex-direction: row;\n}\n.list-group-horizontal > .list-group-item:first-child:not(:last-child) {\n border-bottom-left-radius: var(--bs-list-group-border-radius);\n border-top-right-radius: 0;\n}\n.list-group-horizontal > .list-group-item:last-child:not(:first-child) {\n border-top-right-radius: var(--bs-list-group-border-radius);\n border-bottom-left-radius: 0;\n}\n.list-group-horizontal > .list-group-item.active {\n margin-top: 0;\n}\n.list-group-horizontal > .list-group-item + .list-group-item {\n border-top-width: var(--bs-list-group-border-width);\n border-left-width: 0;\n}\n.list-group-horizontal > .list-group-item + .list-group-item.active {\n margin-left: calc(-1 * var(--bs-list-group-border-width));\n border-left-width: var(--bs-list-group-border-width);\n}\n\n@media (min-width: 576px) {\n .list-group-horizontal-sm {\n flex-direction: row;\n }\n .list-group-horizontal-sm > .list-group-item:first-child:not(:last-child) {\n border-bottom-left-radius: var(--bs-list-group-border-radius);\n border-top-right-radius: 0;\n }\n .list-group-horizontal-sm > .list-group-item:last-child:not(:first-child) {\n border-top-right-radius: var(--bs-list-group-border-radius);\n border-bottom-left-radius: 0;\n }\n .list-group-horizontal-sm > .list-group-item.active {\n margin-top: 0;\n }\n .list-group-horizontal-sm > .list-group-item + .list-group-item {\n border-top-width: var(--bs-list-group-border-width);\n border-left-width: 0;\n }\n .list-group-horizontal-sm > .list-group-item + .list-group-item.active {\n margin-left: calc(-1 * var(--bs-list-group-border-width));\n border-left-width: var(--bs-list-group-border-width);\n }\n}\n@media (min-width: 768px) {\n .list-group-horizontal-md {\n flex-direction: row;\n }\n .list-group-horizontal-md > .list-group-item:first-child:not(:last-child) {\n border-bottom-left-radius: var(--bs-list-group-border-radius);\n border-top-right-radius: 0;\n }\n .list-group-horizontal-md > .list-group-item:last-child:not(:first-child) {\n border-top-right-radius: var(--bs-list-group-border-radius);\n border-bottom-left-radius: 0;\n }\n .list-group-horizontal-md > .list-group-item.active {\n margin-top: 0;\n }\n .list-group-horizontal-md > .list-group-item + .list-group-item {\n border-top-width: var(--bs-list-group-border-width);\n border-left-width: 0;\n }\n .list-group-horizontal-md > .list-group-item + .list-group-item.active {\n margin-left: calc(-1 * var(--bs-list-group-border-width));\n border-left-width: var(--bs-list-group-border-width);\n }\n}\n@media (min-width: 992px) {\n .list-group-horizontal-lg {\n flex-direction: row;\n }\n .list-group-horizontal-lg > .list-group-item:first-child:not(:last-child) {\n border-bottom-left-radius: var(--bs-list-group-border-radius);\n border-top-right-radius: 0;\n }\n .list-group-horizontal-lg > .list-group-item:last-child:not(:first-child) {\n border-top-right-radius: var(--bs-list-group-border-radius);\n border-bottom-left-radius: 0;\n }\n .list-group-horizontal-lg > .list-group-item.active {\n margin-top: 0;\n }\n .list-group-horizontal-lg > .list-group-item + .list-group-item {\n border-top-width: var(--bs-list-group-border-width);\n border-left-width: 0;\n }\n .list-group-horizontal-lg > .list-group-item + .list-group-item.active {\n margin-left: calc(-1 * var(--bs-list-group-border-width));\n border-left-width: var(--bs-list-group-border-width);\n }\n}\n@media (min-width: 1200px) {\n .list-group-horizontal-xl {\n flex-direction: row;\n }\n .list-group-horizontal-xl > .list-group-item:first-child:not(:last-child) {\n border-bottom-left-radius: var(--bs-list-group-border-radius);\n border-top-right-radius: 0;\n }\n .list-group-horizontal-xl > .list-group-item:last-child:not(:first-child) {\n border-top-right-radius: var(--bs-list-group-border-radius);\n border-bottom-left-radius: 0;\n }\n .list-group-horizontal-xl > .list-group-item.active {\n margin-top: 0;\n }\n .list-group-horizontal-xl > .list-group-item + .list-group-item {\n border-top-width: var(--bs-list-group-border-width);\n border-left-width: 0;\n }\n .list-group-horizontal-xl > .list-group-item + .list-group-item.active {\n margin-left: calc(-1 * var(--bs-list-group-border-width));\n border-left-width: var(--bs-list-group-border-width);\n }\n}\n@media (min-width: 1400px) {\n .list-group-horizontal-xxl {\n flex-direction: row;\n }\n .list-group-horizontal-xxl > .list-group-item:first-child:not(:last-child) {\n border-bottom-left-radius: var(--bs-list-group-border-radius);\n border-top-right-radius: 0;\n }\n .list-group-horizontal-xxl > .list-group-item:last-child:not(:first-child) {\n border-top-right-radius: var(--bs-list-group-border-radius);\n border-bottom-left-radius: 0;\n }\n .list-group-horizontal-xxl > .list-group-item.active {\n margin-top: 0;\n }\n .list-group-horizontal-xxl > .list-group-item + .list-group-item {\n border-top-width: var(--bs-list-group-border-width);\n border-left-width: 0;\n }\n .list-group-horizontal-xxl > .list-group-item + .list-group-item.active {\n margin-left: calc(-1 * var(--bs-list-group-border-width));\n border-left-width: var(--bs-list-group-border-width);\n }\n}\n.list-group-flush {\n border-radius: 0;\n}\n.list-group-flush > .list-group-item {\n border-width: 0 0 var(--bs-list-group-border-width);\n}\n.list-group-flush > .list-group-item:last-child {\n border-bottom-width: 0;\n}\n\n.list-group-item-primary {\n --bs-list-group-color: var(--bs-primary-text-emphasis);\n --bs-list-group-bg: var(--bs-primary-bg-subtle);\n --bs-list-group-border-color: var(--bs-primary-border-subtle);\n --bs-list-group-action-hover-color: var(--bs-emphasis-color);\n --bs-list-group-action-hover-bg: var(--bs-primary-border-subtle);\n --bs-list-group-action-active-color: var(--bs-emphasis-color);\n --bs-list-group-action-active-bg: var(--bs-primary-border-subtle);\n --bs-list-group-active-color: var(--bs-primary-bg-subtle);\n --bs-list-group-active-bg: var(--bs-primary-text-emphasis);\n --bs-list-group-active-border-color: var(--bs-primary-text-emphasis);\n}\n\n.list-group-item-secondary {\n --bs-list-group-color: var(--bs-secondary-text-emphasis);\n --bs-list-group-bg: var(--bs-secondary-bg-subtle);\n --bs-list-group-border-color: var(--bs-secondary-border-subtle);\n --bs-list-group-action-hover-color: var(--bs-emphasis-color);\n --bs-list-group-action-hover-bg: var(--bs-secondary-border-subtle);\n --bs-list-group-action-active-color: var(--bs-emphasis-color);\n --bs-list-group-action-active-bg: var(--bs-secondary-border-subtle);\n --bs-list-group-active-color: var(--bs-secondary-bg-subtle);\n --bs-list-group-active-bg: var(--bs-secondary-text-emphasis);\n --bs-list-group-active-border-color: var(--bs-secondary-text-emphasis);\n}\n\n.list-group-item-success {\n --bs-list-group-color: var(--bs-success-text-emphasis);\n --bs-list-group-bg: var(--bs-success-bg-subtle);\n --bs-list-group-border-color: var(--bs-success-border-subtle);\n --bs-list-group-action-hover-color: var(--bs-emphasis-color);\n --bs-list-group-action-hover-bg: var(--bs-success-border-subtle);\n --bs-list-group-action-active-color: var(--bs-emphasis-color);\n --bs-list-group-action-active-bg: var(--bs-success-border-subtle);\n --bs-list-group-active-color: var(--bs-success-bg-subtle);\n --bs-list-group-active-bg: var(--bs-success-text-emphasis);\n --bs-list-group-active-border-color: var(--bs-success-text-emphasis);\n}\n\n.list-group-item-info {\n --bs-list-group-color: var(--bs-info-text-emphasis);\n --bs-list-group-bg: var(--bs-info-bg-subtle);\n --bs-list-group-border-color: var(--bs-info-border-subtle);\n --bs-list-group-action-hover-color: var(--bs-emphasis-color);\n --bs-list-group-action-hover-bg: var(--bs-info-border-subtle);\n --bs-list-group-action-active-color: var(--bs-emphasis-color);\n --bs-list-group-action-active-bg: var(--bs-info-border-subtle);\n --bs-list-group-active-color: var(--bs-info-bg-subtle);\n --bs-list-group-active-bg: var(--bs-info-text-emphasis);\n --bs-list-group-active-border-color: var(--bs-info-text-emphasis);\n}\n\n.list-group-item-warning {\n --bs-list-group-color: var(--bs-warning-text-emphasis);\n --bs-list-group-bg: var(--bs-warning-bg-subtle);\n --bs-list-group-border-color: var(--bs-warning-border-subtle);\n --bs-list-group-action-hover-color: var(--bs-emphasis-color);\n --bs-list-group-action-hover-bg: var(--bs-warning-border-subtle);\n --bs-list-group-action-active-color: var(--bs-emphasis-color);\n --bs-list-group-action-active-bg: var(--bs-warning-border-subtle);\n --bs-list-group-active-color: var(--bs-warning-bg-subtle);\n --bs-list-group-active-bg: var(--bs-warning-text-emphasis);\n --bs-list-group-active-border-color: var(--bs-warning-text-emphasis);\n}\n\n.list-group-item-danger {\n --bs-list-group-color: var(--bs-danger-text-emphasis);\n --bs-list-group-bg: var(--bs-danger-bg-subtle);\n --bs-list-group-border-color: var(--bs-danger-border-subtle);\n --bs-list-group-action-hover-color: var(--bs-emphasis-color);\n --bs-list-group-action-hover-bg: var(--bs-danger-border-subtle);\n --bs-list-group-action-active-color: var(--bs-emphasis-color);\n --bs-list-group-action-active-bg: var(--bs-danger-border-subtle);\n --bs-list-group-active-color: var(--bs-danger-bg-subtle);\n --bs-list-group-active-bg: var(--bs-danger-text-emphasis);\n --bs-list-group-active-border-color: var(--bs-danger-text-emphasis);\n}\n\n.list-group-item-light {\n --bs-list-group-color: var(--bs-light-text-emphasis);\n --bs-list-group-bg: var(--bs-light-bg-subtle);\n --bs-list-group-border-color: var(--bs-light-border-subtle);\n --bs-list-group-action-hover-color: var(--bs-emphasis-color);\n --bs-list-group-action-hover-bg: var(--bs-light-border-subtle);\n --bs-list-group-action-active-color: var(--bs-emphasis-color);\n --bs-list-group-action-active-bg: var(--bs-light-border-subtle);\n --bs-list-group-active-color: var(--bs-light-bg-subtle);\n --bs-list-group-active-bg: var(--bs-light-text-emphasis);\n --bs-list-group-active-border-color: var(--bs-light-text-emphasis);\n}\n\n.list-group-item-dark {\n --bs-list-group-color: var(--bs-dark-text-emphasis);\n --bs-list-group-bg: var(--bs-dark-bg-subtle);\n --bs-list-group-border-color: var(--bs-dark-border-subtle);\n --bs-list-group-action-hover-color: var(--bs-emphasis-color);\n --bs-list-group-action-hover-bg: var(--bs-dark-border-subtle);\n --bs-list-group-action-active-color: var(--bs-emphasis-color);\n --bs-list-group-action-active-bg: var(--bs-dark-border-subtle);\n --bs-list-group-active-color: var(--bs-dark-bg-subtle);\n --bs-list-group-active-bg: var(--bs-dark-text-emphasis);\n --bs-list-group-active-border-color: var(--bs-dark-text-emphasis);\n}\n\n.btn-close {\n --bs-btn-close-color: #000;\n --bs-btn-close-bg: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/%3e%3c/svg%3e\");\n --bs-btn-close-opacity: 0.5;\n --bs-btn-close-hover-opacity: 0.75;\n --bs-btn-close-focus-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);\n --bs-btn-close-focus-opacity: 1;\n --bs-btn-close-disabled-opacity: 0.25;\n --bs-btn-close-white-filter: invert(1) grayscale(100%) brightness(200%);\n box-sizing: content-box;\n width: 1em;\n height: 1em;\n padding: 0.25em 0.25em;\n color: var(--bs-btn-close-color);\n background: transparent var(--bs-btn-close-bg) center/1em auto no-repeat;\n border: 0;\n border-radius: 0.375rem;\n opacity: var(--bs-btn-close-opacity);\n}\n.btn-close:hover {\n color: var(--bs-btn-close-color);\n text-decoration: none;\n opacity: var(--bs-btn-close-hover-opacity);\n}\n.btn-close:focus {\n outline: 0;\n box-shadow: var(--bs-btn-close-focus-shadow);\n opacity: var(--bs-btn-close-focus-opacity);\n}\n.btn-close:disabled, .btn-close.disabled {\n pointer-events: none;\n -webkit-user-select: none;\n -moz-user-select: none;\n user-select: none;\n opacity: var(--bs-btn-close-disabled-opacity);\n}\n\n.btn-close-white {\n filter: var(--bs-btn-close-white-filter);\n}\n\n[data-bs-theme=dark] .btn-close {\n filter: var(--bs-btn-close-white-filter);\n}\n\n.toast {\n --bs-toast-zindex: 1090;\n --bs-toast-padding-x: 0.75rem;\n --bs-toast-padding-y: 0.5rem;\n --bs-toast-spacing: 1.5rem;\n --bs-toast-max-width: 350px;\n --bs-toast-font-size: 0.875rem;\n --bs-toast-color: ;\n --bs-toast-bg: rgba(var(--bs-body-bg-rgb), 0.85);\n --bs-toast-border-width: var(--bs-border-width);\n --bs-toast-border-color: var(--bs-border-color-translucent);\n --bs-toast-border-radius: var(--bs-border-radius);\n --bs-toast-box-shadow: var(--bs-box-shadow);\n --bs-toast-header-color: var(--bs-secondary-color);\n --bs-toast-header-bg: rgba(var(--bs-body-bg-rgb), 0.85);\n --bs-toast-header-border-color: var(--bs-border-color-translucent);\n width: var(--bs-toast-max-width);\n max-width: 100%;\n font-size: var(--bs-toast-font-size);\n color: var(--bs-toast-color);\n pointer-events: auto;\n background-color: var(--bs-toast-bg);\n background-clip: padding-box;\n border: var(--bs-toast-border-width) solid var(--bs-toast-border-color);\n box-shadow: var(--bs-toast-box-shadow);\n border-radius: var(--bs-toast-border-radius);\n}\n.toast.showing {\n opacity: 0;\n}\n.toast:not(.show) {\n display: none;\n}\n\n.toast-container {\n --bs-toast-zindex: 1090;\n position: absolute;\n z-index: var(--bs-toast-zindex);\n width: -webkit-max-content;\n width: -moz-max-content;\n width: max-content;\n max-width: 100%;\n pointer-events: none;\n}\n.toast-container > :not(:last-child) {\n margin-bottom: var(--bs-toast-spacing);\n}\n\n.toast-header {\n display: flex;\n align-items: center;\n padding: var(--bs-toast-padding-y) var(--bs-toast-padding-x);\n color: var(--bs-toast-header-color);\n background-color: var(--bs-toast-header-bg);\n background-clip: padding-box;\n border-bottom: var(--bs-toast-border-width) solid var(--bs-toast-header-border-color);\n border-top-left-radius: calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width));\n border-top-right-radius: calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width));\n}\n.toast-header .btn-close {\n margin-right: calc(-0.5 * var(--bs-toast-padding-x));\n margin-left: var(--bs-toast-padding-x);\n}\n\n.toast-body {\n padding: var(--bs-toast-padding-x);\n word-wrap: break-word;\n}\n\n.modal {\n --bs-modal-zindex: 1055;\n --bs-modal-width: 500px;\n --bs-modal-padding: 1rem;\n --bs-modal-margin: 0.5rem;\n --bs-modal-color: ;\n --bs-modal-bg: var(--bs-body-bg);\n --bs-modal-border-color: var(--bs-border-color-translucent);\n --bs-modal-border-width: var(--bs-border-width);\n --bs-modal-border-radius: var(--bs-border-radius-lg);\n --bs-modal-box-shadow: var(--bs-box-shadow-sm);\n --bs-modal-inner-border-radius: calc(var(--bs-border-radius-lg) - (var(--bs-border-width)));\n --bs-modal-header-padding-x: 1rem;\n --bs-modal-header-padding-y: 1rem;\n --bs-modal-header-padding: 1rem 1rem;\n --bs-modal-header-border-color: var(--bs-border-color);\n --bs-modal-header-border-width: var(--bs-border-width);\n --bs-modal-title-line-height: 1.5;\n --bs-modal-footer-gap: 0.5rem;\n --bs-modal-footer-bg: ;\n --bs-modal-footer-border-color: var(--bs-border-color);\n --bs-modal-footer-border-width: var(--bs-border-width);\n position: fixed;\n top: 0;\n left: 0;\n z-index: var(--bs-modal-zindex);\n display: none;\n width: 100%;\n height: 100%;\n overflow-x: hidden;\n overflow-y: auto;\n outline: 0;\n}\n\n.modal-dialog {\n position: relative;\n width: auto;\n margin: var(--bs-modal-margin);\n pointer-events: none;\n}\n.modal.fade .modal-dialog {\n transition: transform 0.3s ease-out;\n transform: translate(0, -50px);\n}\n@media (prefers-reduced-motion: reduce) {\n .modal.fade .modal-dialog {\n transition: none;\n }\n}\n.modal.show .modal-dialog {\n transform: none;\n}\n.modal.modal-static .modal-dialog {\n transform: scale(1.02);\n}\n\n.modal-dialog-scrollable {\n height: calc(100% - var(--bs-modal-margin) * 2);\n}\n.modal-dialog-scrollable .modal-content {\n max-height: 100%;\n overflow: hidden;\n}\n.modal-dialog-scrollable .modal-body {\n overflow-y: auto;\n}\n\n.modal-dialog-centered {\n display: flex;\n align-items: center;\n min-height: calc(100% - var(--bs-modal-margin) * 2);\n}\n\n.modal-content {\n position: relative;\n display: flex;\n flex-direction: column;\n width: 100%;\n color: var(--bs-modal-color);\n pointer-events: auto;\n background-color: var(--bs-modal-bg);\n background-clip: padding-box;\n border: var(--bs-modal-border-width) solid var(--bs-modal-border-color);\n border-radius: var(--bs-modal-border-radius);\n outline: 0;\n}\n\n.modal-backdrop {\n --bs-backdrop-zindex: 1050;\n --bs-backdrop-bg: #000;\n --bs-backdrop-opacity: 0.5;\n position: fixed;\n top: 0;\n left: 0;\n z-index: var(--bs-backdrop-zindex);\n width: 100vw;\n height: 100vh;\n background-color: var(--bs-backdrop-bg);\n}\n.modal-backdrop.fade {\n opacity: 0;\n}\n.modal-backdrop.show {\n opacity: var(--bs-backdrop-opacity);\n}\n\n.modal-header {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n justify-content: space-between;\n padding: var(--bs-modal-header-padding);\n border-bottom: var(--bs-modal-header-border-width) solid var(--bs-modal-header-border-color);\n border-top-left-radius: var(--bs-modal-inner-border-radius);\n border-top-right-radius: var(--bs-modal-inner-border-radius);\n}\n.modal-header .btn-close {\n padding: calc(var(--bs-modal-header-padding-y) * 0.5) calc(var(--bs-modal-header-padding-x) * 0.5);\n margin: calc(-0.5 * var(--bs-modal-header-padding-y)) calc(-0.5 * var(--bs-modal-header-padding-x)) calc(-0.5 * var(--bs-modal-header-padding-y)) auto;\n}\n\n.modal-title {\n margin-bottom: 0;\n line-height: var(--bs-modal-title-line-height);\n}\n\n.modal-body {\n position: relative;\n flex: 1 1 auto;\n padding: var(--bs-modal-padding);\n}\n\n.modal-footer {\n display: flex;\n flex-shrink: 0;\n flex-wrap: wrap;\n align-items: center;\n justify-content: flex-end;\n padding: calc(var(--bs-modal-padding) - var(--bs-modal-footer-gap) * 0.5);\n background-color: var(--bs-modal-footer-bg);\n border-top: var(--bs-modal-footer-border-width) solid var(--bs-modal-footer-border-color);\n border-bottom-right-radius: var(--bs-modal-inner-border-radius);\n border-bottom-left-radius: var(--bs-modal-inner-border-radius);\n}\n.modal-footer > * {\n margin: calc(var(--bs-modal-footer-gap) * 0.5);\n}\n\n@media (min-width: 576px) {\n .modal {\n --bs-modal-margin: 1.75rem;\n --bs-modal-box-shadow: var(--bs-box-shadow);\n }\n .modal-dialog {\n max-width: var(--bs-modal-width);\n margin-right: auto;\n margin-left: auto;\n }\n .modal-sm {\n --bs-modal-width: 300px;\n }\n}\n@media (min-width: 992px) {\n .modal-lg,\n .modal-xl {\n --bs-modal-width: 800px;\n }\n}\n@media (min-width: 1200px) {\n .modal-xl {\n --bs-modal-width: 1140px;\n }\n}\n.modal-fullscreen {\n width: 100vw;\n max-width: none;\n height: 100%;\n margin: 0;\n}\n.modal-fullscreen .modal-content {\n height: 100%;\n border: 0;\n border-radius: 0;\n}\n.modal-fullscreen .modal-header,\n.modal-fullscreen .modal-footer {\n border-radius: 0;\n}\n.modal-fullscreen .modal-body {\n overflow-y: auto;\n}\n\n@media (max-width: 575.98px) {\n .modal-fullscreen-sm-down {\n width: 100vw;\n max-width: none;\n height: 100%;\n margin: 0;\n }\n .modal-fullscreen-sm-down .modal-content {\n height: 100%;\n border: 0;\n border-radius: 0;\n }\n .modal-fullscreen-sm-down .modal-header,\n .modal-fullscreen-sm-down .modal-footer {\n border-radius: 0;\n }\n .modal-fullscreen-sm-down .modal-body {\n overflow-y: auto;\n }\n}\n@media (max-width: 767.98px) {\n .modal-fullscreen-md-down {\n width: 100vw;\n max-width: none;\n height: 100%;\n margin: 0;\n }\n .modal-fullscreen-md-down .modal-content {\n height: 100%;\n border: 0;\n border-radius: 0;\n }\n .modal-fullscreen-md-down .modal-header,\n .modal-fullscreen-md-down .modal-footer {\n border-radius: 0;\n }\n .modal-fullscreen-md-down .modal-body {\n overflow-y: auto;\n }\n}\n@media (max-width: 991.98px) {\n .modal-fullscreen-lg-down {\n width: 100vw;\n max-width: none;\n height: 100%;\n margin: 0;\n }\n .modal-fullscreen-lg-down .modal-content {\n height: 100%;\n border: 0;\n border-radius: 0;\n }\n .modal-fullscreen-lg-down .modal-header,\n .modal-fullscreen-lg-down .modal-footer {\n border-radius: 0;\n }\n .modal-fullscreen-lg-down .modal-body {\n overflow-y: auto;\n }\n}\n@media (max-width: 1199.98px) {\n .modal-fullscreen-xl-down {\n width: 100vw;\n max-width: none;\n height: 100%;\n margin: 0;\n }\n .modal-fullscreen-xl-down .modal-content {\n height: 100%;\n border: 0;\n border-radius: 0;\n }\n .modal-fullscreen-xl-down .modal-header,\n .modal-fullscreen-xl-down .modal-footer {\n border-radius: 0;\n }\n .modal-fullscreen-xl-down .modal-body {\n overflow-y: auto;\n }\n}\n@media (max-width: 1399.98px) {\n .modal-fullscreen-xxl-down {\n width: 100vw;\n max-width: none;\n height: 100%;\n margin: 0;\n }\n .modal-fullscreen-xxl-down .modal-content {\n height: 100%;\n border: 0;\n border-radius: 0;\n }\n .modal-fullscreen-xxl-down .modal-header,\n .modal-fullscreen-xxl-down .modal-footer {\n border-radius: 0;\n }\n .modal-fullscreen-xxl-down .modal-body {\n overflow-y: auto;\n }\n}\n.tooltip {\n --bs-tooltip-zindex: 1080;\n --bs-tooltip-max-width: 200px;\n --bs-tooltip-padding-x: 0.5rem;\n --bs-tooltip-padding-y: 0.25rem;\n --bs-tooltip-margin: ;\n --bs-tooltip-font-size: 0.875rem;\n --bs-tooltip-color: var(--bs-body-bg);\n --bs-tooltip-bg: var(--bs-emphasis-color);\n --bs-tooltip-border-radius: var(--bs-border-radius);\n --bs-tooltip-opacity: 0.9;\n --bs-tooltip-arrow-width: 0.8rem;\n --bs-tooltip-arrow-height: 0.4rem;\n z-index: var(--bs-tooltip-zindex);\n display: block;\n margin: var(--bs-tooltip-margin);\n font-family: var(--bs-font-sans-serif);\n font-style: normal;\n font-weight: 400;\n line-height: 1.5;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n letter-spacing: normal;\n word-break: normal;\n white-space: normal;\n word-spacing: normal;\n line-break: auto;\n font-size: var(--bs-tooltip-font-size);\n word-wrap: break-word;\n opacity: 0;\n}\n.tooltip.show {\n opacity: var(--bs-tooltip-opacity);\n}\n.tooltip .tooltip-arrow {\n display: block;\n width: var(--bs-tooltip-arrow-width);\n height: var(--bs-tooltip-arrow-height);\n}\n.tooltip .tooltip-arrow::before {\n position: absolute;\n content: \"\";\n border-color: transparent;\n border-style: solid;\n}\n\n.bs-tooltip-top .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow {\n bottom: calc(-1 * var(--bs-tooltip-arrow-height));\n}\n.bs-tooltip-top .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before {\n top: -1px;\n border-width: var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * 0.5) 0;\n border-top-color: var(--bs-tooltip-bg);\n}\n\n/* rtl:begin:ignore */\n.bs-tooltip-end .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow {\n left: calc(-1 * var(--bs-tooltip-arrow-height));\n width: var(--bs-tooltip-arrow-height);\n height: var(--bs-tooltip-arrow-width);\n}\n.bs-tooltip-end .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before {\n right: -1px;\n border-width: calc(var(--bs-tooltip-arrow-width) * 0.5) var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * 0.5) 0;\n border-right-color: var(--bs-tooltip-bg);\n}\n\n/* rtl:end:ignore */\n.bs-tooltip-bottom .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow {\n top: calc(-1 * var(--bs-tooltip-arrow-height));\n}\n.bs-tooltip-bottom .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before {\n bottom: -1px;\n border-width: 0 calc(var(--bs-tooltip-arrow-width) * 0.5) var(--bs-tooltip-arrow-height);\n border-bottom-color: var(--bs-tooltip-bg);\n}\n\n/* rtl:begin:ignore */\n.bs-tooltip-start .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow {\n right: calc(-1 * var(--bs-tooltip-arrow-height));\n width: var(--bs-tooltip-arrow-height);\n height: var(--bs-tooltip-arrow-width);\n}\n.bs-tooltip-start .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before {\n left: -1px;\n border-width: calc(var(--bs-tooltip-arrow-width) * 0.5) 0 calc(var(--bs-tooltip-arrow-width) * 0.5) var(--bs-tooltip-arrow-height);\n border-left-color: var(--bs-tooltip-bg);\n}\n\n/* rtl:end:ignore */\n.tooltip-inner {\n max-width: var(--bs-tooltip-max-width);\n padding: var(--bs-tooltip-padding-y) var(--bs-tooltip-padding-x);\n color: var(--bs-tooltip-color);\n text-align: center;\n background-color: var(--bs-tooltip-bg);\n border-radius: var(--bs-tooltip-border-radius);\n}\n\n.popover {\n --bs-popover-zindex: 1070;\n --bs-popover-max-width: 276px;\n --bs-popover-font-size: 0.875rem;\n --bs-popover-bg: var(--bs-body-bg);\n --bs-popover-border-width: var(--bs-border-width);\n --bs-popover-border-color: var(--bs-border-color-translucent);\n --bs-popover-border-radius: var(--bs-border-radius-lg);\n --bs-popover-inner-border-radius: calc(var(--bs-border-radius-lg) - var(--bs-border-width));\n --bs-popover-box-shadow: var(--bs-box-shadow);\n --bs-popover-header-padding-x: 1rem;\n --bs-popover-header-padding-y: 0.5rem;\n --bs-popover-header-font-size: 1rem;\n --bs-popover-header-color: inherit;\n --bs-popover-header-bg: var(--bs-secondary-bg);\n --bs-popover-body-padding-x: 1rem;\n --bs-popover-body-padding-y: 1rem;\n --bs-popover-body-color: var(--bs-body-color);\n --bs-popover-arrow-width: 1rem;\n --bs-popover-arrow-height: 0.5rem;\n --bs-popover-arrow-border: var(--bs-popover-border-color);\n z-index: var(--bs-popover-zindex);\n display: block;\n max-width: var(--bs-popover-max-width);\n font-family: var(--bs-font-sans-serif);\n font-style: normal;\n font-weight: 400;\n line-height: 1.5;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n letter-spacing: normal;\n word-break: normal;\n white-space: normal;\n word-spacing: normal;\n line-break: auto;\n font-size: var(--bs-popover-font-size);\n word-wrap: break-word;\n background-color: var(--bs-popover-bg);\n background-clip: padding-box;\n border: var(--bs-popover-border-width) solid var(--bs-popover-border-color);\n border-radius: var(--bs-popover-border-radius);\n}\n.popover .popover-arrow {\n display: block;\n width: var(--bs-popover-arrow-width);\n height: var(--bs-popover-arrow-height);\n}\n.popover .popover-arrow::before, .popover .popover-arrow::after {\n position: absolute;\n display: block;\n content: \"\";\n border-color: transparent;\n border-style: solid;\n border-width: 0;\n}\n\n.bs-popover-top > .popover-arrow, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow {\n bottom: calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));\n}\n.bs-popover-top > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow::before, .bs-popover-top > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow::after {\n border-width: var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * 0.5) 0;\n}\n.bs-popover-top > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow::before {\n bottom: 0;\n border-top-color: var(--bs-popover-arrow-border);\n}\n.bs-popover-top > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=top] > .popover-arrow::after {\n bottom: var(--bs-popover-border-width);\n border-top-color: var(--bs-popover-bg);\n}\n\n/* rtl:begin:ignore */\n.bs-popover-end > .popover-arrow, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow {\n left: calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));\n width: var(--bs-popover-arrow-height);\n height: var(--bs-popover-arrow-width);\n}\n.bs-popover-end > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow::before, .bs-popover-end > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow::after {\n border-width: calc(var(--bs-popover-arrow-width) * 0.5) var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * 0.5) 0;\n}\n.bs-popover-end > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow::before {\n left: 0;\n border-right-color: var(--bs-popover-arrow-border);\n}\n.bs-popover-end > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=right] > .popover-arrow::after {\n left: var(--bs-popover-border-width);\n border-right-color: var(--bs-popover-bg);\n}\n\n/* rtl:end:ignore */\n.bs-popover-bottom > .popover-arrow, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow {\n top: calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));\n}\n.bs-popover-bottom > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::before, .bs-popover-bottom > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::after {\n border-width: 0 calc(var(--bs-popover-arrow-width) * 0.5) var(--bs-popover-arrow-height);\n}\n.bs-popover-bottom > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::before {\n top: 0;\n border-bottom-color: var(--bs-popover-arrow-border);\n}\n.bs-popover-bottom > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=bottom] > .popover-arrow::after {\n top: var(--bs-popover-border-width);\n border-bottom-color: var(--bs-popover-bg);\n}\n.bs-popover-bottom .popover-header::before, .bs-popover-auto[data-popper-placement^=bottom] .popover-header::before {\n position: absolute;\n top: 0;\n left: 50%;\n display: block;\n width: var(--bs-popover-arrow-width);\n margin-left: calc(-0.5 * var(--bs-popover-arrow-width));\n content: \"\";\n border-bottom: var(--bs-popover-border-width) solid var(--bs-popover-header-bg);\n}\n\n/* rtl:begin:ignore */\n.bs-popover-start > .popover-arrow, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow {\n right: calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));\n width: var(--bs-popover-arrow-height);\n height: var(--bs-popover-arrow-width);\n}\n.bs-popover-start > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow::before, .bs-popover-start > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow::after {\n border-width: calc(var(--bs-popover-arrow-width) * 0.5) 0 calc(var(--bs-popover-arrow-width) * 0.5) var(--bs-popover-arrow-height);\n}\n.bs-popover-start > .popover-arrow::before, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow::before {\n right: 0;\n border-left-color: var(--bs-popover-arrow-border);\n}\n.bs-popover-start > .popover-arrow::after, .bs-popover-auto[data-popper-placement^=left] > .popover-arrow::after {\n right: var(--bs-popover-border-width);\n border-left-color: var(--bs-popover-bg);\n}\n\n/* rtl:end:ignore */\n.popover-header {\n padding: var(--bs-popover-header-padding-y) var(--bs-popover-header-padding-x);\n margin-bottom: 0;\n font-size: var(--bs-popover-header-font-size);\n color: var(--bs-popover-header-color);\n background-color: var(--bs-popover-header-bg);\n border-bottom: var(--bs-popover-border-width) solid var(--bs-popover-border-color);\n border-top-left-radius: var(--bs-popover-inner-border-radius);\n border-top-right-radius: var(--bs-popover-inner-border-radius);\n}\n.popover-header:empty {\n display: none;\n}\n\n.popover-body {\n padding: var(--bs-popover-body-padding-y) var(--bs-popover-body-padding-x);\n color: var(--bs-popover-body-color);\n}\n\n.carousel {\n position: relative;\n}\n\n.carousel.pointer-event {\n touch-action: pan-y;\n}\n\n.carousel-inner {\n position: relative;\n width: 100%;\n overflow: hidden;\n}\n.carousel-inner::after {\n display: block;\n clear: both;\n content: \"\";\n}\n\n.carousel-item {\n position: relative;\n display: none;\n float: left;\n width: 100%;\n margin-right: -100%;\n -webkit-backface-visibility: hidden;\n backface-visibility: hidden;\n transition: transform 0.6s ease-in-out;\n}\n@media (prefers-reduced-motion: reduce) {\n .carousel-item {\n transition: none;\n }\n}\n\n.carousel-item.active,\n.carousel-item-next,\n.carousel-item-prev {\n display: block;\n}\n\n.carousel-item-next:not(.carousel-item-start),\n.active.carousel-item-end {\n transform: translateX(100%);\n}\n\n.carousel-item-prev:not(.carousel-item-end),\n.active.carousel-item-start {\n transform: translateX(-100%);\n}\n\n.carousel-fade .carousel-item {\n opacity: 0;\n transition-property: opacity;\n transform: none;\n}\n.carousel-fade .carousel-item.active,\n.carousel-fade .carousel-item-next.carousel-item-start,\n.carousel-fade .carousel-item-prev.carousel-item-end {\n z-index: 1;\n opacity: 1;\n}\n.carousel-fade .active.carousel-item-start,\n.carousel-fade .active.carousel-item-end {\n z-index: 0;\n opacity: 0;\n transition: opacity 0s 0.6s;\n}\n@media (prefers-reduced-motion: reduce) {\n .carousel-fade .active.carousel-item-start,\n .carousel-fade .active.carousel-item-end {\n transition: none;\n }\n}\n\n.carousel-control-prev,\n.carousel-control-next {\n position: absolute;\n top: 0;\n bottom: 0;\n z-index: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 15%;\n padding: 0;\n color: #fff;\n text-align: center;\n background: none;\n border: 0;\n opacity: 0.5;\n transition: opacity 0.15s ease;\n}\n@media (prefers-reduced-motion: reduce) {\n .carousel-control-prev,\n .carousel-control-next {\n transition: none;\n }\n}\n.carousel-control-prev:hover, .carousel-control-prev:focus,\n.carousel-control-next:hover,\n.carousel-control-next:focus {\n color: #fff;\n text-decoration: none;\n outline: 0;\n opacity: 0.9;\n}\n\n.carousel-control-prev {\n left: 0;\n}\n\n.carousel-control-next {\n right: 0;\n}\n\n.carousel-control-prev-icon,\n.carousel-control-next-icon {\n display: inline-block;\n width: 2rem;\n height: 2rem;\n background-repeat: no-repeat;\n background-position: 50%;\n background-size: 100% 100%;\n}\n\n/* rtl:options: {\n \"autoRename\": true,\n \"stringMap\":[ {\n \"name\" : \"prev-next\",\n \"search\" : \"prev\",\n \"replace\" : \"next\"\n } ]\n} */\n.carousel-control-prev-icon {\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e\");\n}\n\n.carousel-control-next-icon {\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e\");\n}\n\n.carousel-indicators {\n position: absolute;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 2;\n display: flex;\n justify-content: center;\n padding: 0;\n margin-right: 15%;\n margin-bottom: 1rem;\n margin-left: 15%;\n}\n.carousel-indicators [data-bs-target] {\n box-sizing: content-box;\n flex: 0 1 auto;\n width: 30px;\n height: 3px;\n padding: 0;\n margin-right: 3px;\n margin-left: 3px;\n text-indent: -999px;\n cursor: pointer;\n background-color: #fff;\n background-clip: padding-box;\n border: 0;\n border-top: 10px solid transparent;\n border-bottom: 10px solid transparent;\n opacity: 0.5;\n transition: opacity 0.6s ease;\n}\n@media (prefers-reduced-motion: reduce) {\n .carousel-indicators [data-bs-target] {\n transition: none;\n }\n}\n.carousel-indicators .active {\n opacity: 1;\n}\n\n.carousel-caption {\n position: absolute;\n right: 15%;\n bottom: 1.25rem;\n left: 15%;\n padding-top: 1.25rem;\n padding-bottom: 1.25rem;\n color: #fff;\n text-align: center;\n}\n\n.carousel-dark .carousel-control-prev-icon,\n.carousel-dark .carousel-control-next-icon {\n filter: invert(1) grayscale(100);\n}\n.carousel-dark .carousel-indicators [data-bs-target] {\n background-color: #000;\n}\n.carousel-dark .carousel-caption {\n color: #000;\n}\n\n[data-bs-theme=dark] .carousel .carousel-control-prev-icon,\n[data-bs-theme=dark] .carousel .carousel-control-next-icon, [data-bs-theme=dark].carousel .carousel-control-prev-icon,\n[data-bs-theme=dark].carousel .carousel-control-next-icon {\n filter: invert(1) grayscale(100);\n}\n[data-bs-theme=dark] .carousel .carousel-indicators [data-bs-target], [data-bs-theme=dark].carousel .carousel-indicators [data-bs-target] {\n background-color: #000;\n}\n[data-bs-theme=dark] .carousel .carousel-caption, [data-bs-theme=dark].carousel .carousel-caption {\n color: #000;\n}\n\n.spinner-grow,\n.spinner-border {\n display: inline-block;\n width: var(--bs-spinner-width);\n height: var(--bs-spinner-height);\n vertical-align: var(--bs-spinner-vertical-align);\n border-radius: 50%;\n animation: var(--bs-spinner-animation-speed) linear infinite var(--bs-spinner-animation-name);\n}\n\n@keyframes spinner-border {\n to {\n transform: rotate(360deg) /* rtl:ignore */;\n }\n}\n.spinner-border {\n --bs-spinner-width: 2rem;\n --bs-spinner-height: 2rem;\n --bs-spinner-vertical-align: -0.125em;\n --bs-spinner-border-width: 0.25em;\n --bs-spinner-animation-speed: 0.75s;\n --bs-spinner-animation-name: spinner-border;\n border: var(--bs-spinner-border-width) solid currentcolor;\n border-right-color: transparent;\n}\n\n.spinner-border-sm {\n --bs-spinner-width: 1rem;\n --bs-spinner-height: 1rem;\n --bs-spinner-border-width: 0.2em;\n}\n\n@keyframes spinner-grow {\n 0% {\n transform: scale(0);\n }\n 50% {\n opacity: 1;\n transform: none;\n }\n}\n.spinner-grow {\n --bs-spinner-width: 2rem;\n --bs-spinner-height: 2rem;\n --bs-spinner-vertical-align: -0.125em;\n --bs-spinner-animation-speed: 0.75s;\n --bs-spinner-animation-name: spinner-grow;\n background-color: currentcolor;\n opacity: 0;\n}\n\n.spinner-grow-sm {\n --bs-spinner-width: 1rem;\n --bs-spinner-height: 1rem;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .spinner-border,\n .spinner-grow {\n --bs-spinner-animation-speed: 1.5s;\n }\n}\n.offcanvas, .offcanvas-xxl, .offcanvas-xl, .offcanvas-lg, .offcanvas-md, .offcanvas-sm {\n --bs-offcanvas-zindex: 1045;\n --bs-offcanvas-width: 400px;\n --bs-offcanvas-height: 30vh;\n --bs-offcanvas-padding-x: 1rem;\n --bs-offcanvas-padding-y: 1rem;\n --bs-offcanvas-color: var(--bs-body-color);\n --bs-offcanvas-bg: var(--bs-body-bg);\n --bs-offcanvas-border-width: var(--bs-border-width);\n --bs-offcanvas-border-color: var(--bs-border-color-translucent);\n --bs-offcanvas-box-shadow: var(--bs-box-shadow-sm);\n --bs-offcanvas-transition: transform 0.3s ease-in-out;\n --bs-offcanvas-title-line-height: 1.5;\n}\n\n@media (max-width: 575.98px) {\n .offcanvas-sm {\n position: fixed;\n bottom: 0;\n z-index: var(--bs-offcanvas-zindex);\n display: flex;\n flex-direction: column;\n max-width: 100%;\n color: var(--bs-offcanvas-color);\n visibility: hidden;\n background-color: var(--bs-offcanvas-bg);\n background-clip: padding-box;\n outline: 0;\n transition: var(--bs-offcanvas-transition);\n }\n}\n@media (max-width: 575.98px) and (prefers-reduced-motion: reduce) {\n .offcanvas-sm {\n transition: none;\n }\n}\n@media (max-width: 575.98px) {\n .offcanvas-sm.offcanvas-start {\n top: 0;\n left: 0;\n width: var(--bs-offcanvas-width);\n border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n transform: translateX(-100%);\n }\n .offcanvas-sm.offcanvas-end {\n top: 0;\n right: 0;\n width: var(--bs-offcanvas-width);\n border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n transform: translateX(100%);\n }\n .offcanvas-sm.offcanvas-top {\n top: 0;\n right: 0;\n left: 0;\n height: var(--bs-offcanvas-height);\n max-height: 100%;\n border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n transform: translateY(-100%);\n }\n .offcanvas-sm.offcanvas-bottom {\n right: 0;\n left: 0;\n height: var(--bs-offcanvas-height);\n max-height: 100%;\n border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n transform: translateY(100%);\n }\n .offcanvas-sm.showing, .offcanvas-sm.show:not(.hiding) {\n transform: none;\n }\n .offcanvas-sm.showing, .offcanvas-sm.hiding, .offcanvas-sm.show {\n visibility: visible;\n }\n}\n@media (min-width: 576px) {\n .offcanvas-sm {\n --bs-offcanvas-height: auto;\n --bs-offcanvas-border-width: 0;\n background-color: transparent !important;\n }\n .offcanvas-sm .offcanvas-header {\n display: none;\n }\n .offcanvas-sm .offcanvas-body {\n display: flex;\n flex-grow: 0;\n padding: 0;\n overflow-y: visible;\n background-color: transparent !important;\n }\n}\n\n@media (max-width: 767.98px) {\n .offcanvas-md {\n position: fixed;\n bottom: 0;\n z-index: var(--bs-offcanvas-zindex);\n display: flex;\n flex-direction: column;\n max-width: 100%;\n color: var(--bs-offcanvas-color);\n visibility: hidden;\n background-color: var(--bs-offcanvas-bg);\n background-clip: padding-box;\n outline: 0;\n transition: var(--bs-offcanvas-transition);\n }\n}\n@media (max-width: 767.98px) and (prefers-reduced-motion: reduce) {\n .offcanvas-md {\n transition: none;\n }\n}\n@media (max-width: 767.98px) {\n .offcanvas-md.offcanvas-start {\n top: 0;\n left: 0;\n width: var(--bs-offcanvas-width);\n border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n transform: translateX(-100%);\n }\n .offcanvas-md.offcanvas-end {\n top: 0;\n right: 0;\n width: var(--bs-offcanvas-width);\n border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n transform: translateX(100%);\n }\n .offcanvas-md.offcanvas-top {\n top: 0;\n right: 0;\n left: 0;\n height: var(--bs-offcanvas-height);\n max-height: 100%;\n border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n transform: translateY(-100%);\n }\n .offcanvas-md.offcanvas-bottom {\n right: 0;\n left: 0;\n height: var(--bs-offcanvas-height);\n max-height: 100%;\n border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n transform: translateY(100%);\n }\n .offcanvas-md.showing, .offcanvas-md.show:not(.hiding) {\n transform: none;\n }\n .offcanvas-md.showing, .offcanvas-md.hiding, .offcanvas-md.show {\n visibility: visible;\n }\n}\n@media (min-width: 768px) {\n .offcanvas-md {\n --bs-offcanvas-height: auto;\n --bs-offcanvas-border-width: 0;\n background-color: transparent !important;\n }\n .offcanvas-md .offcanvas-header {\n display: none;\n }\n .offcanvas-md .offcanvas-body {\n display: flex;\n flex-grow: 0;\n padding: 0;\n overflow-y: visible;\n background-color: transparent !important;\n }\n}\n\n@media (max-width: 991.98px) {\n .offcanvas-lg {\n position: fixed;\n bottom: 0;\n z-index: var(--bs-offcanvas-zindex);\n display: flex;\n flex-direction: column;\n max-width: 100%;\n color: var(--bs-offcanvas-color);\n visibility: hidden;\n background-color: var(--bs-offcanvas-bg);\n background-clip: padding-box;\n outline: 0;\n transition: var(--bs-offcanvas-transition);\n }\n}\n@media (max-width: 991.98px) and (prefers-reduced-motion: reduce) {\n .offcanvas-lg {\n transition: none;\n }\n}\n@media (max-width: 991.98px) {\n .offcanvas-lg.offcanvas-start {\n top: 0;\n left: 0;\n width: var(--bs-offcanvas-width);\n border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n transform: translateX(-100%);\n }\n .offcanvas-lg.offcanvas-end {\n top: 0;\n right: 0;\n width: var(--bs-offcanvas-width);\n border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n transform: translateX(100%);\n }\n .offcanvas-lg.offcanvas-top {\n top: 0;\n right: 0;\n left: 0;\n height: var(--bs-offcanvas-height);\n max-height: 100%;\n border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n transform: translateY(-100%);\n }\n .offcanvas-lg.offcanvas-bottom {\n right: 0;\n left: 0;\n height: var(--bs-offcanvas-height);\n max-height: 100%;\n border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n transform: translateY(100%);\n }\n .offcanvas-lg.showing, .offcanvas-lg.show:not(.hiding) {\n transform: none;\n }\n .offcanvas-lg.showing, .offcanvas-lg.hiding, .offcanvas-lg.show {\n visibility: visible;\n }\n}\n@media (min-width: 992px) {\n .offcanvas-lg {\n --bs-offcanvas-height: auto;\n --bs-offcanvas-border-width: 0;\n background-color: transparent !important;\n }\n .offcanvas-lg .offcanvas-header {\n display: none;\n }\n .offcanvas-lg .offcanvas-body {\n display: flex;\n flex-grow: 0;\n padding: 0;\n overflow-y: visible;\n background-color: transparent !important;\n }\n}\n\n@media (max-width: 1199.98px) {\n .offcanvas-xl {\n position: fixed;\n bottom: 0;\n z-index: var(--bs-offcanvas-zindex);\n display: flex;\n flex-direction: column;\n max-width: 100%;\n color: var(--bs-offcanvas-color);\n visibility: hidden;\n background-color: var(--bs-offcanvas-bg);\n background-clip: padding-box;\n outline: 0;\n transition: var(--bs-offcanvas-transition);\n }\n}\n@media (max-width: 1199.98px) and (prefers-reduced-motion: reduce) {\n .offcanvas-xl {\n transition: none;\n }\n}\n@media (max-width: 1199.98px) {\n .offcanvas-xl.offcanvas-start {\n top: 0;\n left: 0;\n width: var(--bs-offcanvas-width);\n border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n transform: translateX(-100%);\n }\n .offcanvas-xl.offcanvas-end {\n top: 0;\n right: 0;\n width: var(--bs-offcanvas-width);\n border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n transform: translateX(100%);\n }\n .offcanvas-xl.offcanvas-top {\n top: 0;\n right: 0;\n left: 0;\n height: var(--bs-offcanvas-height);\n max-height: 100%;\n border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n transform: translateY(-100%);\n }\n .offcanvas-xl.offcanvas-bottom {\n right: 0;\n left: 0;\n height: var(--bs-offcanvas-height);\n max-height: 100%;\n border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n transform: translateY(100%);\n }\n .offcanvas-xl.showing, .offcanvas-xl.show:not(.hiding) {\n transform: none;\n }\n .offcanvas-xl.showing, .offcanvas-xl.hiding, .offcanvas-xl.show {\n visibility: visible;\n }\n}\n@media (min-width: 1200px) {\n .offcanvas-xl {\n --bs-offcanvas-height: auto;\n --bs-offcanvas-border-width: 0;\n background-color: transparent !important;\n }\n .offcanvas-xl .offcanvas-header {\n display: none;\n }\n .offcanvas-xl .offcanvas-body {\n display: flex;\n flex-grow: 0;\n padding: 0;\n overflow-y: visible;\n background-color: transparent !important;\n }\n}\n\n@media (max-width: 1399.98px) {\n .offcanvas-xxl {\n position: fixed;\n bottom: 0;\n z-index: var(--bs-offcanvas-zindex);\n display: flex;\n flex-direction: column;\n max-width: 100%;\n color: var(--bs-offcanvas-color);\n visibility: hidden;\n background-color: var(--bs-offcanvas-bg);\n background-clip: padding-box;\n outline: 0;\n transition: var(--bs-offcanvas-transition);\n }\n}\n@media (max-width: 1399.98px) and (prefers-reduced-motion: reduce) {\n .offcanvas-xxl {\n transition: none;\n }\n}\n@media (max-width: 1399.98px) {\n .offcanvas-xxl.offcanvas-start {\n top: 0;\n left: 0;\n width: var(--bs-offcanvas-width);\n border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n transform: translateX(-100%);\n }\n .offcanvas-xxl.offcanvas-end {\n top: 0;\n right: 0;\n width: var(--bs-offcanvas-width);\n border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n transform: translateX(100%);\n }\n .offcanvas-xxl.offcanvas-top {\n top: 0;\n right: 0;\n left: 0;\n height: var(--bs-offcanvas-height);\n max-height: 100%;\n border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n transform: translateY(-100%);\n }\n .offcanvas-xxl.offcanvas-bottom {\n right: 0;\n left: 0;\n height: var(--bs-offcanvas-height);\n max-height: 100%;\n border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n transform: translateY(100%);\n }\n .offcanvas-xxl.showing, .offcanvas-xxl.show:not(.hiding) {\n transform: none;\n }\n .offcanvas-xxl.showing, .offcanvas-xxl.hiding, .offcanvas-xxl.show {\n visibility: visible;\n }\n}\n@media (min-width: 1400px) {\n .offcanvas-xxl {\n --bs-offcanvas-height: auto;\n --bs-offcanvas-border-width: 0;\n background-color: transparent !important;\n }\n .offcanvas-xxl .offcanvas-header {\n display: none;\n }\n .offcanvas-xxl .offcanvas-body {\n display: flex;\n flex-grow: 0;\n padding: 0;\n overflow-y: visible;\n background-color: transparent !important;\n }\n}\n\n.offcanvas {\n position: fixed;\n bottom: 0;\n z-index: var(--bs-offcanvas-zindex);\n display: flex;\n flex-direction: column;\n max-width: 100%;\n color: var(--bs-offcanvas-color);\n visibility: hidden;\n background-color: var(--bs-offcanvas-bg);\n background-clip: padding-box;\n outline: 0;\n transition: var(--bs-offcanvas-transition);\n}\n@media (prefers-reduced-motion: reduce) {\n .offcanvas {\n transition: none;\n }\n}\n.offcanvas.offcanvas-start {\n top: 0;\n left: 0;\n width: var(--bs-offcanvas-width);\n border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n transform: translateX(-100%);\n}\n.offcanvas.offcanvas-end {\n top: 0;\n right: 0;\n width: var(--bs-offcanvas-width);\n border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n transform: translateX(100%);\n}\n.offcanvas.offcanvas-top {\n top: 0;\n right: 0;\n left: 0;\n height: var(--bs-offcanvas-height);\n max-height: 100%;\n border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n transform: translateY(-100%);\n}\n.offcanvas.offcanvas-bottom {\n right: 0;\n left: 0;\n height: var(--bs-offcanvas-height);\n max-height: 100%;\n border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);\n transform: translateY(100%);\n}\n.offcanvas.showing, .offcanvas.show:not(.hiding) {\n transform: none;\n}\n.offcanvas.showing, .offcanvas.hiding, .offcanvas.show {\n visibility: visible;\n}\n\n.offcanvas-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n z-index: 1040;\n width: 100vw;\n height: 100vh;\n background-color: #000;\n}\n.offcanvas-backdrop.fade {\n opacity: 0;\n}\n.offcanvas-backdrop.show {\n opacity: 0.5;\n}\n\n.offcanvas-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x);\n}\n.offcanvas-header .btn-close {\n padding: calc(var(--bs-offcanvas-padding-y) * 0.5) calc(var(--bs-offcanvas-padding-x) * 0.5);\n margin-top: calc(-0.5 * var(--bs-offcanvas-padding-y));\n margin-right: calc(-0.5 * var(--bs-offcanvas-padding-x));\n margin-bottom: calc(-0.5 * var(--bs-offcanvas-padding-y));\n}\n\n.offcanvas-title {\n margin-bottom: 0;\n line-height: var(--bs-offcanvas-title-line-height);\n}\n\n.offcanvas-body {\n flex-grow: 1;\n padding: var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x);\n overflow-y: auto;\n}\n\n.placeholder {\n display: inline-block;\n min-height: 1em;\n vertical-align: middle;\n cursor: wait;\n background-color: currentcolor;\n opacity: 0.5;\n}\n.placeholder.btn::before {\n display: inline-block;\n content: \"\";\n}\n\n.placeholder-xs {\n min-height: 0.6em;\n}\n\n.placeholder-sm {\n min-height: 0.8em;\n}\n\n.placeholder-lg {\n min-height: 1.2em;\n}\n\n.placeholder-glow .placeholder {\n animation: placeholder-glow 2s ease-in-out infinite;\n}\n\n@keyframes placeholder-glow {\n 50% {\n opacity: 0.2;\n }\n}\n.placeholder-wave {\n -webkit-mask-image: linear-gradient(130deg, #000 55%, rgba(0, 0, 0, 0.8) 75%, #000 95%);\n mask-image: linear-gradient(130deg, #000 55%, rgba(0, 0, 0, 0.8) 75%, #000 95%);\n -webkit-mask-size: 200% 100%;\n mask-size: 200% 100%;\n animation: placeholder-wave 2s linear infinite;\n}\n\n@keyframes placeholder-wave {\n 100% {\n -webkit-mask-position: -200% 0%;\n mask-position: -200% 0%;\n }\n}\n.clearfix::after {\n display: block;\n clear: both;\n content: \"\";\n}\n\n.text-bg-primary {\n color: #fff !important;\n background-color: RGBA(var(--bs-primary-rgb), var(--bs-bg-opacity, 1)) !important;\n}\n\n.text-bg-secondary {\n color: #fff !important;\n background-color: RGBA(var(--bs-secondary-rgb), var(--bs-bg-opacity, 1)) !important;\n}\n\n.text-bg-success {\n color: #fff !important;\n background-color: RGBA(var(--bs-success-rgb), var(--bs-bg-opacity, 1)) !important;\n}\n\n.text-bg-info {\n color: #000 !important;\n background-color: RGBA(var(--bs-info-rgb), var(--bs-bg-opacity, 1)) !important;\n}\n\n.text-bg-warning {\n color: #000 !important;\n background-color: RGBA(var(--bs-warning-rgb), var(--bs-bg-opacity, 1)) !important;\n}\n\n.text-bg-danger {\n color: #fff !important;\n background-color: RGBA(var(--bs-danger-rgb), var(--bs-bg-opacity, 1)) !important;\n}\n\n.text-bg-light {\n color: #000 !important;\n background-color: RGBA(var(--bs-light-rgb), var(--bs-bg-opacity, 1)) !important;\n}\n\n.text-bg-dark {\n color: #fff !important;\n background-color: RGBA(var(--bs-dark-rgb), var(--bs-bg-opacity, 1)) !important;\n}\n\n.link-primary {\n color: RGBA(var(--bs-primary-rgb), var(--bs-link-opacity, 1)) !important;\n -webkit-text-decoration-color: RGBA(var(--bs-primary-rgb), var(--bs-link-underline-opacity, 1)) !important;\n text-decoration-color: RGBA(var(--bs-primary-rgb), var(--bs-link-underline-opacity, 1)) !important;\n}\n.link-primary:hover, .link-primary:focus {\n color: RGBA(10, 88, 202, var(--bs-link-opacity, 1)) !important;\n -webkit-text-decoration-color: RGBA(10, 88, 202, var(--bs-link-underline-opacity, 1)) !important;\n text-decoration-color: RGBA(10, 88, 202, var(--bs-link-underline-opacity, 1)) !important;\n}\n\n.link-secondary {\n color: RGBA(var(--bs-secondary-rgb), var(--bs-link-opacity, 1)) !important;\n -webkit-text-decoration-color: RGBA(var(--bs-secondary-rgb), var(--bs-link-underline-opacity, 1)) !important;\n text-decoration-color: RGBA(var(--bs-secondary-rgb), var(--bs-link-underline-opacity, 1)) !important;\n}\n.link-secondary:hover, .link-secondary:focus {\n color: RGBA(86, 94, 100, var(--bs-link-opacity, 1)) !important;\n -webkit-text-decoration-color: RGBA(86, 94, 100, var(--bs-link-underline-opacity, 1)) !important;\n text-decoration-color: RGBA(86, 94, 100, var(--bs-link-underline-opacity, 1)) !important;\n}\n\n.link-success {\n color: RGBA(var(--bs-success-rgb), var(--bs-link-opacity, 1)) !important;\n -webkit-text-decoration-color: RGBA(var(--bs-success-rgb), var(--bs-link-underline-opacity, 1)) !important;\n text-decoration-color: RGBA(var(--bs-success-rgb), var(--bs-link-underline-opacity, 1)) !important;\n}\n.link-success:hover, .link-success:focus {\n color: RGBA(20, 108, 67, var(--bs-link-opacity, 1)) !important;\n -webkit-text-decoration-color: RGBA(20, 108, 67, var(--bs-link-underline-opacity, 1)) !important;\n text-decoration-color: RGBA(20, 108, 67, var(--bs-link-underline-opacity, 1)) !important;\n}\n\n.link-info {\n color: RGBA(var(--bs-info-rgb), var(--bs-link-opacity, 1)) !important;\n -webkit-text-decoration-color: RGBA(var(--bs-info-rgb), var(--bs-link-underline-opacity, 1)) !important;\n text-decoration-color: RGBA(var(--bs-info-rgb), var(--bs-link-underline-opacity, 1)) !important;\n}\n.link-info:hover, .link-info:focus {\n color: RGBA(61, 213, 243, var(--bs-link-opacity, 1)) !important;\n -webkit-text-decoration-color: RGBA(61, 213, 243, var(--bs-link-underline-opacity, 1)) !important;\n text-decoration-color: RGBA(61, 213, 243, var(--bs-link-underline-opacity, 1)) !important;\n}\n\n.link-warning {\n color: RGBA(var(--bs-warning-rgb), var(--bs-link-opacity, 1)) !important;\n -webkit-text-decoration-color: RGBA(var(--bs-warning-rgb), var(--bs-link-underline-opacity, 1)) !important;\n text-decoration-color: RGBA(var(--bs-warning-rgb), var(--bs-link-underline-opacity, 1)) !important;\n}\n.link-warning:hover, .link-warning:focus {\n color: RGBA(255, 205, 57, var(--bs-link-opacity, 1)) !important;\n -webkit-text-decoration-color: RGBA(255, 205, 57, var(--bs-link-underline-opacity, 1)) !important;\n text-decoration-color: RGBA(255, 205, 57, var(--bs-link-underline-opacity, 1)) !important;\n}\n\n.link-danger {\n color: RGBA(var(--bs-danger-rgb), var(--bs-link-opacity, 1)) !important;\n -webkit-text-decoration-color: RGBA(var(--bs-danger-rgb), var(--bs-link-underline-opacity, 1)) !important;\n text-decoration-color: RGBA(var(--bs-danger-rgb), var(--bs-link-underline-opacity, 1)) !important;\n}\n.link-danger:hover, .link-danger:focus {\n color: RGBA(176, 42, 55, var(--bs-link-opacity, 1)) !important;\n -webkit-text-decoration-color: RGBA(176, 42, 55, var(--bs-link-underline-opacity, 1)) !important;\n text-decoration-color: RGBA(176, 42, 55, var(--bs-link-underline-opacity, 1)) !important;\n}\n\n.link-light {\n color: RGBA(var(--bs-light-rgb), var(--bs-link-opacity, 1)) !important;\n -webkit-text-decoration-color: RGBA(var(--bs-light-rgb), var(--bs-link-underline-opacity, 1)) !important;\n text-decoration-color: RGBA(var(--bs-light-rgb), var(--bs-link-underline-opacity, 1)) !important;\n}\n.link-light:hover, .link-light:focus {\n color: RGBA(249, 250, 251, var(--bs-link-opacity, 1)) !important;\n -webkit-text-decoration-color: RGBA(249, 250, 251, var(--bs-link-underline-opacity, 1)) !important;\n text-decoration-color: RGBA(249, 250, 251, var(--bs-link-underline-opacity, 1)) !important;\n}\n\n.link-dark {\n color: RGBA(var(--bs-dark-rgb), var(--bs-link-opacity, 1)) !important;\n -webkit-text-decoration-color: RGBA(var(--bs-dark-rgb), var(--bs-link-underline-opacity, 1)) !important;\n text-decoration-color: RGBA(var(--bs-dark-rgb), var(--bs-link-underline-opacity, 1)) !important;\n}\n.link-dark:hover, .link-dark:focus {\n color: RGBA(26, 30, 33, var(--bs-link-opacity, 1)) !important;\n -webkit-text-decoration-color: RGBA(26, 30, 33, var(--bs-link-underline-opacity, 1)) !important;\n text-decoration-color: RGBA(26, 30, 33, var(--bs-link-underline-opacity, 1)) !important;\n}\n\n.link-body-emphasis {\n color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-opacity, 1)) !important;\n -webkit-text-decoration-color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 1)) !important;\n text-decoration-color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 1)) !important;\n}\n.link-body-emphasis:hover, .link-body-emphasis:focus {\n color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-opacity, 0.75)) !important;\n -webkit-text-decoration-color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 0.75)) !important;\n text-decoration-color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 0.75)) !important;\n}\n\n.focus-ring:focus {\n outline: 0;\n box-shadow: var(--bs-focus-ring-x, 0) var(--bs-focus-ring-y, 0) var(--bs-focus-ring-blur, 0) var(--bs-focus-ring-width) var(--bs-focus-ring-color);\n}\n\n.icon-link {\n display: inline-flex;\n gap: 0.375rem;\n align-items: center;\n -webkit-text-decoration-color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 0.5));\n text-decoration-color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 0.5));\n text-underline-offset: 0.25em;\n -webkit-backface-visibility: hidden;\n backface-visibility: hidden;\n}\n.icon-link > .bi {\n flex-shrink: 0;\n width: 1em;\n height: 1em;\n fill: currentcolor;\n transition: 0.2s ease-in-out transform;\n}\n@media (prefers-reduced-motion: reduce) {\n .icon-link > .bi {\n transition: none;\n }\n}\n\n.icon-link-hover:hover > .bi, .icon-link-hover:focus-visible > .bi {\n transform: var(--bs-icon-link-transform, translate3d(0.25em, 0, 0));\n}\n\n.ratio {\n position: relative;\n width: 100%;\n}\n.ratio::before {\n display: block;\n padding-top: var(--bs-aspect-ratio);\n content: \"\";\n}\n.ratio > * {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n}\n\n.ratio-1x1 {\n --bs-aspect-ratio: 100%;\n}\n\n.ratio-4x3 {\n --bs-aspect-ratio: 75%;\n}\n\n.ratio-16x9 {\n --bs-aspect-ratio: 56.25%;\n}\n\n.ratio-21x9 {\n --bs-aspect-ratio: 42.8571428571%;\n}\n\n.fixed-top {\n position: fixed;\n top: 0;\n right: 0;\n left: 0;\n z-index: 1030;\n}\n\n.fixed-bottom {\n position: fixed;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1030;\n}\n\n.sticky-top {\n position: -webkit-sticky;\n position: sticky;\n top: 0;\n z-index: 1020;\n}\n\n.sticky-bottom {\n position: -webkit-sticky;\n position: sticky;\n bottom: 0;\n z-index: 1020;\n}\n\n@media (min-width: 576px) {\n .sticky-sm-top {\n position: -webkit-sticky;\n position: sticky;\n top: 0;\n z-index: 1020;\n }\n .sticky-sm-bottom {\n position: -webkit-sticky;\n position: sticky;\n bottom: 0;\n z-index: 1020;\n }\n}\n@media (min-width: 768px) {\n .sticky-md-top {\n position: -webkit-sticky;\n position: sticky;\n top: 0;\n z-index: 1020;\n }\n .sticky-md-bottom {\n position: -webkit-sticky;\n position: sticky;\n bottom: 0;\n z-index: 1020;\n }\n}\n@media (min-width: 992px) {\n .sticky-lg-top {\n position: -webkit-sticky;\n position: sticky;\n top: 0;\n z-index: 1020;\n }\n .sticky-lg-bottom {\n position: -webkit-sticky;\n position: sticky;\n bottom: 0;\n z-index: 1020;\n }\n}\n@media (min-width: 1200px) {\n .sticky-xl-top {\n position: -webkit-sticky;\n position: sticky;\n top: 0;\n z-index: 1020;\n }\n .sticky-xl-bottom {\n position: -webkit-sticky;\n position: sticky;\n bottom: 0;\n z-index: 1020;\n }\n}\n@media (min-width: 1400px) {\n .sticky-xxl-top {\n position: -webkit-sticky;\n position: sticky;\n top: 0;\n z-index: 1020;\n }\n .sticky-xxl-bottom {\n position: -webkit-sticky;\n position: sticky;\n bottom: 0;\n z-index: 1020;\n }\n}\n.hstack {\n display: flex;\n flex-direction: row;\n align-items: center;\n align-self: stretch;\n}\n\n.vstack {\n display: flex;\n flex: 1 1 auto;\n flex-direction: column;\n align-self: stretch;\n}\n\n.visually-hidden,\n.visually-hidden-focusable:not(:focus):not(:focus-within) {\n width: 1px !important;\n height: 1px !important;\n padding: 0 !important;\n margin: -1px !important;\n overflow: hidden !important;\n clip: rect(0, 0, 0, 0) !important;\n white-space: nowrap !important;\n border: 0 !important;\n}\n.visually-hidden:not(caption),\n.visually-hidden-focusable:not(:focus):not(:focus-within):not(caption) {\n position: absolute !important;\n}\n\n.stretched-link::after {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1;\n content: \"\";\n}\n\n.text-truncate {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.vr {\n display: inline-block;\n align-self: stretch;\n width: var(--bs-border-width);\n min-height: 1em;\n background-color: currentcolor;\n opacity: 0.25;\n}\n\n.align-baseline {\n vertical-align: baseline !important;\n}\n\n.align-top {\n vertical-align: top !important;\n}\n\n.align-middle {\n vertical-align: middle !important;\n}\n\n.align-bottom {\n vertical-align: bottom !important;\n}\n\n.align-text-bottom {\n vertical-align: text-bottom !important;\n}\n\n.align-text-top {\n vertical-align: text-top !important;\n}\n\n.float-start {\n float: left !important;\n}\n\n.float-end {\n float: right !important;\n}\n\n.float-none {\n float: none !important;\n}\n\n.object-fit-contain {\n -o-object-fit: contain !important;\n object-fit: contain !important;\n}\n\n.object-fit-cover {\n -o-object-fit: cover !important;\n object-fit: cover !important;\n}\n\n.object-fit-fill {\n -o-object-fit: fill !important;\n object-fit: fill !important;\n}\n\n.object-fit-scale {\n -o-object-fit: scale-down !important;\n object-fit: scale-down !important;\n}\n\n.object-fit-none {\n -o-object-fit: none !important;\n object-fit: none !important;\n}\n\n.opacity-0 {\n opacity: 0 !important;\n}\n\n.opacity-25 {\n opacity: 0.25 !important;\n}\n\n.opacity-50 {\n opacity: 0.5 !important;\n}\n\n.opacity-75 {\n opacity: 0.75 !important;\n}\n\n.opacity-100 {\n opacity: 1 !important;\n}\n\n.overflow-auto {\n overflow: auto !important;\n}\n\n.overflow-hidden {\n overflow: hidden !important;\n}\n\n.overflow-visible {\n overflow: visible !important;\n}\n\n.overflow-scroll {\n overflow: scroll !important;\n}\n\n.overflow-x-auto {\n overflow-x: auto !important;\n}\n\n.overflow-x-hidden {\n overflow-x: hidden !important;\n}\n\n.overflow-x-visible {\n overflow-x: visible !important;\n}\n\n.overflow-x-scroll {\n overflow-x: scroll !important;\n}\n\n.overflow-y-auto {\n overflow-y: auto !important;\n}\n\n.overflow-y-hidden {\n overflow-y: hidden !important;\n}\n\n.overflow-y-visible {\n overflow-y: visible !important;\n}\n\n.overflow-y-scroll {\n overflow-y: scroll !important;\n}\n\n.d-inline {\n display: inline !important;\n}\n\n.d-inline-block {\n display: inline-block !important;\n}\n\n.d-block {\n display: block !important;\n}\n\n.d-grid {\n display: grid !important;\n}\n\n.d-inline-grid {\n display: inline-grid !important;\n}\n\n.d-table {\n display: table !important;\n}\n\n.d-table-row {\n display: table-row !important;\n}\n\n.d-table-cell {\n display: table-cell !important;\n}\n\n.d-flex {\n display: flex !important;\n}\n\n.d-inline-flex {\n display: inline-flex !important;\n}\n\n.d-none {\n display: none !important;\n}\n\n.shadow {\n box-shadow: var(--bs-box-shadow) !important;\n}\n\n.shadow-sm {\n box-shadow: var(--bs-box-shadow-sm) !important;\n}\n\n.shadow-lg {\n box-shadow: var(--bs-box-shadow-lg) !important;\n}\n\n.shadow-none {\n box-shadow: none !important;\n}\n\n.focus-ring-primary {\n --bs-focus-ring-color: rgba(var(--bs-primary-rgb), var(--bs-focus-ring-opacity));\n}\n\n.focus-ring-secondary {\n --bs-focus-ring-color: rgba(var(--bs-secondary-rgb), var(--bs-focus-ring-opacity));\n}\n\n.focus-ring-success {\n --bs-focus-ring-color: rgba(var(--bs-success-rgb), var(--bs-focus-ring-opacity));\n}\n\n.focus-ring-info {\n --bs-focus-ring-color: rgba(var(--bs-info-rgb), var(--bs-focus-ring-opacity));\n}\n\n.focus-ring-warning {\n --bs-focus-ring-color: rgba(var(--bs-warning-rgb), var(--bs-focus-ring-opacity));\n}\n\n.focus-ring-danger {\n --bs-focus-ring-color: rgba(var(--bs-danger-rgb), var(--bs-focus-ring-opacity));\n}\n\n.focus-ring-light {\n --bs-focus-ring-color: rgba(var(--bs-light-rgb), var(--bs-focus-ring-opacity));\n}\n\n.focus-ring-dark {\n --bs-focus-ring-color: rgba(var(--bs-dark-rgb), var(--bs-focus-ring-opacity));\n}\n\n.position-static {\n position: static !important;\n}\n\n.position-relative {\n position: relative !important;\n}\n\n.position-absolute {\n position: absolute !important;\n}\n\n.position-fixed {\n position: fixed !important;\n}\n\n.position-sticky {\n position: -webkit-sticky !important;\n position: sticky !important;\n}\n\n.top-0 {\n top: 0 !important;\n}\n\n.top-50 {\n top: 50% !important;\n}\n\n.top-100 {\n top: 100% !important;\n}\n\n.bottom-0 {\n bottom: 0 !important;\n}\n\n.bottom-50 {\n bottom: 50% !important;\n}\n\n.bottom-100 {\n bottom: 100% !important;\n}\n\n.start-0 {\n left: 0 !important;\n}\n\n.start-50 {\n left: 50% !important;\n}\n\n.start-100 {\n left: 100% !important;\n}\n\n.end-0 {\n right: 0 !important;\n}\n\n.end-50 {\n right: 50% !important;\n}\n\n.end-100 {\n right: 100% !important;\n}\n\n.translate-middle {\n transform: translate(-50%, -50%) !important;\n}\n\n.translate-middle-x {\n transform: translateX(-50%) !important;\n}\n\n.translate-middle-y {\n transform: translateY(-50%) !important;\n}\n\n.border {\n border: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important;\n}\n\n.border-0 {\n border: 0 !important;\n}\n\n.border-top {\n border-top: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important;\n}\n\n.border-top-0 {\n border-top: 0 !important;\n}\n\n.border-end {\n border-right: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important;\n}\n\n.border-end-0 {\n border-right: 0 !important;\n}\n\n.border-bottom {\n border-bottom: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important;\n}\n\n.border-bottom-0 {\n border-bottom: 0 !important;\n}\n\n.border-start {\n border-left: var(--bs-border-width) var(--bs-border-style) var(--bs-border-color) !important;\n}\n\n.border-start-0 {\n border-left: 0 !important;\n}\n\n.border-primary {\n --bs-border-opacity: 1;\n border-color: rgba(var(--bs-primary-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-secondary {\n --bs-border-opacity: 1;\n border-color: rgba(var(--bs-secondary-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-success {\n --bs-border-opacity: 1;\n border-color: rgba(var(--bs-success-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-info {\n --bs-border-opacity: 1;\n border-color: rgba(var(--bs-info-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-warning {\n --bs-border-opacity: 1;\n border-color: rgba(var(--bs-warning-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-danger {\n --bs-border-opacity: 1;\n border-color: rgba(var(--bs-danger-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-light {\n --bs-border-opacity: 1;\n border-color: rgba(var(--bs-light-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-dark {\n --bs-border-opacity: 1;\n border-color: rgba(var(--bs-dark-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-black {\n --bs-border-opacity: 1;\n border-color: rgba(var(--bs-black-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-white {\n --bs-border-opacity: 1;\n border-color: rgba(var(--bs-white-rgb), var(--bs-border-opacity)) !important;\n}\n\n.border-primary-subtle {\n border-color: var(--bs-primary-border-subtle) !important;\n}\n\n.border-secondary-subtle {\n border-color: var(--bs-secondary-border-subtle) !important;\n}\n\n.border-success-subtle {\n border-color: var(--bs-success-border-subtle) !important;\n}\n\n.border-info-subtle {\n border-color: var(--bs-info-border-subtle) !important;\n}\n\n.border-warning-subtle {\n border-color: var(--bs-warning-border-subtle) !important;\n}\n\n.border-danger-subtle {\n border-color: var(--bs-danger-border-subtle) !important;\n}\n\n.border-light-subtle {\n border-color: var(--bs-light-border-subtle) !important;\n}\n\n.border-dark-subtle {\n border-color: var(--bs-dark-border-subtle) !important;\n}\n\n.border-1 {\n border-width: 1px !important;\n}\n\n.border-2 {\n border-width: 2px !important;\n}\n\n.border-3 {\n border-width: 3px !important;\n}\n\n.border-4 {\n border-width: 4px !important;\n}\n\n.border-5 {\n border-width: 5px !important;\n}\n\n.border-opacity-10 {\n --bs-border-opacity: 0.1;\n}\n\n.border-opacity-25 {\n --bs-border-opacity: 0.25;\n}\n\n.border-opacity-50 {\n --bs-border-opacity: 0.5;\n}\n\n.border-opacity-75 {\n --bs-border-opacity: 0.75;\n}\n\n.border-opacity-100 {\n --bs-border-opacity: 1;\n}\n\n.w-25 {\n width: 25% !important;\n}\n\n.w-50 {\n width: 50% !important;\n}\n\n.w-75 {\n width: 75% !important;\n}\n\n.w-100 {\n width: 100% !important;\n}\n\n.w-auto {\n width: auto !important;\n}\n\n.mw-100 {\n max-width: 100% !important;\n}\n\n.vw-100 {\n width: 100vw !important;\n}\n\n.min-vw-100 {\n min-width: 100vw !important;\n}\n\n.h-25 {\n height: 25% !important;\n}\n\n.h-50 {\n height: 50% !important;\n}\n\n.h-75 {\n height: 75% !important;\n}\n\n.h-100 {\n height: 100% !important;\n}\n\n.h-auto {\n height: auto !important;\n}\n\n.mh-100 {\n max-height: 100% !important;\n}\n\n.vh-100 {\n height: 100vh !important;\n}\n\n.min-vh-100 {\n min-height: 100vh !important;\n}\n\n.flex-fill {\n flex: 1 1 auto !important;\n}\n\n.flex-row {\n flex-direction: row !important;\n}\n\n.flex-column {\n flex-direction: column !important;\n}\n\n.flex-row-reverse {\n flex-direction: row-reverse !important;\n}\n\n.flex-column-reverse {\n flex-direction: column-reverse !important;\n}\n\n.flex-grow-0 {\n flex-grow: 0 !important;\n}\n\n.flex-grow-1 {\n flex-grow: 1 !important;\n}\n\n.flex-shrink-0 {\n flex-shrink: 0 !important;\n}\n\n.flex-shrink-1 {\n flex-shrink: 1 !important;\n}\n\n.flex-wrap {\n flex-wrap: wrap !important;\n}\n\n.flex-nowrap {\n flex-wrap: nowrap !important;\n}\n\n.flex-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n}\n\n.justify-content-start {\n justify-content: flex-start !important;\n}\n\n.justify-content-end {\n justify-content: flex-end !important;\n}\n\n.justify-content-center {\n justify-content: center !important;\n}\n\n.justify-content-between {\n justify-content: space-between !important;\n}\n\n.justify-content-around {\n justify-content: space-around !important;\n}\n\n.justify-content-evenly {\n justify-content: space-evenly !important;\n}\n\n.align-items-start {\n align-items: flex-start !important;\n}\n\n.align-items-end {\n align-items: flex-end !important;\n}\n\n.align-items-center {\n align-items: center !important;\n}\n\n.align-items-baseline {\n align-items: baseline !important;\n}\n\n.align-items-stretch {\n align-items: stretch !important;\n}\n\n.align-content-start {\n align-content: flex-start !important;\n}\n\n.align-content-end {\n align-content: flex-end !important;\n}\n\n.align-content-center {\n align-content: center !important;\n}\n\n.align-content-between {\n align-content: space-between !important;\n}\n\n.align-content-around {\n align-content: space-around !important;\n}\n\n.align-content-stretch {\n align-content: stretch !important;\n}\n\n.align-self-auto {\n align-self: auto !important;\n}\n\n.align-self-start {\n align-self: flex-start !important;\n}\n\n.align-self-end {\n align-self: flex-end !important;\n}\n\n.align-self-center {\n align-self: center !important;\n}\n\n.align-self-baseline {\n align-self: baseline !important;\n}\n\n.align-self-stretch {\n align-self: stretch !important;\n}\n\n.order-first {\n order: -1 !important;\n}\n\n.order-0 {\n order: 0 !important;\n}\n\n.order-1 {\n order: 1 !important;\n}\n\n.order-2 {\n order: 2 !important;\n}\n\n.order-3 {\n order: 3 !important;\n}\n\n.order-4 {\n order: 4 !important;\n}\n\n.order-5 {\n order: 5 !important;\n}\n\n.order-last {\n order: 6 !important;\n}\n\n.m-0 {\n margin: 0 !important;\n}\n\n.m-1 {\n margin: 0.25rem !important;\n}\n\n.m-2 {\n margin: 0.5rem !important;\n}\n\n.m-3 {\n margin: 1rem !important;\n}\n\n.m-4 {\n margin: 1.5rem !important;\n}\n\n.m-5 {\n margin: 3rem !important;\n}\n\n.m-auto {\n margin: auto !important;\n}\n\n.mx-0 {\n margin-right: 0 !important;\n margin-left: 0 !important;\n}\n\n.mx-1 {\n margin-right: 0.25rem !important;\n margin-left: 0.25rem !important;\n}\n\n.mx-2 {\n margin-right: 0.5rem !important;\n margin-left: 0.5rem !important;\n}\n\n.mx-3 {\n margin-right: 1rem !important;\n margin-left: 1rem !important;\n}\n\n.mx-4 {\n margin-right: 1.5rem !important;\n margin-left: 1.5rem !important;\n}\n\n.mx-5 {\n margin-right: 3rem !important;\n margin-left: 3rem !important;\n}\n\n.mx-auto {\n margin-right: auto !important;\n margin-left: auto !important;\n}\n\n.my-0 {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n}\n\n.my-1 {\n margin-top: 0.25rem !important;\n margin-bottom: 0.25rem !important;\n}\n\n.my-2 {\n margin-top: 0.5rem !important;\n margin-bottom: 0.5rem !important;\n}\n\n.my-3 {\n margin-top: 1rem !important;\n margin-bottom: 1rem !important;\n}\n\n.my-4 {\n margin-top: 1.5rem !important;\n margin-bottom: 1.5rem !important;\n}\n\n.my-5 {\n margin-top: 3rem !important;\n margin-bottom: 3rem !important;\n}\n\n.my-auto {\n margin-top: auto !important;\n margin-bottom: auto !important;\n}\n\n.mt-0 {\n margin-top: 0 !important;\n}\n\n.mt-1 {\n margin-top: 0.25rem !important;\n}\n\n.mt-2 {\n margin-top: 0.5rem !important;\n}\n\n.mt-3 {\n margin-top: 1rem !important;\n}\n\n.mt-4 {\n margin-top: 1.5rem !important;\n}\n\n.mt-5 {\n margin-top: 3rem !important;\n}\n\n.mt-auto {\n margin-top: auto !important;\n}\n\n.me-0 {\n margin-right: 0 !important;\n}\n\n.me-1 {\n margin-right: 0.25rem !important;\n}\n\n.me-2 {\n margin-right: 0.5rem !important;\n}\n\n.me-3 {\n margin-right: 1rem !important;\n}\n\n.me-4 {\n margin-right: 1.5rem !important;\n}\n\n.me-5 {\n margin-right: 3rem !important;\n}\n\n.me-auto {\n margin-right: auto !important;\n}\n\n.mb-0 {\n margin-bottom: 0 !important;\n}\n\n.mb-1 {\n margin-bottom: 0.25rem !important;\n}\n\n.mb-2 {\n margin-bottom: 0.5rem !important;\n}\n\n.mb-3 {\n margin-bottom: 1rem !important;\n}\n\n.mb-4 {\n margin-bottom: 1.5rem !important;\n}\n\n.mb-5 {\n margin-bottom: 3rem !important;\n}\n\n.mb-auto {\n margin-bottom: auto !important;\n}\n\n.ms-0 {\n margin-left: 0 !important;\n}\n\n.ms-1 {\n margin-left: 0.25rem !important;\n}\n\n.ms-2 {\n margin-left: 0.5rem !important;\n}\n\n.ms-3 {\n margin-left: 1rem !important;\n}\n\n.ms-4 {\n margin-left: 1.5rem !important;\n}\n\n.ms-5 {\n margin-left: 3rem !important;\n}\n\n.ms-auto {\n margin-left: auto !important;\n}\n\n.p-0 {\n padding: 0 !important;\n}\n\n.p-1 {\n padding: 0.25rem !important;\n}\n\n.p-2 {\n padding: 0.5rem !important;\n}\n\n.p-3 {\n padding: 1rem !important;\n}\n\n.p-4 {\n padding: 1.5rem !important;\n}\n\n.p-5 {\n padding: 3rem !important;\n}\n\n.px-0 {\n padding-right: 0 !important;\n padding-left: 0 !important;\n}\n\n.px-1 {\n padding-right: 0.25rem !important;\n padding-left: 0.25rem !important;\n}\n\n.px-2 {\n padding-right: 0.5rem !important;\n padding-left: 0.5rem !important;\n}\n\n.px-3 {\n padding-right: 1rem !important;\n padding-left: 1rem !important;\n}\n\n.px-4 {\n padding-right: 1.5rem !important;\n padding-left: 1.5rem !important;\n}\n\n.px-5 {\n padding-right: 3rem !important;\n padding-left: 3rem !important;\n}\n\n.py-0 {\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n}\n\n.py-1 {\n padding-top: 0.25rem !important;\n padding-bottom: 0.25rem !important;\n}\n\n.py-2 {\n padding-top: 0.5rem !important;\n padding-bottom: 0.5rem !important;\n}\n\n.py-3 {\n padding-top: 1rem !important;\n padding-bottom: 1rem !important;\n}\n\n.py-4 {\n padding-top: 1.5rem !important;\n padding-bottom: 1.5rem !important;\n}\n\n.py-5 {\n padding-top: 3rem !important;\n padding-bottom: 3rem !important;\n}\n\n.pt-0 {\n padding-top: 0 !important;\n}\n\n.pt-1 {\n padding-top: 0.25rem !important;\n}\n\n.pt-2 {\n padding-top: 0.5rem !important;\n}\n\n.pt-3 {\n padding-top: 1rem !important;\n}\n\n.pt-4 {\n padding-top: 1.5rem !important;\n}\n\n.pt-5 {\n padding-top: 3rem !important;\n}\n\n.pe-0 {\n padding-right: 0 !important;\n}\n\n.pe-1 {\n padding-right: 0.25rem !important;\n}\n\n.pe-2 {\n padding-right: 0.5rem !important;\n}\n\n.pe-3 {\n padding-right: 1rem !important;\n}\n\n.pe-4 {\n padding-right: 1.5rem !important;\n}\n\n.pe-5 {\n padding-right: 3rem !important;\n}\n\n.pb-0 {\n padding-bottom: 0 !important;\n}\n\n.pb-1 {\n padding-bottom: 0.25rem !important;\n}\n\n.pb-2 {\n padding-bottom: 0.5rem !important;\n}\n\n.pb-3 {\n padding-bottom: 1rem !important;\n}\n\n.pb-4 {\n padding-bottom: 1.5rem !important;\n}\n\n.pb-5 {\n padding-bottom: 3rem !important;\n}\n\n.ps-0 {\n padding-left: 0 !important;\n}\n\n.ps-1 {\n padding-left: 0.25rem !important;\n}\n\n.ps-2 {\n padding-left: 0.5rem !important;\n}\n\n.ps-3 {\n padding-left: 1rem !important;\n}\n\n.ps-4 {\n padding-left: 1.5rem !important;\n}\n\n.ps-5 {\n padding-left: 3rem !important;\n}\n\n.gap-0 {\n gap: 0 !important;\n}\n\n.gap-1 {\n gap: 0.25rem !important;\n}\n\n.gap-2 {\n gap: 0.5rem !important;\n}\n\n.gap-3 {\n gap: 1rem !important;\n}\n\n.gap-4 {\n gap: 1.5rem !important;\n}\n\n.gap-5 {\n gap: 3rem !important;\n}\n\n.row-gap-0 {\n row-gap: 0 !important;\n}\n\n.row-gap-1 {\n row-gap: 0.25rem !important;\n}\n\n.row-gap-2 {\n row-gap: 0.5rem !important;\n}\n\n.row-gap-3 {\n row-gap: 1rem !important;\n}\n\n.row-gap-4 {\n row-gap: 1.5rem !important;\n}\n\n.row-gap-5 {\n row-gap: 3rem !important;\n}\n\n.column-gap-0 {\n -moz-column-gap: 0 !important;\n column-gap: 0 !important;\n}\n\n.column-gap-1 {\n -moz-column-gap: 0.25rem !important;\n column-gap: 0.25rem !important;\n}\n\n.column-gap-2 {\n -moz-column-gap: 0.5rem !important;\n column-gap: 0.5rem !important;\n}\n\n.column-gap-3 {\n -moz-column-gap: 1rem !important;\n column-gap: 1rem !important;\n}\n\n.column-gap-4 {\n -moz-column-gap: 1.5rem !important;\n column-gap: 1.5rem !important;\n}\n\n.column-gap-5 {\n -moz-column-gap: 3rem !important;\n column-gap: 3rem !important;\n}\n\n.font-monospace {\n font-family: var(--bs-font-monospace) !important;\n}\n\n.fs-1 {\n font-size: calc(1.375rem + 1.5vw) !important;\n}\n\n.fs-2 {\n font-size: calc(1.325rem + 0.9vw) !important;\n}\n\n.fs-3 {\n font-size: calc(1.3rem + 0.6vw) !important;\n}\n\n.fs-4 {\n font-size: calc(1.275rem + 0.3vw) !important;\n}\n\n.fs-5 {\n font-size: 1.25rem !important;\n}\n\n.fs-6 {\n font-size: 1rem !important;\n}\n\n.fst-italic {\n font-style: italic !important;\n}\n\n.fst-normal {\n font-style: normal !important;\n}\n\n.fw-lighter {\n font-weight: lighter !important;\n}\n\n.fw-light {\n font-weight: 300 !important;\n}\n\n.fw-normal {\n font-weight: 400 !important;\n}\n\n.fw-medium {\n font-weight: 500 !important;\n}\n\n.fw-semibold {\n font-weight: 600 !important;\n}\n\n.fw-bold {\n font-weight: 700 !important;\n}\n\n.fw-bolder {\n font-weight: bolder !important;\n}\n\n.lh-1 {\n line-height: 1 !important;\n}\n\n.lh-sm {\n line-height: 1.25 !important;\n}\n\n.lh-base {\n line-height: 1.5 !important;\n}\n\n.lh-lg {\n line-height: 2 !important;\n}\n\n.text-start {\n text-align: left !important;\n}\n\n.text-end {\n text-align: right !important;\n}\n\n.text-center {\n text-align: center !important;\n}\n\n.text-decoration-none {\n text-decoration: none !important;\n}\n\n.text-decoration-underline {\n text-decoration: underline !important;\n}\n\n.text-decoration-line-through {\n text-decoration: line-through !important;\n}\n\n.text-lowercase {\n text-transform: lowercase !important;\n}\n\n.text-uppercase {\n text-transform: uppercase !important;\n}\n\n.text-capitalize {\n text-transform: capitalize !important;\n}\n\n.text-wrap {\n white-space: normal !important;\n}\n\n.text-nowrap {\n white-space: nowrap !important;\n}\n\n/* rtl:begin:remove */\n.text-break {\n word-wrap: break-word !important;\n word-break: break-word !important;\n}\n\n/* rtl:end:remove */\n.text-primary {\n --bs-text-opacity: 1;\n color: rgba(var(--bs-primary-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-secondary {\n --bs-text-opacity: 1;\n color: rgba(var(--bs-secondary-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-success {\n --bs-text-opacity: 1;\n color: rgba(var(--bs-success-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-info {\n --bs-text-opacity: 1;\n color: rgba(var(--bs-info-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-warning {\n --bs-text-opacity: 1;\n color: rgba(var(--bs-warning-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-danger {\n --bs-text-opacity: 1;\n color: rgba(var(--bs-danger-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-light {\n --bs-text-opacity: 1;\n color: rgba(var(--bs-light-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-dark {\n --bs-text-opacity: 1;\n color: rgba(var(--bs-dark-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-black {\n --bs-text-opacity: 1;\n color: rgba(var(--bs-black-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-white {\n --bs-text-opacity: 1;\n color: rgba(var(--bs-white-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-body {\n --bs-text-opacity: 1;\n color: rgba(var(--bs-body-color-rgb), var(--bs-text-opacity)) !important;\n}\n\n.text-muted {\n --bs-text-opacity: 1;\n color: var(--bs-secondary-color) !important;\n}\n\n.text-black-50 {\n --bs-text-opacity: 1;\n color: rgba(0, 0, 0, 0.5) !important;\n}\n\n.text-white-50 {\n --bs-text-opacity: 1;\n color: rgba(255, 255, 255, 0.5) !important;\n}\n\n.text-body-secondary {\n --bs-text-opacity: 1;\n color: var(--bs-secondary-color) !important;\n}\n\n.text-body-tertiary {\n --bs-text-opacity: 1;\n color: var(--bs-tertiary-color) !important;\n}\n\n.text-body-emphasis {\n --bs-text-opacity: 1;\n color: var(--bs-emphasis-color) !important;\n}\n\n.text-reset {\n --bs-text-opacity: 1;\n color: inherit !important;\n}\n\n.text-opacity-25 {\n --bs-text-opacity: 0.25;\n}\n\n.text-opacity-50 {\n --bs-text-opacity: 0.5;\n}\n\n.text-opacity-75 {\n --bs-text-opacity: 0.75;\n}\n\n.text-opacity-100 {\n --bs-text-opacity: 1;\n}\n\n.text-primary-emphasis {\n color: var(--bs-primary-text-emphasis) !important;\n}\n\n.text-secondary-emphasis {\n color: var(--bs-secondary-text-emphasis) !important;\n}\n\n.text-success-emphasis {\n color: var(--bs-success-text-emphasis) !important;\n}\n\n.text-info-emphasis {\n color: var(--bs-info-text-emphasis) !important;\n}\n\n.text-warning-emphasis {\n color: var(--bs-warning-text-emphasis) !important;\n}\n\n.text-danger-emphasis {\n color: var(--bs-danger-text-emphasis) !important;\n}\n\n.text-light-emphasis {\n color: var(--bs-light-text-emphasis) !important;\n}\n\n.text-dark-emphasis {\n color: var(--bs-dark-text-emphasis) !important;\n}\n\n.link-opacity-10 {\n --bs-link-opacity: 0.1;\n}\n\n.link-opacity-10-hover:hover {\n --bs-link-opacity: 0.1;\n}\n\n.link-opacity-25 {\n --bs-link-opacity: 0.25;\n}\n\n.link-opacity-25-hover:hover {\n --bs-link-opacity: 0.25;\n}\n\n.link-opacity-50 {\n --bs-link-opacity: 0.5;\n}\n\n.link-opacity-50-hover:hover {\n --bs-link-opacity: 0.5;\n}\n\n.link-opacity-75 {\n --bs-link-opacity: 0.75;\n}\n\n.link-opacity-75-hover:hover {\n --bs-link-opacity: 0.75;\n}\n\n.link-opacity-100 {\n --bs-link-opacity: 1;\n}\n\n.link-opacity-100-hover:hover {\n --bs-link-opacity: 1;\n}\n\n.link-offset-1 {\n text-underline-offset: 0.125em !important;\n}\n\n.link-offset-1-hover:hover {\n text-underline-offset: 0.125em !important;\n}\n\n.link-offset-2 {\n text-underline-offset: 0.25em !important;\n}\n\n.link-offset-2-hover:hover {\n text-underline-offset: 0.25em !important;\n}\n\n.link-offset-3 {\n text-underline-offset: 0.375em !important;\n}\n\n.link-offset-3-hover:hover {\n text-underline-offset: 0.375em !important;\n}\n\n.link-underline-primary {\n --bs-link-underline-opacity: 1;\n -webkit-text-decoration-color: rgba(var(--bs-primary-rgb), var(--bs-link-underline-opacity)) !important;\n text-decoration-color: rgba(var(--bs-primary-rgb), var(--bs-link-underline-opacity)) !important;\n}\n\n.link-underline-secondary {\n --bs-link-underline-opacity: 1;\n -webkit-text-decoration-color: rgba(var(--bs-secondary-rgb), var(--bs-link-underline-opacity)) !important;\n text-decoration-color: rgba(var(--bs-secondary-rgb), var(--bs-link-underline-opacity)) !important;\n}\n\n.link-underline-success {\n --bs-link-underline-opacity: 1;\n -webkit-text-decoration-color: rgba(var(--bs-success-rgb), var(--bs-link-underline-opacity)) !important;\n text-decoration-color: rgba(var(--bs-success-rgb), var(--bs-link-underline-opacity)) !important;\n}\n\n.link-underline-info {\n --bs-link-underline-opacity: 1;\n -webkit-text-decoration-color: rgba(var(--bs-info-rgb), var(--bs-link-underline-opacity)) !important;\n text-decoration-color: rgba(var(--bs-info-rgb), var(--bs-link-underline-opacity)) !important;\n}\n\n.link-underline-warning {\n --bs-link-underline-opacity: 1;\n -webkit-text-decoration-color: rgba(var(--bs-warning-rgb), var(--bs-link-underline-opacity)) !important;\n text-decoration-color: rgba(var(--bs-warning-rgb), var(--bs-link-underline-opacity)) !important;\n}\n\n.link-underline-danger {\n --bs-link-underline-opacity: 1;\n -webkit-text-decoration-color: rgba(var(--bs-danger-rgb), var(--bs-link-underline-opacity)) !important;\n text-decoration-color: rgba(var(--bs-danger-rgb), var(--bs-link-underline-opacity)) !important;\n}\n\n.link-underline-light {\n --bs-link-underline-opacity: 1;\n -webkit-text-decoration-color: rgba(var(--bs-light-rgb), var(--bs-link-underline-opacity)) !important;\n text-decoration-color: rgba(var(--bs-light-rgb), var(--bs-link-underline-opacity)) !important;\n}\n\n.link-underline-dark {\n --bs-link-underline-opacity: 1;\n -webkit-text-decoration-color: rgba(var(--bs-dark-rgb), var(--bs-link-underline-opacity)) !important;\n text-decoration-color: rgba(var(--bs-dark-rgb), var(--bs-link-underline-opacity)) !important;\n}\n\n.link-underline {\n --bs-link-underline-opacity: 1;\n -webkit-text-decoration-color: rgba(var(--bs-link-color-rgb), var(--bs-link-underline-opacity, 1)) !important;\n text-decoration-color: rgba(var(--bs-link-color-rgb), var(--bs-link-underline-opacity, 1)) !important;\n}\n\n.link-underline-opacity-0 {\n --bs-link-underline-opacity: 0;\n}\n\n.link-underline-opacity-0-hover:hover {\n --bs-link-underline-opacity: 0;\n}\n\n.link-underline-opacity-10 {\n --bs-link-underline-opacity: 0.1;\n}\n\n.link-underline-opacity-10-hover:hover {\n --bs-link-underline-opacity: 0.1;\n}\n\n.link-underline-opacity-25 {\n --bs-link-underline-opacity: 0.25;\n}\n\n.link-underline-opacity-25-hover:hover {\n --bs-link-underline-opacity: 0.25;\n}\n\n.link-underline-opacity-50 {\n --bs-link-underline-opacity: 0.5;\n}\n\n.link-underline-opacity-50-hover:hover {\n --bs-link-underline-opacity: 0.5;\n}\n\n.link-underline-opacity-75 {\n --bs-link-underline-opacity: 0.75;\n}\n\n.link-underline-opacity-75-hover:hover {\n --bs-link-underline-opacity: 0.75;\n}\n\n.link-underline-opacity-100 {\n --bs-link-underline-opacity: 1;\n}\n\n.link-underline-opacity-100-hover:hover {\n --bs-link-underline-opacity: 1;\n}\n\n.bg-primary {\n --bs-bg-opacity: 1;\n background-color: rgba(var(--bs-primary-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-secondary {\n --bs-bg-opacity: 1;\n background-color: rgba(var(--bs-secondary-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-success {\n --bs-bg-opacity: 1;\n background-color: rgba(var(--bs-success-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-info {\n --bs-bg-opacity: 1;\n background-color: rgba(var(--bs-info-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-warning {\n --bs-bg-opacity: 1;\n background-color: rgba(var(--bs-warning-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-danger {\n --bs-bg-opacity: 1;\n background-color: rgba(var(--bs-danger-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-light {\n --bs-bg-opacity: 1;\n background-color: rgba(var(--bs-light-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-dark {\n --bs-bg-opacity: 1;\n background-color: rgba(var(--bs-dark-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-black {\n --bs-bg-opacity: 1;\n background-color: rgba(var(--bs-black-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-white {\n --bs-bg-opacity: 1;\n background-color: rgba(var(--bs-white-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-body {\n --bs-bg-opacity: 1;\n background-color: rgba(var(--bs-body-bg-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-transparent {\n --bs-bg-opacity: 1;\n background-color: transparent !important;\n}\n\n.bg-body-secondary {\n --bs-bg-opacity: 1;\n background-color: rgba(var(--bs-secondary-bg-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-body-tertiary {\n --bs-bg-opacity: 1;\n background-color: rgba(var(--bs-tertiary-bg-rgb), var(--bs-bg-opacity)) !important;\n}\n\n.bg-opacity-10 {\n --bs-bg-opacity: 0.1;\n}\n\n.bg-opacity-25 {\n --bs-bg-opacity: 0.25;\n}\n\n.bg-opacity-50 {\n --bs-bg-opacity: 0.5;\n}\n\n.bg-opacity-75 {\n --bs-bg-opacity: 0.75;\n}\n\n.bg-opacity-100 {\n --bs-bg-opacity: 1;\n}\n\n.bg-primary-subtle {\n background-color: var(--bs-primary-bg-subtle) !important;\n}\n\n.bg-secondary-subtle {\n background-color: var(--bs-secondary-bg-subtle) !important;\n}\n\n.bg-success-subtle {\n background-color: var(--bs-success-bg-subtle) !important;\n}\n\n.bg-info-subtle {\n background-color: var(--bs-info-bg-subtle) !important;\n}\n\n.bg-warning-subtle {\n background-color: var(--bs-warning-bg-subtle) !important;\n}\n\n.bg-danger-subtle {\n background-color: var(--bs-danger-bg-subtle) !important;\n}\n\n.bg-light-subtle {\n background-color: var(--bs-light-bg-subtle) !important;\n}\n\n.bg-dark-subtle {\n background-color: var(--bs-dark-bg-subtle) !important;\n}\n\n.bg-gradient {\n background-image: var(--bs-gradient) !important;\n}\n\n.user-select-all {\n -webkit-user-select: all !important;\n -moz-user-select: all !important;\n user-select: all !important;\n}\n\n.user-select-auto {\n -webkit-user-select: auto !important;\n -moz-user-select: auto !important;\n user-select: auto !important;\n}\n\n.user-select-none {\n -webkit-user-select: none !important;\n -moz-user-select: none !important;\n user-select: none !important;\n}\n\n.pe-none {\n pointer-events: none !important;\n}\n\n.pe-auto {\n pointer-events: auto !important;\n}\n\n.rounded {\n border-radius: var(--bs-border-radius) !important;\n}\n\n.rounded-0 {\n border-radius: 0 !important;\n}\n\n.rounded-1 {\n border-radius: var(--bs-border-radius-sm) !important;\n}\n\n.rounded-2 {\n border-radius: var(--bs-border-radius) !important;\n}\n\n.rounded-3 {\n border-radius: var(--bs-border-radius-lg) !important;\n}\n\n.rounded-4 {\n border-radius: var(--bs-border-radius-xl) !important;\n}\n\n.rounded-5 {\n border-radius: var(--bs-border-radius-xxl) !important;\n}\n\n.rounded-circle {\n border-radius: 50% !important;\n}\n\n.rounded-pill {\n border-radius: var(--bs-border-radius-pill) !important;\n}\n\n.rounded-top {\n border-top-left-radius: var(--bs-border-radius) !important;\n border-top-right-radius: var(--bs-border-radius) !important;\n}\n\n.rounded-top-0 {\n border-top-left-radius: 0 !important;\n border-top-right-radius: 0 !important;\n}\n\n.rounded-top-1 {\n border-top-left-radius: var(--bs-border-radius-sm) !important;\n border-top-right-radius: var(--bs-border-radius-sm) !important;\n}\n\n.rounded-top-2 {\n border-top-left-radius: var(--bs-border-radius) !important;\n border-top-right-radius: var(--bs-border-radius) !important;\n}\n\n.rounded-top-3 {\n border-top-left-radius: var(--bs-border-radius-lg) !important;\n border-top-right-radius: var(--bs-border-radius-lg) !important;\n}\n\n.rounded-top-4 {\n border-top-left-radius: var(--bs-border-radius-xl) !important;\n border-top-right-radius: var(--bs-border-radius-xl) !important;\n}\n\n.rounded-top-5 {\n border-top-left-radius: var(--bs-border-radius-xxl) !important;\n border-top-right-radius: var(--bs-border-radius-xxl) !important;\n}\n\n.rounded-top-circle {\n border-top-left-radius: 50% !important;\n border-top-right-radius: 50% !important;\n}\n\n.rounded-top-pill {\n border-top-left-radius: var(--bs-border-radius-pill) !important;\n border-top-right-radius: var(--bs-border-radius-pill) !important;\n}\n\n.rounded-end {\n border-top-right-radius: var(--bs-border-radius) !important;\n border-bottom-right-radius: var(--bs-border-radius) !important;\n}\n\n.rounded-end-0 {\n border-top-right-radius: 0 !important;\n border-bottom-right-radius: 0 !important;\n}\n\n.rounded-end-1 {\n border-top-right-radius: var(--bs-border-radius-sm) !important;\n border-bottom-right-radius: var(--bs-border-radius-sm) !important;\n}\n\n.rounded-end-2 {\n border-top-right-radius: var(--bs-border-radius) !important;\n border-bottom-right-radius: var(--bs-border-radius) !important;\n}\n\n.rounded-end-3 {\n border-top-right-radius: var(--bs-border-radius-lg) !important;\n border-bottom-right-radius: var(--bs-border-radius-lg) !important;\n}\n\n.rounded-end-4 {\n border-top-right-radius: var(--bs-border-radius-xl) !important;\n border-bottom-right-radius: var(--bs-border-radius-xl) !important;\n}\n\n.rounded-end-5 {\n border-top-right-radius: var(--bs-border-radius-xxl) !important;\n border-bottom-right-radius: var(--bs-border-radius-xxl) !important;\n}\n\n.rounded-end-circle {\n border-top-right-radius: 50% !important;\n border-bottom-right-radius: 50% !important;\n}\n\n.rounded-end-pill {\n border-top-right-radius: var(--bs-border-radius-pill) !important;\n border-bottom-right-radius: var(--bs-border-radius-pill) !important;\n}\n\n.rounded-bottom {\n border-bottom-right-radius: var(--bs-border-radius) !important;\n border-bottom-left-radius: var(--bs-border-radius) !important;\n}\n\n.rounded-bottom-0 {\n border-bottom-right-radius: 0 !important;\n border-bottom-left-radius: 0 !important;\n}\n\n.rounded-bottom-1 {\n border-bottom-right-radius: var(--bs-border-radius-sm) !important;\n border-bottom-left-radius: var(--bs-border-radius-sm) !important;\n}\n\n.rounded-bottom-2 {\n border-bottom-right-radius: var(--bs-border-radius) !important;\n border-bottom-left-radius: var(--bs-border-radius) !important;\n}\n\n.rounded-bottom-3 {\n border-bottom-right-radius: var(--bs-border-radius-lg) !important;\n border-bottom-left-radius: var(--bs-border-radius-lg) !important;\n}\n\n.rounded-bottom-4 {\n border-bottom-right-radius: var(--bs-border-radius-xl) !important;\n border-bottom-left-radius: var(--bs-border-radius-xl) !important;\n}\n\n.rounded-bottom-5 {\n border-bottom-right-radius: var(--bs-border-radius-xxl) !important;\n border-bottom-left-radius: var(--bs-border-radius-xxl) !important;\n}\n\n.rounded-bottom-circle {\n border-bottom-right-radius: 50% !important;\n border-bottom-left-radius: 50% !important;\n}\n\n.rounded-bottom-pill {\n border-bottom-right-radius: var(--bs-border-radius-pill) !important;\n border-bottom-left-radius: var(--bs-border-radius-pill) !important;\n}\n\n.rounded-start {\n border-bottom-left-radius: var(--bs-border-radius) !important;\n border-top-left-radius: var(--bs-border-radius) !important;\n}\n\n.rounded-start-0 {\n border-bottom-left-radius: 0 !important;\n border-top-left-radius: 0 !important;\n}\n\n.rounded-start-1 {\n border-bottom-left-radius: var(--bs-border-radius-sm) !important;\n border-top-left-radius: var(--bs-border-radius-sm) !important;\n}\n\n.rounded-start-2 {\n border-bottom-left-radius: var(--bs-border-radius) !important;\n border-top-left-radius: var(--bs-border-radius) !important;\n}\n\n.rounded-start-3 {\n border-bottom-left-radius: var(--bs-border-radius-lg) !important;\n border-top-left-radius: var(--bs-border-radius-lg) !important;\n}\n\n.rounded-start-4 {\n border-bottom-left-radius: var(--bs-border-radius-xl) !important;\n border-top-left-radius: var(--bs-border-radius-xl) !important;\n}\n\n.rounded-start-5 {\n border-bottom-left-radius: var(--bs-border-radius-xxl) !important;\n border-top-left-radius: var(--bs-border-radius-xxl) !important;\n}\n\n.rounded-start-circle {\n border-bottom-left-radius: 50% !important;\n border-top-left-radius: 50% !important;\n}\n\n.rounded-start-pill {\n border-bottom-left-radius: var(--bs-border-radius-pill) !important;\n border-top-left-radius: var(--bs-border-radius-pill) !important;\n}\n\n.visible {\n visibility: visible !important;\n}\n\n.invisible {\n visibility: hidden !important;\n}\n\n.z-n1 {\n z-index: -1 !important;\n}\n\n.z-0 {\n z-index: 0 !important;\n}\n\n.z-1 {\n z-index: 1 !important;\n}\n\n.z-2 {\n z-index: 2 !important;\n}\n\n.z-3 {\n z-index: 3 !important;\n}\n\n@media (min-width: 576px) {\n .float-sm-start {\n float: left !important;\n }\n .float-sm-end {\n float: right !important;\n }\n .float-sm-none {\n float: none !important;\n }\n .object-fit-sm-contain {\n -o-object-fit: contain !important;\n object-fit: contain !important;\n }\n .object-fit-sm-cover {\n -o-object-fit: cover !important;\n object-fit: cover !important;\n }\n .object-fit-sm-fill {\n -o-object-fit: fill !important;\n object-fit: fill !important;\n }\n .object-fit-sm-scale {\n -o-object-fit: scale-down !important;\n object-fit: scale-down !important;\n }\n .object-fit-sm-none {\n -o-object-fit: none !important;\n object-fit: none !important;\n }\n .d-sm-inline {\n display: inline !important;\n }\n .d-sm-inline-block {\n display: inline-block !important;\n }\n .d-sm-block {\n display: block !important;\n }\n .d-sm-grid {\n display: grid !important;\n }\n .d-sm-inline-grid {\n display: inline-grid !important;\n }\n .d-sm-table {\n display: table !important;\n }\n .d-sm-table-row {\n display: table-row !important;\n }\n .d-sm-table-cell {\n display: table-cell !important;\n }\n .d-sm-flex {\n display: flex !important;\n }\n .d-sm-inline-flex {\n display: inline-flex !important;\n }\n .d-sm-none {\n display: none !important;\n }\n .flex-sm-fill {\n flex: 1 1 auto !important;\n }\n .flex-sm-row {\n flex-direction: row !important;\n }\n .flex-sm-column {\n flex-direction: column !important;\n }\n .flex-sm-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-sm-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-sm-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-sm-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-sm-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-sm-shrink-1 {\n flex-shrink: 1 !important;\n }\n .flex-sm-wrap {\n flex-wrap: wrap !important;\n }\n .flex-sm-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-sm-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .justify-content-sm-start {\n justify-content: flex-start !important;\n }\n .justify-content-sm-end {\n justify-content: flex-end !important;\n }\n .justify-content-sm-center {\n justify-content: center !important;\n }\n .justify-content-sm-between {\n justify-content: space-between !important;\n }\n .justify-content-sm-around {\n justify-content: space-around !important;\n }\n .justify-content-sm-evenly {\n justify-content: space-evenly !important;\n }\n .align-items-sm-start {\n align-items: flex-start !important;\n }\n .align-items-sm-end {\n align-items: flex-end !important;\n }\n .align-items-sm-center {\n align-items: center !important;\n }\n .align-items-sm-baseline {\n align-items: baseline !important;\n }\n .align-items-sm-stretch {\n align-items: stretch !important;\n }\n .align-content-sm-start {\n align-content: flex-start !important;\n }\n .align-content-sm-end {\n align-content: flex-end !important;\n }\n .align-content-sm-center {\n align-content: center !important;\n }\n .align-content-sm-between {\n align-content: space-between !important;\n }\n .align-content-sm-around {\n align-content: space-around !important;\n }\n .align-content-sm-stretch {\n align-content: stretch !important;\n }\n .align-self-sm-auto {\n align-self: auto !important;\n }\n .align-self-sm-start {\n align-self: flex-start !important;\n }\n .align-self-sm-end {\n align-self: flex-end !important;\n }\n .align-self-sm-center {\n align-self: center !important;\n }\n .align-self-sm-baseline {\n align-self: baseline !important;\n }\n .align-self-sm-stretch {\n align-self: stretch !important;\n }\n .order-sm-first {\n order: -1 !important;\n }\n .order-sm-0 {\n order: 0 !important;\n }\n .order-sm-1 {\n order: 1 !important;\n }\n .order-sm-2 {\n order: 2 !important;\n }\n .order-sm-3 {\n order: 3 !important;\n }\n .order-sm-4 {\n order: 4 !important;\n }\n .order-sm-5 {\n order: 5 !important;\n }\n .order-sm-last {\n order: 6 !important;\n }\n .m-sm-0 {\n margin: 0 !important;\n }\n .m-sm-1 {\n margin: 0.25rem !important;\n }\n .m-sm-2 {\n margin: 0.5rem !important;\n }\n .m-sm-3 {\n margin: 1rem !important;\n }\n .m-sm-4 {\n margin: 1.5rem !important;\n }\n .m-sm-5 {\n margin: 3rem !important;\n }\n .m-sm-auto {\n margin: auto !important;\n }\n .mx-sm-0 {\n margin-right: 0 !important;\n margin-left: 0 !important;\n }\n .mx-sm-1 {\n margin-right: 0.25rem !important;\n margin-left: 0.25rem !important;\n }\n .mx-sm-2 {\n margin-right: 0.5rem !important;\n margin-left: 0.5rem !important;\n }\n .mx-sm-3 {\n margin-right: 1rem !important;\n margin-left: 1rem !important;\n }\n .mx-sm-4 {\n margin-right: 1.5rem !important;\n margin-left: 1.5rem !important;\n }\n .mx-sm-5 {\n margin-right: 3rem !important;\n margin-left: 3rem !important;\n }\n .mx-sm-auto {\n margin-right: auto !important;\n margin-left: auto !important;\n }\n .my-sm-0 {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n }\n .my-sm-1 {\n margin-top: 0.25rem !important;\n margin-bottom: 0.25rem !important;\n }\n .my-sm-2 {\n margin-top: 0.5rem !important;\n margin-bottom: 0.5rem !important;\n }\n .my-sm-3 {\n margin-top: 1rem !important;\n margin-bottom: 1rem !important;\n }\n .my-sm-4 {\n margin-top: 1.5rem !important;\n margin-bottom: 1.5rem !important;\n }\n .my-sm-5 {\n margin-top: 3rem !important;\n margin-bottom: 3rem !important;\n }\n .my-sm-auto {\n margin-top: auto !important;\n margin-bottom: auto !important;\n }\n .mt-sm-0 {\n margin-top: 0 !important;\n }\n .mt-sm-1 {\n margin-top: 0.25rem !important;\n }\n .mt-sm-2 {\n margin-top: 0.5rem !important;\n }\n .mt-sm-3 {\n margin-top: 1rem !important;\n }\n .mt-sm-4 {\n margin-top: 1.5rem !important;\n }\n .mt-sm-5 {\n margin-top: 3rem !important;\n }\n .mt-sm-auto {\n margin-top: auto !important;\n }\n .me-sm-0 {\n margin-right: 0 !important;\n }\n .me-sm-1 {\n margin-right: 0.25rem !important;\n }\n .me-sm-2 {\n margin-right: 0.5rem !important;\n }\n .me-sm-3 {\n margin-right: 1rem !important;\n }\n .me-sm-4 {\n margin-right: 1.5rem !important;\n }\n .me-sm-5 {\n margin-right: 3rem !important;\n }\n .me-sm-auto {\n margin-right: auto !important;\n }\n .mb-sm-0 {\n margin-bottom: 0 !important;\n }\n .mb-sm-1 {\n margin-bottom: 0.25rem !important;\n }\n .mb-sm-2 {\n margin-bottom: 0.5rem !important;\n }\n .mb-sm-3 {\n margin-bottom: 1rem !important;\n }\n .mb-sm-4 {\n margin-bottom: 1.5rem !important;\n }\n .mb-sm-5 {\n margin-bottom: 3rem !important;\n }\n .mb-sm-auto {\n margin-bottom: auto !important;\n }\n .ms-sm-0 {\n margin-left: 0 !important;\n }\n .ms-sm-1 {\n margin-left: 0.25rem !important;\n }\n .ms-sm-2 {\n margin-left: 0.5rem !important;\n }\n .ms-sm-3 {\n margin-left: 1rem !important;\n }\n .ms-sm-4 {\n margin-left: 1.5rem !important;\n }\n .ms-sm-5 {\n margin-left: 3rem !important;\n }\n .ms-sm-auto {\n margin-left: auto !important;\n }\n .p-sm-0 {\n padding: 0 !important;\n }\n .p-sm-1 {\n padding: 0.25rem !important;\n }\n .p-sm-2 {\n padding: 0.5rem !important;\n }\n .p-sm-3 {\n padding: 1rem !important;\n }\n .p-sm-4 {\n padding: 1.5rem !important;\n }\n .p-sm-5 {\n padding: 3rem !important;\n }\n .px-sm-0 {\n padding-right: 0 !important;\n padding-left: 0 !important;\n }\n .px-sm-1 {\n padding-right: 0.25rem !important;\n padding-left: 0.25rem !important;\n }\n .px-sm-2 {\n padding-right: 0.5rem !important;\n padding-left: 0.5rem !important;\n }\n .px-sm-3 {\n padding-right: 1rem !important;\n padding-left: 1rem !important;\n }\n .px-sm-4 {\n padding-right: 1.5rem !important;\n padding-left: 1.5rem !important;\n }\n .px-sm-5 {\n padding-right: 3rem !important;\n padding-left: 3rem !important;\n }\n .py-sm-0 {\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n }\n .py-sm-1 {\n padding-top: 0.25rem !important;\n padding-bottom: 0.25rem !important;\n }\n .py-sm-2 {\n padding-top: 0.5rem !important;\n padding-bottom: 0.5rem !important;\n }\n .py-sm-3 {\n padding-top: 1rem !important;\n padding-bottom: 1rem !important;\n }\n .py-sm-4 {\n padding-top: 1.5rem !important;\n padding-bottom: 1.5rem !important;\n }\n .py-sm-5 {\n padding-top: 3rem !important;\n padding-bottom: 3rem !important;\n }\n .pt-sm-0 {\n padding-top: 0 !important;\n }\n .pt-sm-1 {\n padding-top: 0.25rem !important;\n }\n .pt-sm-2 {\n padding-top: 0.5rem !important;\n }\n .pt-sm-3 {\n padding-top: 1rem !important;\n }\n .pt-sm-4 {\n padding-top: 1.5rem !important;\n }\n .pt-sm-5 {\n padding-top: 3rem !important;\n }\n .pe-sm-0 {\n padding-right: 0 !important;\n }\n .pe-sm-1 {\n padding-right: 0.25rem !important;\n }\n .pe-sm-2 {\n padding-right: 0.5rem !important;\n }\n .pe-sm-3 {\n padding-right: 1rem !important;\n }\n .pe-sm-4 {\n padding-right: 1.5rem !important;\n }\n .pe-sm-5 {\n padding-right: 3rem !important;\n }\n .pb-sm-0 {\n padding-bottom: 0 !important;\n }\n .pb-sm-1 {\n padding-bottom: 0.25rem !important;\n }\n .pb-sm-2 {\n padding-bottom: 0.5rem !important;\n }\n .pb-sm-3 {\n padding-bottom: 1rem !important;\n }\n .pb-sm-4 {\n padding-bottom: 1.5rem !important;\n }\n .pb-sm-5 {\n padding-bottom: 3rem !important;\n }\n .ps-sm-0 {\n padding-left: 0 !important;\n }\n .ps-sm-1 {\n padding-left: 0.25rem !important;\n }\n .ps-sm-2 {\n padding-left: 0.5rem !important;\n }\n .ps-sm-3 {\n padding-left: 1rem !important;\n }\n .ps-sm-4 {\n padding-left: 1.5rem !important;\n }\n .ps-sm-5 {\n padding-left: 3rem !important;\n }\n .gap-sm-0 {\n gap: 0 !important;\n }\n .gap-sm-1 {\n gap: 0.25rem !important;\n }\n .gap-sm-2 {\n gap: 0.5rem !important;\n }\n .gap-sm-3 {\n gap: 1rem !important;\n }\n .gap-sm-4 {\n gap: 1.5rem !important;\n }\n .gap-sm-5 {\n gap: 3rem !important;\n }\n .row-gap-sm-0 {\n row-gap: 0 !important;\n }\n .row-gap-sm-1 {\n row-gap: 0.25rem !important;\n }\n .row-gap-sm-2 {\n row-gap: 0.5rem !important;\n }\n .row-gap-sm-3 {\n row-gap: 1rem !important;\n }\n .row-gap-sm-4 {\n row-gap: 1.5rem !important;\n }\n .row-gap-sm-5 {\n row-gap: 3rem !important;\n }\n .column-gap-sm-0 {\n -moz-column-gap: 0 !important;\n column-gap: 0 !important;\n }\n .column-gap-sm-1 {\n -moz-column-gap: 0.25rem !important;\n column-gap: 0.25rem !important;\n }\n .column-gap-sm-2 {\n -moz-column-gap: 0.5rem !important;\n column-gap: 0.5rem !important;\n }\n .column-gap-sm-3 {\n -moz-column-gap: 1rem !important;\n column-gap: 1rem !important;\n }\n .column-gap-sm-4 {\n -moz-column-gap: 1.5rem !important;\n column-gap: 1.5rem !important;\n }\n .column-gap-sm-5 {\n -moz-column-gap: 3rem !important;\n column-gap: 3rem !important;\n }\n .text-sm-start {\n text-align: left !important;\n }\n .text-sm-end {\n text-align: right !important;\n }\n .text-sm-center {\n text-align: center !important;\n }\n}\n@media (min-width: 768px) {\n .float-md-start {\n float: left !important;\n }\n .float-md-end {\n float: right !important;\n }\n .float-md-none {\n float: none !important;\n }\n .object-fit-md-contain {\n -o-object-fit: contain !important;\n object-fit: contain !important;\n }\n .object-fit-md-cover {\n -o-object-fit: cover !important;\n object-fit: cover !important;\n }\n .object-fit-md-fill {\n -o-object-fit: fill !important;\n object-fit: fill !important;\n }\n .object-fit-md-scale {\n -o-object-fit: scale-down !important;\n object-fit: scale-down !important;\n }\n .object-fit-md-none {\n -o-object-fit: none !important;\n object-fit: none !important;\n }\n .d-md-inline {\n display: inline !important;\n }\n .d-md-inline-block {\n display: inline-block !important;\n }\n .d-md-block {\n display: block !important;\n }\n .d-md-grid {\n display: grid !important;\n }\n .d-md-inline-grid {\n display: inline-grid !important;\n }\n .d-md-table {\n display: table !important;\n }\n .d-md-table-row {\n display: table-row !important;\n }\n .d-md-table-cell {\n display: table-cell !important;\n }\n .d-md-flex {\n display: flex !important;\n }\n .d-md-inline-flex {\n display: inline-flex !important;\n }\n .d-md-none {\n display: none !important;\n }\n .flex-md-fill {\n flex: 1 1 auto !important;\n }\n .flex-md-row {\n flex-direction: row !important;\n }\n .flex-md-column {\n flex-direction: column !important;\n }\n .flex-md-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-md-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-md-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-md-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-md-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-md-shrink-1 {\n flex-shrink: 1 !important;\n }\n .flex-md-wrap {\n flex-wrap: wrap !important;\n }\n .flex-md-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-md-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .justify-content-md-start {\n justify-content: flex-start !important;\n }\n .justify-content-md-end {\n justify-content: flex-end !important;\n }\n .justify-content-md-center {\n justify-content: center !important;\n }\n .justify-content-md-between {\n justify-content: space-between !important;\n }\n .justify-content-md-around {\n justify-content: space-around !important;\n }\n .justify-content-md-evenly {\n justify-content: space-evenly !important;\n }\n .align-items-md-start {\n align-items: flex-start !important;\n }\n .align-items-md-end {\n align-items: flex-end !important;\n }\n .align-items-md-center {\n align-items: center !important;\n }\n .align-items-md-baseline {\n align-items: baseline !important;\n }\n .align-items-md-stretch {\n align-items: stretch !important;\n }\n .align-content-md-start {\n align-content: flex-start !important;\n }\n .align-content-md-end {\n align-content: flex-end !important;\n }\n .align-content-md-center {\n align-content: center !important;\n }\n .align-content-md-between {\n align-content: space-between !important;\n }\n .align-content-md-around {\n align-content: space-around !important;\n }\n .align-content-md-stretch {\n align-content: stretch !important;\n }\n .align-self-md-auto {\n align-self: auto !important;\n }\n .align-self-md-start {\n align-self: flex-start !important;\n }\n .align-self-md-end {\n align-self: flex-end !important;\n }\n .align-self-md-center {\n align-self: center !important;\n }\n .align-self-md-baseline {\n align-self: baseline !important;\n }\n .align-self-md-stretch {\n align-self: stretch !important;\n }\n .order-md-first {\n order: -1 !important;\n }\n .order-md-0 {\n order: 0 !important;\n }\n .order-md-1 {\n order: 1 !important;\n }\n .order-md-2 {\n order: 2 !important;\n }\n .order-md-3 {\n order: 3 !important;\n }\n .order-md-4 {\n order: 4 !important;\n }\n .order-md-5 {\n order: 5 !important;\n }\n .order-md-last {\n order: 6 !important;\n }\n .m-md-0 {\n margin: 0 !important;\n }\n .m-md-1 {\n margin: 0.25rem !important;\n }\n .m-md-2 {\n margin: 0.5rem !important;\n }\n .m-md-3 {\n margin: 1rem !important;\n }\n .m-md-4 {\n margin: 1.5rem !important;\n }\n .m-md-5 {\n margin: 3rem !important;\n }\n .m-md-auto {\n margin: auto !important;\n }\n .mx-md-0 {\n margin-right: 0 !important;\n margin-left: 0 !important;\n }\n .mx-md-1 {\n margin-right: 0.25rem !important;\n margin-left: 0.25rem !important;\n }\n .mx-md-2 {\n margin-right: 0.5rem !important;\n margin-left: 0.5rem !important;\n }\n .mx-md-3 {\n margin-right: 1rem !important;\n margin-left: 1rem !important;\n }\n .mx-md-4 {\n margin-right: 1.5rem !important;\n margin-left: 1.5rem !important;\n }\n .mx-md-5 {\n margin-right: 3rem !important;\n margin-left: 3rem !important;\n }\n .mx-md-auto {\n margin-right: auto !important;\n margin-left: auto !important;\n }\n .my-md-0 {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n }\n .my-md-1 {\n margin-top: 0.25rem !important;\n margin-bottom: 0.25rem !important;\n }\n .my-md-2 {\n margin-top: 0.5rem !important;\n margin-bottom: 0.5rem !important;\n }\n .my-md-3 {\n margin-top: 1rem !important;\n margin-bottom: 1rem !important;\n }\n .my-md-4 {\n margin-top: 1.5rem !important;\n margin-bottom: 1.5rem !important;\n }\n .my-md-5 {\n margin-top: 3rem !important;\n margin-bottom: 3rem !important;\n }\n .my-md-auto {\n margin-top: auto !important;\n margin-bottom: auto !important;\n }\n .mt-md-0 {\n margin-top: 0 !important;\n }\n .mt-md-1 {\n margin-top: 0.25rem !important;\n }\n .mt-md-2 {\n margin-top: 0.5rem !important;\n }\n .mt-md-3 {\n margin-top: 1rem !important;\n }\n .mt-md-4 {\n margin-top: 1.5rem !important;\n }\n .mt-md-5 {\n margin-top: 3rem !important;\n }\n .mt-md-auto {\n margin-top: auto !important;\n }\n .me-md-0 {\n margin-right: 0 !important;\n }\n .me-md-1 {\n margin-right: 0.25rem !important;\n }\n .me-md-2 {\n margin-right: 0.5rem !important;\n }\n .me-md-3 {\n margin-right: 1rem !important;\n }\n .me-md-4 {\n margin-right: 1.5rem !important;\n }\n .me-md-5 {\n margin-right: 3rem !important;\n }\n .me-md-auto {\n margin-right: auto !important;\n }\n .mb-md-0 {\n margin-bottom: 0 !important;\n }\n .mb-md-1 {\n margin-bottom: 0.25rem !important;\n }\n .mb-md-2 {\n margin-bottom: 0.5rem !important;\n }\n .mb-md-3 {\n margin-bottom: 1rem !important;\n }\n .mb-md-4 {\n margin-bottom: 1.5rem !important;\n }\n .mb-md-5 {\n margin-bottom: 3rem !important;\n }\n .mb-md-auto {\n margin-bottom: auto !important;\n }\n .ms-md-0 {\n margin-left: 0 !important;\n }\n .ms-md-1 {\n margin-left: 0.25rem !important;\n }\n .ms-md-2 {\n margin-left: 0.5rem !important;\n }\n .ms-md-3 {\n margin-left: 1rem !important;\n }\n .ms-md-4 {\n margin-left: 1.5rem !important;\n }\n .ms-md-5 {\n margin-left: 3rem !important;\n }\n .ms-md-auto {\n margin-left: auto !important;\n }\n .p-md-0 {\n padding: 0 !important;\n }\n .p-md-1 {\n padding: 0.25rem !important;\n }\n .p-md-2 {\n padding: 0.5rem !important;\n }\n .p-md-3 {\n padding: 1rem !important;\n }\n .p-md-4 {\n padding: 1.5rem !important;\n }\n .p-md-5 {\n padding: 3rem !important;\n }\n .px-md-0 {\n padding-right: 0 !important;\n padding-left: 0 !important;\n }\n .px-md-1 {\n padding-right: 0.25rem !important;\n padding-left: 0.25rem !important;\n }\n .px-md-2 {\n padding-right: 0.5rem !important;\n padding-left: 0.5rem !important;\n }\n .px-md-3 {\n padding-right: 1rem !important;\n padding-left: 1rem !important;\n }\n .px-md-4 {\n padding-right: 1.5rem !important;\n padding-left: 1.5rem !important;\n }\n .px-md-5 {\n padding-right: 3rem !important;\n padding-left: 3rem !important;\n }\n .py-md-0 {\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n }\n .py-md-1 {\n padding-top: 0.25rem !important;\n padding-bottom: 0.25rem !important;\n }\n .py-md-2 {\n padding-top: 0.5rem !important;\n padding-bottom: 0.5rem !important;\n }\n .py-md-3 {\n padding-top: 1rem !important;\n padding-bottom: 1rem !important;\n }\n .py-md-4 {\n padding-top: 1.5rem !important;\n padding-bottom: 1.5rem !important;\n }\n .py-md-5 {\n padding-top: 3rem !important;\n padding-bottom: 3rem !important;\n }\n .pt-md-0 {\n padding-top: 0 !important;\n }\n .pt-md-1 {\n padding-top: 0.25rem !important;\n }\n .pt-md-2 {\n padding-top: 0.5rem !important;\n }\n .pt-md-3 {\n padding-top: 1rem !important;\n }\n .pt-md-4 {\n padding-top: 1.5rem !important;\n }\n .pt-md-5 {\n padding-top: 3rem !important;\n }\n .pe-md-0 {\n padding-right: 0 !important;\n }\n .pe-md-1 {\n padding-right: 0.25rem !important;\n }\n .pe-md-2 {\n padding-right: 0.5rem !important;\n }\n .pe-md-3 {\n padding-right: 1rem !important;\n }\n .pe-md-4 {\n padding-right: 1.5rem !important;\n }\n .pe-md-5 {\n padding-right: 3rem !important;\n }\n .pb-md-0 {\n padding-bottom: 0 !important;\n }\n .pb-md-1 {\n padding-bottom: 0.25rem !important;\n }\n .pb-md-2 {\n padding-bottom: 0.5rem !important;\n }\n .pb-md-3 {\n padding-bottom: 1rem !important;\n }\n .pb-md-4 {\n padding-bottom: 1.5rem !important;\n }\n .pb-md-5 {\n padding-bottom: 3rem !important;\n }\n .ps-md-0 {\n padding-left: 0 !important;\n }\n .ps-md-1 {\n padding-left: 0.25rem !important;\n }\n .ps-md-2 {\n padding-left: 0.5rem !important;\n }\n .ps-md-3 {\n padding-left: 1rem !important;\n }\n .ps-md-4 {\n padding-left: 1.5rem !important;\n }\n .ps-md-5 {\n padding-left: 3rem !important;\n }\n .gap-md-0 {\n gap: 0 !important;\n }\n .gap-md-1 {\n gap: 0.25rem !important;\n }\n .gap-md-2 {\n gap: 0.5rem !important;\n }\n .gap-md-3 {\n gap: 1rem !important;\n }\n .gap-md-4 {\n gap: 1.5rem !important;\n }\n .gap-md-5 {\n gap: 3rem !important;\n }\n .row-gap-md-0 {\n row-gap: 0 !important;\n }\n .row-gap-md-1 {\n row-gap: 0.25rem !important;\n }\n .row-gap-md-2 {\n row-gap: 0.5rem !important;\n }\n .row-gap-md-3 {\n row-gap: 1rem !important;\n }\n .row-gap-md-4 {\n row-gap: 1.5rem !important;\n }\n .row-gap-md-5 {\n row-gap: 3rem !important;\n }\n .column-gap-md-0 {\n -moz-column-gap: 0 !important;\n column-gap: 0 !important;\n }\n .column-gap-md-1 {\n -moz-column-gap: 0.25rem !important;\n column-gap: 0.25rem !important;\n }\n .column-gap-md-2 {\n -moz-column-gap: 0.5rem !important;\n column-gap: 0.5rem !important;\n }\n .column-gap-md-3 {\n -moz-column-gap: 1rem !important;\n column-gap: 1rem !important;\n }\n .column-gap-md-4 {\n -moz-column-gap: 1.5rem !important;\n column-gap: 1.5rem !important;\n }\n .column-gap-md-5 {\n -moz-column-gap: 3rem !important;\n column-gap: 3rem !important;\n }\n .text-md-start {\n text-align: left !important;\n }\n .text-md-end {\n text-align: right !important;\n }\n .text-md-center {\n text-align: center !important;\n }\n}\n@media (min-width: 992px) {\n .float-lg-start {\n float: left !important;\n }\n .float-lg-end {\n float: right !important;\n }\n .float-lg-none {\n float: none !important;\n }\n .object-fit-lg-contain {\n -o-object-fit: contain !important;\n object-fit: contain !important;\n }\n .object-fit-lg-cover {\n -o-object-fit: cover !important;\n object-fit: cover !important;\n }\n .object-fit-lg-fill {\n -o-object-fit: fill !important;\n object-fit: fill !important;\n }\n .object-fit-lg-scale {\n -o-object-fit: scale-down !important;\n object-fit: scale-down !important;\n }\n .object-fit-lg-none {\n -o-object-fit: none !important;\n object-fit: none !important;\n }\n .d-lg-inline {\n display: inline !important;\n }\n .d-lg-inline-block {\n display: inline-block !important;\n }\n .d-lg-block {\n display: block !important;\n }\n .d-lg-grid {\n display: grid !important;\n }\n .d-lg-inline-grid {\n display: inline-grid !important;\n }\n .d-lg-table {\n display: table !important;\n }\n .d-lg-table-row {\n display: table-row !important;\n }\n .d-lg-table-cell {\n display: table-cell !important;\n }\n .d-lg-flex {\n display: flex !important;\n }\n .d-lg-inline-flex {\n display: inline-flex !important;\n }\n .d-lg-none {\n display: none !important;\n }\n .flex-lg-fill {\n flex: 1 1 auto !important;\n }\n .flex-lg-row {\n flex-direction: row !important;\n }\n .flex-lg-column {\n flex-direction: column !important;\n }\n .flex-lg-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-lg-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-lg-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-lg-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-lg-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-lg-shrink-1 {\n flex-shrink: 1 !important;\n }\n .flex-lg-wrap {\n flex-wrap: wrap !important;\n }\n .flex-lg-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-lg-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .justify-content-lg-start {\n justify-content: flex-start !important;\n }\n .justify-content-lg-end {\n justify-content: flex-end !important;\n }\n .justify-content-lg-center {\n justify-content: center !important;\n }\n .justify-content-lg-between {\n justify-content: space-between !important;\n }\n .justify-content-lg-around {\n justify-content: space-around !important;\n }\n .justify-content-lg-evenly {\n justify-content: space-evenly !important;\n }\n .align-items-lg-start {\n align-items: flex-start !important;\n }\n .align-items-lg-end {\n align-items: flex-end !important;\n }\n .align-items-lg-center {\n align-items: center !important;\n }\n .align-items-lg-baseline {\n align-items: baseline !important;\n }\n .align-items-lg-stretch {\n align-items: stretch !important;\n }\n .align-content-lg-start {\n align-content: flex-start !important;\n }\n .align-content-lg-end {\n align-content: flex-end !important;\n }\n .align-content-lg-center {\n align-content: center !important;\n }\n .align-content-lg-between {\n align-content: space-between !important;\n }\n .align-content-lg-around {\n align-content: space-around !important;\n }\n .align-content-lg-stretch {\n align-content: stretch !important;\n }\n .align-self-lg-auto {\n align-self: auto !important;\n }\n .align-self-lg-start {\n align-self: flex-start !important;\n }\n .align-self-lg-end {\n align-self: flex-end !important;\n }\n .align-self-lg-center {\n align-self: center !important;\n }\n .align-self-lg-baseline {\n align-self: baseline !important;\n }\n .align-self-lg-stretch {\n align-self: stretch !important;\n }\n .order-lg-first {\n order: -1 !important;\n }\n .order-lg-0 {\n order: 0 !important;\n }\n .order-lg-1 {\n order: 1 !important;\n }\n .order-lg-2 {\n order: 2 !important;\n }\n .order-lg-3 {\n order: 3 !important;\n }\n .order-lg-4 {\n order: 4 !important;\n }\n .order-lg-5 {\n order: 5 !important;\n }\n .order-lg-last {\n order: 6 !important;\n }\n .m-lg-0 {\n margin: 0 !important;\n }\n .m-lg-1 {\n margin: 0.25rem !important;\n }\n .m-lg-2 {\n margin: 0.5rem !important;\n }\n .m-lg-3 {\n margin: 1rem !important;\n }\n .m-lg-4 {\n margin: 1.5rem !important;\n }\n .m-lg-5 {\n margin: 3rem !important;\n }\n .m-lg-auto {\n margin: auto !important;\n }\n .mx-lg-0 {\n margin-right: 0 !important;\n margin-left: 0 !important;\n }\n .mx-lg-1 {\n margin-right: 0.25rem !important;\n margin-left: 0.25rem !important;\n }\n .mx-lg-2 {\n margin-right: 0.5rem !important;\n margin-left: 0.5rem !important;\n }\n .mx-lg-3 {\n margin-right: 1rem !important;\n margin-left: 1rem !important;\n }\n .mx-lg-4 {\n margin-right: 1.5rem !important;\n margin-left: 1.5rem !important;\n }\n .mx-lg-5 {\n margin-right: 3rem !important;\n margin-left: 3rem !important;\n }\n .mx-lg-auto {\n margin-right: auto !important;\n margin-left: auto !important;\n }\n .my-lg-0 {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n }\n .my-lg-1 {\n margin-top: 0.25rem !important;\n margin-bottom: 0.25rem !important;\n }\n .my-lg-2 {\n margin-top: 0.5rem !important;\n margin-bottom: 0.5rem !important;\n }\n .my-lg-3 {\n margin-top: 1rem !important;\n margin-bottom: 1rem !important;\n }\n .my-lg-4 {\n margin-top: 1.5rem !important;\n margin-bottom: 1.5rem !important;\n }\n .my-lg-5 {\n margin-top: 3rem !important;\n margin-bottom: 3rem !important;\n }\n .my-lg-auto {\n margin-top: auto !important;\n margin-bottom: auto !important;\n }\n .mt-lg-0 {\n margin-top: 0 !important;\n }\n .mt-lg-1 {\n margin-top: 0.25rem !important;\n }\n .mt-lg-2 {\n margin-top: 0.5rem !important;\n }\n .mt-lg-3 {\n margin-top: 1rem !important;\n }\n .mt-lg-4 {\n margin-top: 1.5rem !important;\n }\n .mt-lg-5 {\n margin-top: 3rem !important;\n }\n .mt-lg-auto {\n margin-top: auto !important;\n }\n .me-lg-0 {\n margin-right: 0 !important;\n }\n .me-lg-1 {\n margin-right: 0.25rem !important;\n }\n .me-lg-2 {\n margin-right: 0.5rem !important;\n }\n .me-lg-3 {\n margin-right: 1rem !important;\n }\n .me-lg-4 {\n margin-right: 1.5rem !important;\n }\n .me-lg-5 {\n margin-right: 3rem !important;\n }\n .me-lg-auto {\n margin-right: auto !important;\n }\n .mb-lg-0 {\n margin-bottom: 0 !important;\n }\n .mb-lg-1 {\n margin-bottom: 0.25rem !important;\n }\n .mb-lg-2 {\n margin-bottom: 0.5rem !important;\n }\n .mb-lg-3 {\n margin-bottom: 1rem !important;\n }\n .mb-lg-4 {\n margin-bottom: 1.5rem !important;\n }\n .mb-lg-5 {\n margin-bottom: 3rem !important;\n }\n .mb-lg-auto {\n margin-bottom: auto !important;\n }\n .ms-lg-0 {\n margin-left: 0 !important;\n }\n .ms-lg-1 {\n margin-left: 0.25rem !important;\n }\n .ms-lg-2 {\n margin-left: 0.5rem !important;\n }\n .ms-lg-3 {\n margin-left: 1rem !important;\n }\n .ms-lg-4 {\n margin-left: 1.5rem !important;\n }\n .ms-lg-5 {\n margin-left: 3rem !important;\n }\n .ms-lg-auto {\n margin-left: auto !important;\n }\n .p-lg-0 {\n padding: 0 !important;\n }\n .p-lg-1 {\n padding: 0.25rem !important;\n }\n .p-lg-2 {\n padding: 0.5rem !important;\n }\n .p-lg-3 {\n padding: 1rem !important;\n }\n .p-lg-4 {\n padding: 1.5rem !important;\n }\n .p-lg-5 {\n padding: 3rem !important;\n }\n .px-lg-0 {\n padding-right: 0 !important;\n padding-left: 0 !important;\n }\n .px-lg-1 {\n padding-right: 0.25rem !important;\n padding-left: 0.25rem !important;\n }\n .px-lg-2 {\n padding-right: 0.5rem !important;\n padding-left: 0.5rem !important;\n }\n .px-lg-3 {\n padding-right: 1rem !important;\n padding-left: 1rem !important;\n }\n .px-lg-4 {\n padding-right: 1.5rem !important;\n padding-left: 1.5rem !important;\n }\n .px-lg-5 {\n padding-right: 3rem !important;\n padding-left: 3rem !important;\n }\n .py-lg-0 {\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n }\n .py-lg-1 {\n padding-top: 0.25rem !important;\n padding-bottom: 0.25rem !important;\n }\n .py-lg-2 {\n padding-top: 0.5rem !important;\n padding-bottom: 0.5rem !important;\n }\n .py-lg-3 {\n padding-top: 1rem !important;\n padding-bottom: 1rem !important;\n }\n .py-lg-4 {\n padding-top: 1.5rem !important;\n padding-bottom: 1.5rem !important;\n }\n .py-lg-5 {\n padding-top: 3rem !important;\n padding-bottom: 3rem !important;\n }\n .pt-lg-0 {\n padding-top: 0 !important;\n }\n .pt-lg-1 {\n padding-top: 0.25rem !important;\n }\n .pt-lg-2 {\n padding-top: 0.5rem !important;\n }\n .pt-lg-3 {\n padding-top: 1rem !important;\n }\n .pt-lg-4 {\n padding-top: 1.5rem !important;\n }\n .pt-lg-5 {\n padding-top: 3rem !important;\n }\n .pe-lg-0 {\n padding-right: 0 !important;\n }\n .pe-lg-1 {\n padding-right: 0.25rem !important;\n }\n .pe-lg-2 {\n padding-right: 0.5rem !important;\n }\n .pe-lg-3 {\n padding-right: 1rem !important;\n }\n .pe-lg-4 {\n padding-right: 1.5rem !important;\n }\n .pe-lg-5 {\n padding-right: 3rem !important;\n }\n .pb-lg-0 {\n padding-bottom: 0 !important;\n }\n .pb-lg-1 {\n padding-bottom: 0.25rem !important;\n }\n .pb-lg-2 {\n padding-bottom: 0.5rem !important;\n }\n .pb-lg-3 {\n padding-bottom: 1rem !important;\n }\n .pb-lg-4 {\n padding-bottom: 1.5rem !important;\n }\n .pb-lg-5 {\n padding-bottom: 3rem !important;\n }\n .ps-lg-0 {\n padding-left: 0 !important;\n }\n .ps-lg-1 {\n padding-left: 0.25rem !important;\n }\n .ps-lg-2 {\n padding-left: 0.5rem !important;\n }\n .ps-lg-3 {\n padding-left: 1rem !important;\n }\n .ps-lg-4 {\n padding-left: 1.5rem !important;\n }\n .ps-lg-5 {\n padding-left: 3rem !important;\n }\n .gap-lg-0 {\n gap: 0 !important;\n }\n .gap-lg-1 {\n gap: 0.25rem !important;\n }\n .gap-lg-2 {\n gap: 0.5rem !important;\n }\n .gap-lg-3 {\n gap: 1rem !important;\n }\n .gap-lg-4 {\n gap: 1.5rem !important;\n }\n .gap-lg-5 {\n gap: 3rem !important;\n }\n .row-gap-lg-0 {\n row-gap: 0 !important;\n }\n .row-gap-lg-1 {\n row-gap: 0.25rem !important;\n }\n .row-gap-lg-2 {\n row-gap: 0.5rem !important;\n }\n .row-gap-lg-3 {\n row-gap: 1rem !important;\n }\n .row-gap-lg-4 {\n row-gap: 1.5rem !important;\n }\n .row-gap-lg-5 {\n row-gap: 3rem !important;\n }\n .column-gap-lg-0 {\n -moz-column-gap: 0 !important;\n column-gap: 0 !important;\n }\n .column-gap-lg-1 {\n -moz-column-gap: 0.25rem !important;\n column-gap: 0.25rem !important;\n }\n .column-gap-lg-2 {\n -moz-column-gap: 0.5rem !important;\n column-gap: 0.5rem !important;\n }\n .column-gap-lg-3 {\n -moz-column-gap: 1rem !important;\n column-gap: 1rem !important;\n }\n .column-gap-lg-4 {\n -moz-column-gap: 1.5rem !important;\n column-gap: 1.5rem !important;\n }\n .column-gap-lg-5 {\n -moz-column-gap: 3rem !important;\n column-gap: 3rem !important;\n }\n .text-lg-start {\n text-align: left !important;\n }\n .text-lg-end {\n text-align: right !important;\n }\n .text-lg-center {\n text-align: center !important;\n }\n}\n@media (min-width: 1200px) {\n .float-xl-start {\n float: left !important;\n }\n .float-xl-end {\n float: right !important;\n }\n .float-xl-none {\n float: none !important;\n }\n .object-fit-xl-contain {\n -o-object-fit: contain !important;\n object-fit: contain !important;\n }\n .object-fit-xl-cover {\n -o-object-fit: cover !important;\n object-fit: cover !important;\n }\n .object-fit-xl-fill {\n -o-object-fit: fill !important;\n object-fit: fill !important;\n }\n .object-fit-xl-scale {\n -o-object-fit: scale-down !important;\n object-fit: scale-down !important;\n }\n .object-fit-xl-none {\n -o-object-fit: none !important;\n object-fit: none !important;\n }\n .d-xl-inline {\n display: inline !important;\n }\n .d-xl-inline-block {\n display: inline-block !important;\n }\n .d-xl-block {\n display: block !important;\n }\n .d-xl-grid {\n display: grid !important;\n }\n .d-xl-inline-grid {\n display: inline-grid !important;\n }\n .d-xl-table {\n display: table !important;\n }\n .d-xl-table-row {\n display: table-row !important;\n }\n .d-xl-table-cell {\n display: table-cell !important;\n }\n .d-xl-flex {\n display: flex !important;\n }\n .d-xl-inline-flex {\n display: inline-flex !important;\n }\n .d-xl-none {\n display: none !important;\n }\n .flex-xl-fill {\n flex: 1 1 auto !important;\n }\n .flex-xl-row {\n flex-direction: row !important;\n }\n .flex-xl-column {\n flex-direction: column !important;\n }\n .flex-xl-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-xl-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-xl-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-xl-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-xl-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-xl-shrink-1 {\n flex-shrink: 1 !important;\n }\n .flex-xl-wrap {\n flex-wrap: wrap !important;\n }\n .flex-xl-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-xl-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .justify-content-xl-start {\n justify-content: flex-start !important;\n }\n .justify-content-xl-end {\n justify-content: flex-end !important;\n }\n .justify-content-xl-center {\n justify-content: center !important;\n }\n .justify-content-xl-between {\n justify-content: space-between !important;\n }\n .justify-content-xl-around {\n justify-content: space-around !important;\n }\n .justify-content-xl-evenly {\n justify-content: space-evenly !important;\n }\n .align-items-xl-start {\n align-items: flex-start !important;\n }\n .align-items-xl-end {\n align-items: flex-end !important;\n }\n .align-items-xl-center {\n align-items: center !important;\n }\n .align-items-xl-baseline {\n align-items: baseline !important;\n }\n .align-items-xl-stretch {\n align-items: stretch !important;\n }\n .align-content-xl-start {\n align-content: flex-start !important;\n }\n .align-content-xl-end {\n align-content: flex-end !important;\n }\n .align-content-xl-center {\n align-content: center !important;\n }\n .align-content-xl-between {\n align-content: space-between !important;\n }\n .align-content-xl-around {\n align-content: space-around !important;\n }\n .align-content-xl-stretch {\n align-content: stretch !important;\n }\n .align-self-xl-auto {\n align-self: auto !important;\n }\n .align-self-xl-start {\n align-self: flex-start !important;\n }\n .align-self-xl-end {\n align-self: flex-end !important;\n }\n .align-self-xl-center {\n align-self: center !important;\n }\n .align-self-xl-baseline {\n align-self: baseline !important;\n }\n .align-self-xl-stretch {\n align-self: stretch !important;\n }\n .order-xl-first {\n order: -1 !important;\n }\n .order-xl-0 {\n order: 0 !important;\n }\n .order-xl-1 {\n order: 1 !important;\n }\n .order-xl-2 {\n order: 2 !important;\n }\n .order-xl-3 {\n order: 3 !important;\n }\n .order-xl-4 {\n order: 4 !important;\n }\n .order-xl-5 {\n order: 5 !important;\n }\n .order-xl-last {\n order: 6 !important;\n }\n .m-xl-0 {\n margin: 0 !important;\n }\n .m-xl-1 {\n margin: 0.25rem !important;\n }\n .m-xl-2 {\n margin: 0.5rem !important;\n }\n .m-xl-3 {\n margin: 1rem !important;\n }\n .m-xl-4 {\n margin: 1.5rem !important;\n }\n .m-xl-5 {\n margin: 3rem !important;\n }\n .m-xl-auto {\n margin: auto !important;\n }\n .mx-xl-0 {\n margin-right: 0 !important;\n margin-left: 0 !important;\n }\n .mx-xl-1 {\n margin-right: 0.25rem !important;\n margin-left: 0.25rem !important;\n }\n .mx-xl-2 {\n margin-right: 0.5rem !important;\n margin-left: 0.5rem !important;\n }\n .mx-xl-3 {\n margin-right: 1rem !important;\n margin-left: 1rem !important;\n }\n .mx-xl-4 {\n margin-right: 1.5rem !important;\n margin-left: 1.5rem !important;\n }\n .mx-xl-5 {\n margin-right: 3rem !important;\n margin-left: 3rem !important;\n }\n .mx-xl-auto {\n margin-right: auto !important;\n margin-left: auto !important;\n }\n .my-xl-0 {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n }\n .my-xl-1 {\n margin-top: 0.25rem !important;\n margin-bottom: 0.25rem !important;\n }\n .my-xl-2 {\n margin-top: 0.5rem !important;\n margin-bottom: 0.5rem !important;\n }\n .my-xl-3 {\n margin-top: 1rem !important;\n margin-bottom: 1rem !important;\n }\n .my-xl-4 {\n margin-top: 1.5rem !important;\n margin-bottom: 1.5rem !important;\n }\n .my-xl-5 {\n margin-top: 3rem !important;\n margin-bottom: 3rem !important;\n }\n .my-xl-auto {\n margin-top: auto !important;\n margin-bottom: auto !important;\n }\n .mt-xl-0 {\n margin-top: 0 !important;\n }\n .mt-xl-1 {\n margin-top: 0.25rem !important;\n }\n .mt-xl-2 {\n margin-top: 0.5rem !important;\n }\n .mt-xl-3 {\n margin-top: 1rem !important;\n }\n .mt-xl-4 {\n margin-top: 1.5rem !important;\n }\n .mt-xl-5 {\n margin-top: 3rem !important;\n }\n .mt-xl-auto {\n margin-top: auto !important;\n }\n .me-xl-0 {\n margin-right: 0 !important;\n }\n .me-xl-1 {\n margin-right: 0.25rem !important;\n }\n .me-xl-2 {\n margin-right: 0.5rem !important;\n }\n .me-xl-3 {\n margin-right: 1rem !important;\n }\n .me-xl-4 {\n margin-right: 1.5rem !important;\n }\n .me-xl-5 {\n margin-right: 3rem !important;\n }\n .me-xl-auto {\n margin-right: auto !important;\n }\n .mb-xl-0 {\n margin-bottom: 0 !important;\n }\n .mb-xl-1 {\n margin-bottom: 0.25rem !important;\n }\n .mb-xl-2 {\n margin-bottom: 0.5rem !important;\n }\n .mb-xl-3 {\n margin-bottom: 1rem !important;\n }\n .mb-xl-4 {\n margin-bottom: 1.5rem !important;\n }\n .mb-xl-5 {\n margin-bottom: 3rem !important;\n }\n .mb-xl-auto {\n margin-bottom: auto !important;\n }\n .ms-xl-0 {\n margin-left: 0 !important;\n }\n .ms-xl-1 {\n margin-left: 0.25rem !important;\n }\n .ms-xl-2 {\n margin-left: 0.5rem !important;\n }\n .ms-xl-3 {\n margin-left: 1rem !important;\n }\n .ms-xl-4 {\n margin-left: 1.5rem !important;\n }\n .ms-xl-5 {\n margin-left: 3rem !important;\n }\n .ms-xl-auto {\n margin-left: auto !important;\n }\n .p-xl-0 {\n padding: 0 !important;\n }\n .p-xl-1 {\n padding: 0.25rem !important;\n }\n .p-xl-2 {\n padding: 0.5rem !important;\n }\n .p-xl-3 {\n padding: 1rem !important;\n }\n .p-xl-4 {\n padding: 1.5rem !important;\n }\n .p-xl-5 {\n padding: 3rem !important;\n }\n .px-xl-0 {\n padding-right: 0 !important;\n padding-left: 0 !important;\n }\n .px-xl-1 {\n padding-right: 0.25rem !important;\n padding-left: 0.25rem !important;\n }\n .px-xl-2 {\n padding-right: 0.5rem !important;\n padding-left: 0.5rem !important;\n }\n .px-xl-3 {\n padding-right: 1rem !important;\n padding-left: 1rem !important;\n }\n .px-xl-4 {\n padding-right: 1.5rem !important;\n padding-left: 1.5rem !important;\n }\n .px-xl-5 {\n padding-right: 3rem !important;\n padding-left: 3rem !important;\n }\n .py-xl-0 {\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n }\n .py-xl-1 {\n padding-top: 0.25rem !important;\n padding-bottom: 0.25rem !important;\n }\n .py-xl-2 {\n padding-top: 0.5rem !important;\n padding-bottom: 0.5rem !important;\n }\n .py-xl-3 {\n padding-top: 1rem !important;\n padding-bottom: 1rem !important;\n }\n .py-xl-4 {\n padding-top: 1.5rem !important;\n padding-bottom: 1.5rem !important;\n }\n .py-xl-5 {\n padding-top: 3rem !important;\n padding-bottom: 3rem !important;\n }\n .pt-xl-0 {\n padding-top: 0 !important;\n }\n .pt-xl-1 {\n padding-top: 0.25rem !important;\n }\n .pt-xl-2 {\n padding-top: 0.5rem !important;\n }\n .pt-xl-3 {\n padding-top: 1rem !important;\n }\n .pt-xl-4 {\n padding-top: 1.5rem !important;\n }\n .pt-xl-5 {\n padding-top: 3rem !important;\n }\n .pe-xl-0 {\n padding-right: 0 !important;\n }\n .pe-xl-1 {\n padding-right: 0.25rem !important;\n }\n .pe-xl-2 {\n padding-right: 0.5rem !important;\n }\n .pe-xl-3 {\n padding-right: 1rem !important;\n }\n .pe-xl-4 {\n padding-right: 1.5rem !important;\n }\n .pe-xl-5 {\n padding-right: 3rem !important;\n }\n .pb-xl-0 {\n padding-bottom: 0 !important;\n }\n .pb-xl-1 {\n padding-bottom: 0.25rem !important;\n }\n .pb-xl-2 {\n padding-bottom: 0.5rem !important;\n }\n .pb-xl-3 {\n padding-bottom: 1rem !important;\n }\n .pb-xl-4 {\n padding-bottom: 1.5rem !important;\n }\n .pb-xl-5 {\n padding-bottom: 3rem !important;\n }\n .ps-xl-0 {\n padding-left: 0 !important;\n }\n .ps-xl-1 {\n padding-left: 0.25rem !important;\n }\n .ps-xl-2 {\n padding-left: 0.5rem !important;\n }\n .ps-xl-3 {\n padding-left: 1rem !important;\n }\n .ps-xl-4 {\n padding-left: 1.5rem !important;\n }\n .ps-xl-5 {\n padding-left: 3rem !important;\n }\n .gap-xl-0 {\n gap: 0 !important;\n }\n .gap-xl-1 {\n gap: 0.25rem !important;\n }\n .gap-xl-2 {\n gap: 0.5rem !important;\n }\n .gap-xl-3 {\n gap: 1rem !important;\n }\n .gap-xl-4 {\n gap: 1.5rem !important;\n }\n .gap-xl-5 {\n gap: 3rem !important;\n }\n .row-gap-xl-0 {\n row-gap: 0 !important;\n }\n .row-gap-xl-1 {\n row-gap: 0.25rem !important;\n }\n .row-gap-xl-2 {\n row-gap: 0.5rem !important;\n }\n .row-gap-xl-3 {\n row-gap: 1rem !important;\n }\n .row-gap-xl-4 {\n row-gap: 1.5rem !important;\n }\n .row-gap-xl-5 {\n row-gap: 3rem !important;\n }\n .column-gap-xl-0 {\n -moz-column-gap: 0 !important;\n column-gap: 0 !important;\n }\n .column-gap-xl-1 {\n -moz-column-gap: 0.25rem !important;\n column-gap: 0.25rem !important;\n }\n .column-gap-xl-2 {\n -moz-column-gap: 0.5rem !important;\n column-gap: 0.5rem !important;\n }\n .column-gap-xl-3 {\n -moz-column-gap: 1rem !important;\n column-gap: 1rem !important;\n }\n .column-gap-xl-4 {\n -moz-column-gap: 1.5rem !important;\n column-gap: 1.5rem !important;\n }\n .column-gap-xl-5 {\n -moz-column-gap: 3rem !important;\n column-gap: 3rem !important;\n }\n .text-xl-start {\n text-align: left !important;\n }\n .text-xl-end {\n text-align: right !important;\n }\n .text-xl-center {\n text-align: center !important;\n }\n}\n@media (min-width: 1400px) {\n .float-xxl-start {\n float: left !important;\n }\n .float-xxl-end {\n float: right !important;\n }\n .float-xxl-none {\n float: none !important;\n }\n .object-fit-xxl-contain {\n -o-object-fit: contain !important;\n object-fit: contain !important;\n }\n .object-fit-xxl-cover {\n -o-object-fit: cover !important;\n object-fit: cover !important;\n }\n .object-fit-xxl-fill {\n -o-object-fit: fill !important;\n object-fit: fill !important;\n }\n .object-fit-xxl-scale {\n -o-object-fit: scale-down !important;\n object-fit: scale-down !important;\n }\n .object-fit-xxl-none {\n -o-object-fit: none !important;\n object-fit: none !important;\n }\n .d-xxl-inline {\n display: inline !important;\n }\n .d-xxl-inline-block {\n display: inline-block !important;\n }\n .d-xxl-block {\n display: block !important;\n }\n .d-xxl-grid {\n display: grid !important;\n }\n .d-xxl-inline-grid {\n display: inline-grid !important;\n }\n .d-xxl-table {\n display: table !important;\n }\n .d-xxl-table-row {\n display: table-row !important;\n }\n .d-xxl-table-cell {\n display: table-cell !important;\n }\n .d-xxl-flex {\n display: flex !important;\n }\n .d-xxl-inline-flex {\n display: inline-flex !important;\n }\n .d-xxl-none {\n display: none !important;\n }\n .flex-xxl-fill {\n flex: 1 1 auto !important;\n }\n .flex-xxl-row {\n flex-direction: row !important;\n }\n .flex-xxl-column {\n flex-direction: column !important;\n }\n .flex-xxl-row-reverse {\n flex-direction: row-reverse !important;\n }\n .flex-xxl-column-reverse {\n flex-direction: column-reverse !important;\n }\n .flex-xxl-grow-0 {\n flex-grow: 0 !important;\n }\n .flex-xxl-grow-1 {\n flex-grow: 1 !important;\n }\n .flex-xxl-shrink-0 {\n flex-shrink: 0 !important;\n }\n .flex-xxl-shrink-1 {\n flex-shrink: 1 !important;\n }\n .flex-xxl-wrap {\n flex-wrap: wrap !important;\n }\n .flex-xxl-nowrap {\n flex-wrap: nowrap !important;\n }\n .flex-xxl-wrap-reverse {\n flex-wrap: wrap-reverse !important;\n }\n .justify-content-xxl-start {\n justify-content: flex-start !important;\n }\n .justify-content-xxl-end {\n justify-content: flex-end !important;\n }\n .justify-content-xxl-center {\n justify-content: center !important;\n }\n .justify-content-xxl-between {\n justify-content: space-between !important;\n }\n .justify-content-xxl-around {\n justify-content: space-around !important;\n }\n .justify-content-xxl-evenly {\n justify-content: space-evenly !important;\n }\n .align-items-xxl-start {\n align-items: flex-start !important;\n }\n .align-items-xxl-end {\n align-items: flex-end !important;\n }\n .align-items-xxl-center {\n align-items: center !important;\n }\n .align-items-xxl-baseline {\n align-items: baseline !important;\n }\n .align-items-xxl-stretch {\n align-items: stretch !important;\n }\n .align-content-xxl-start {\n align-content: flex-start !important;\n }\n .align-content-xxl-end {\n align-content: flex-end !important;\n }\n .align-content-xxl-center {\n align-content: center !important;\n }\n .align-content-xxl-between {\n align-content: space-between !important;\n }\n .align-content-xxl-around {\n align-content: space-around !important;\n }\n .align-content-xxl-stretch {\n align-content: stretch !important;\n }\n .align-self-xxl-auto {\n align-self: auto !important;\n }\n .align-self-xxl-start {\n align-self: flex-start !important;\n }\n .align-self-xxl-end {\n align-self: flex-end !important;\n }\n .align-self-xxl-center {\n align-self: center !important;\n }\n .align-self-xxl-baseline {\n align-self: baseline !important;\n }\n .align-self-xxl-stretch {\n align-self: stretch !important;\n }\n .order-xxl-first {\n order: -1 !important;\n }\n .order-xxl-0 {\n order: 0 !important;\n }\n .order-xxl-1 {\n order: 1 !important;\n }\n .order-xxl-2 {\n order: 2 !important;\n }\n .order-xxl-3 {\n order: 3 !important;\n }\n .order-xxl-4 {\n order: 4 !important;\n }\n .order-xxl-5 {\n order: 5 !important;\n }\n .order-xxl-last {\n order: 6 !important;\n }\n .m-xxl-0 {\n margin: 0 !important;\n }\n .m-xxl-1 {\n margin: 0.25rem !important;\n }\n .m-xxl-2 {\n margin: 0.5rem !important;\n }\n .m-xxl-3 {\n margin: 1rem !important;\n }\n .m-xxl-4 {\n margin: 1.5rem !important;\n }\n .m-xxl-5 {\n margin: 3rem !important;\n }\n .m-xxl-auto {\n margin: auto !important;\n }\n .mx-xxl-0 {\n margin-right: 0 !important;\n margin-left: 0 !important;\n }\n .mx-xxl-1 {\n margin-right: 0.25rem !important;\n margin-left: 0.25rem !important;\n }\n .mx-xxl-2 {\n margin-right: 0.5rem !important;\n margin-left: 0.5rem !important;\n }\n .mx-xxl-3 {\n margin-right: 1rem !important;\n margin-left: 1rem !important;\n }\n .mx-xxl-4 {\n margin-right: 1.5rem !important;\n margin-left: 1.5rem !important;\n }\n .mx-xxl-5 {\n margin-right: 3rem !important;\n margin-left: 3rem !important;\n }\n .mx-xxl-auto {\n margin-right: auto !important;\n margin-left: auto !important;\n }\n .my-xxl-0 {\n margin-top: 0 !important;\n margin-bottom: 0 !important;\n }\n .my-xxl-1 {\n margin-top: 0.25rem !important;\n margin-bottom: 0.25rem !important;\n }\n .my-xxl-2 {\n margin-top: 0.5rem !important;\n margin-bottom: 0.5rem !important;\n }\n .my-xxl-3 {\n margin-top: 1rem !important;\n margin-bottom: 1rem !important;\n }\n .my-xxl-4 {\n margin-top: 1.5rem !important;\n margin-bottom: 1.5rem !important;\n }\n .my-xxl-5 {\n margin-top: 3rem !important;\n margin-bottom: 3rem !important;\n }\n .my-xxl-auto {\n margin-top: auto !important;\n margin-bottom: auto !important;\n }\n .mt-xxl-0 {\n margin-top: 0 !important;\n }\n .mt-xxl-1 {\n margin-top: 0.25rem !important;\n }\n .mt-xxl-2 {\n margin-top: 0.5rem !important;\n }\n .mt-xxl-3 {\n margin-top: 1rem !important;\n }\n .mt-xxl-4 {\n margin-top: 1.5rem !important;\n }\n .mt-xxl-5 {\n margin-top: 3rem !important;\n }\n .mt-xxl-auto {\n margin-top: auto !important;\n }\n .me-xxl-0 {\n margin-right: 0 !important;\n }\n .me-xxl-1 {\n margin-right: 0.25rem !important;\n }\n .me-xxl-2 {\n margin-right: 0.5rem !important;\n }\n .me-xxl-3 {\n margin-right: 1rem !important;\n }\n .me-xxl-4 {\n margin-right: 1.5rem !important;\n }\n .me-xxl-5 {\n margin-right: 3rem !important;\n }\n .me-xxl-auto {\n margin-right: auto !important;\n }\n .mb-xxl-0 {\n margin-bottom: 0 !important;\n }\n .mb-xxl-1 {\n margin-bottom: 0.25rem !important;\n }\n .mb-xxl-2 {\n margin-bottom: 0.5rem !important;\n }\n .mb-xxl-3 {\n margin-bottom: 1rem !important;\n }\n .mb-xxl-4 {\n margin-bottom: 1.5rem !important;\n }\n .mb-xxl-5 {\n margin-bottom: 3rem !important;\n }\n .mb-xxl-auto {\n margin-bottom: auto !important;\n }\n .ms-xxl-0 {\n margin-left: 0 !important;\n }\n .ms-xxl-1 {\n margin-left: 0.25rem !important;\n }\n .ms-xxl-2 {\n margin-left: 0.5rem !important;\n }\n .ms-xxl-3 {\n margin-left: 1rem !important;\n }\n .ms-xxl-4 {\n margin-left: 1.5rem !important;\n }\n .ms-xxl-5 {\n margin-left: 3rem !important;\n }\n .ms-xxl-auto {\n margin-left: auto !important;\n }\n .p-xxl-0 {\n padding: 0 !important;\n }\n .p-xxl-1 {\n padding: 0.25rem !important;\n }\n .p-xxl-2 {\n padding: 0.5rem !important;\n }\n .p-xxl-3 {\n padding: 1rem !important;\n }\n .p-xxl-4 {\n padding: 1.5rem !important;\n }\n .p-xxl-5 {\n padding: 3rem !important;\n }\n .px-xxl-0 {\n padding-right: 0 !important;\n padding-left: 0 !important;\n }\n .px-xxl-1 {\n padding-right: 0.25rem !important;\n padding-left: 0.25rem !important;\n }\n .px-xxl-2 {\n padding-right: 0.5rem !important;\n padding-left: 0.5rem !important;\n }\n .px-xxl-3 {\n padding-right: 1rem !important;\n padding-left: 1rem !important;\n }\n .px-xxl-4 {\n padding-right: 1.5rem !important;\n padding-left: 1.5rem !important;\n }\n .px-xxl-5 {\n padding-right: 3rem !important;\n padding-left: 3rem !important;\n }\n .py-xxl-0 {\n padding-top: 0 !important;\n padding-bottom: 0 !important;\n }\n .py-xxl-1 {\n padding-top: 0.25rem !important;\n padding-bottom: 0.25rem !important;\n }\n .py-xxl-2 {\n padding-top: 0.5rem !important;\n padding-bottom: 0.5rem !important;\n }\n .py-xxl-3 {\n padding-top: 1rem !important;\n padding-bottom: 1rem !important;\n }\n .py-xxl-4 {\n padding-top: 1.5rem !important;\n padding-bottom: 1.5rem !important;\n }\n .py-xxl-5 {\n padding-top: 3rem !important;\n padding-bottom: 3rem !important;\n }\n .pt-xxl-0 {\n padding-top: 0 !important;\n }\n .pt-xxl-1 {\n padding-top: 0.25rem !important;\n }\n .pt-xxl-2 {\n padding-top: 0.5rem !important;\n }\n .pt-xxl-3 {\n padding-top: 1rem !important;\n }\n .pt-xxl-4 {\n padding-top: 1.5rem !important;\n }\n .pt-xxl-5 {\n padding-top: 3rem !important;\n }\n .pe-xxl-0 {\n padding-right: 0 !important;\n }\n .pe-xxl-1 {\n padding-right: 0.25rem !important;\n }\n .pe-xxl-2 {\n padding-right: 0.5rem !important;\n }\n .pe-xxl-3 {\n padding-right: 1rem !important;\n }\n .pe-xxl-4 {\n padding-right: 1.5rem !important;\n }\n .pe-xxl-5 {\n padding-right: 3rem !important;\n }\n .pb-xxl-0 {\n padding-bottom: 0 !important;\n }\n .pb-xxl-1 {\n padding-bottom: 0.25rem !important;\n }\n .pb-xxl-2 {\n padding-bottom: 0.5rem !important;\n }\n .pb-xxl-3 {\n padding-bottom: 1rem !important;\n }\n .pb-xxl-4 {\n padding-bottom: 1.5rem !important;\n }\n .pb-xxl-5 {\n padding-bottom: 3rem !important;\n }\n .ps-xxl-0 {\n padding-left: 0 !important;\n }\n .ps-xxl-1 {\n padding-left: 0.25rem !important;\n }\n .ps-xxl-2 {\n padding-left: 0.5rem !important;\n }\n .ps-xxl-3 {\n padding-left: 1rem !important;\n }\n .ps-xxl-4 {\n padding-left: 1.5rem !important;\n }\n .ps-xxl-5 {\n padding-left: 3rem !important;\n }\n .gap-xxl-0 {\n gap: 0 !important;\n }\n .gap-xxl-1 {\n gap: 0.25rem !important;\n }\n .gap-xxl-2 {\n gap: 0.5rem !important;\n }\n .gap-xxl-3 {\n gap: 1rem !important;\n }\n .gap-xxl-4 {\n gap: 1.5rem !important;\n }\n .gap-xxl-5 {\n gap: 3rem !important;\n }\n .row-gap-xxl-0 {\n row-gap: 0 !important;\n }\n .row-gap-xxl-1 {\n row-gap: 0.25rem !important;\n }\n .row-gap-xxl-2 {\n row-gap: 0.5rem !important;\n }\n .row-gap-xxl-3 {\n row-gap: 1rem !important;\n }\n .row-gap-xxl-4 {\n row-gap: 1.5rem !important;\n }\n .row-gap-xxl-5 {\n row-gap: 3rem !important;\n }\n .column-gap-xxl-0 {\n -moz-column-gap: 0 !important;\n column-gap: 0 !important;\n }\n .column-gap-xxl-1 {\n -moz-column-gap: 0.25rem !important;\n column-gap: 0.25rem !important;\n }\n .column-gap-xxl-2 {\n -moz-column-gap: 0.5rem !important;\n column-gap: 0.5rem !important;\n }\n .column-gap-xxl-3 {\n -moz-column-gap: 1rem !important;\n column-gap: 1rem !important;\n }\n .column-gap-xxl-4 {\n -moz-column-gap: 1.5rem !important;\n column-gap: 1.5rem !important;\n }\n .column-gap-xxl-5 {\n -moz-column-gap: 3rem !important;\n column-gap: 3rem !important;\n }\n .text-xxl-start {\n text-align: left !important;\n }\n .text-xxl-end {\n text-align: right !important;\n }\n .text-xxl-center {\n text-align: center !important;\n }\n}\n@media (min-width: 1200px) {\n .fs-1 {\n font-size: 2.5rem !important;\n }\n .fs-2 {\n font-size: 2rem !important;\n }\n .fs-3 {\n font-size: 1.75rem !important;\n }\n .fs-4 {\n font-size: 1.5rem !important;\n }\n}\n@media print {\n .d-print-inline {\n display: inline !important;\n }\n .d-print-inline-block {\n display: inline-block !important;\n }\n .d-print-block {\n display: block !important;\n }\n .d-print-grid {\n display: grid !important;\n }\n .d-print-inline-grid {\n display: inline-grid !important;\n }\n .d-print-table {\n display: table !important;\n }\n .d-print-table-row {\n display: table-row !important;\n }\n .d-print-table-cell {\n display: table-cell !important;\n }\n .d-print-flex {\n display: flex !important;\n }\n .d-print-inline-flex {\n display: inline-flex !important;\n }\n .d-print-none {\n display: none !important;\n }\n}\n\n/*# sourceMappingURL=bootstrap.css.map */","// stylelint-disable scss/dimension-no-non-numeric-values\n\n// SCSS RFS mixin\n//\n// Automated responsive values for font sizes, paddings, margins and much more\n//\n// Licensed under MIT (https://github.com/twbs/rfs/blob/main/LICENSE)\n\n// Configuration\n\n// Base value\n$rfs-base-value: 1.25rem !default;\n$rfs-unit: rem !default;\n\n@if $rfs-unit != rem and $rfs-unit != px {\n @error \"`#{$rfs-unit}` is not a valid unit for $rfs-unit. Use `px` or `rem`.\";\n}\n\n// Breakpoint at where values start decreasing if screen width is smaller\n$rfs-breakpoint: 1200px !default;\n$rfs-breakpoint-unit: px !default;\n\n@if $rfs-breakpoint-unit != px and $rfs-breakpoint-unit != em and $rfs-breakpoint-unit != rem {\n @error \"`#{$rfs-breakpoint-unit}` is not a valid unit for $rfs-breakpoint-unit. Use `px`, `em` or `rem`.\";\n}\n\n// Resize values based on screen height and width\n$rfs-two-dimensional: false !default;\n\n// Factor of decrease\n$rfs-factor: 10 !default;\n\n@if type-of($rfs-factor) != number or $rfs-factor <= 1 {\n @error \"`#{$rfs-factor}` is not a valid $rfs-factor, it must be greater than 1.\";\n}\n\n// Mode. Possibilities: \"min-media-query\", \"max-media-query\"\n$rfs-mode: min-media-query !default;\n\n// Generate enable or disable classes. Possibilities: false, \"enable\" or \"disable\"\n$rfs-class: false !default;\n\n// 1 rem = $rfs-rem-value px\n$rfs-rem-value: 16 !default;\n\n// Safari iframe resize bug: https://github.com/twbs/rfs/issues/14\n$rfs-safari-iframe-resize-bug-fix: false !default;\n\n// Disable RFS by setting $enable-rfs to false\n$enable-rfs: true !default;\n\n// Cache $rfs-base-value unit\n$rfs-base-value-unit: unit($rfs-base-value);\n\n@function divide($dividend, $divisor, $precision: 10) {\n $sign: if($dividend > 0 and $divisor > 0 or $dividend < 0 and $divisor < 0, 1, -1);\n $dividend: abs($dividend);\n $divisor: abs($divisor);\n @if $dividend == 0 {\n @return 0;\n }\n @if $divisor == 0 {\n @error \"Cannot divide by 0\";\n }\n $remainder: $dividend;\n $result: 0;\n $factor: 10;\n @while ($remainder > 0 and $precision >= 0) {\n $quotient: 0;\n @while ($remainder >= $divisor) {\n $remainder: $remainder - $divisor;\n $quotient: $quotient + 1;\n }\n $result: $result * 10 + $quotient;\n $factor: $factor * .1;\n $remainder: $remainder * 10;\n $precision: $precision - 1;\n @if ($precision < 0 and $remainder >= $divisor * 5) {\n $result: $result + 1;\n }\n }\n $result: $result * $factor * $sign;\n $dividend-unit: unit($dividend);\n $divisor-unit: unit($divisor);\n $unit-map: (\n \"px\": 1px,\n \"rem\": 1rem,\n \"em\": 1em,\n \"%\": 1%\n );\n @if ($dividend-unit != $divisor-unit and map-has-key($unit-map, $dividend-unit)) {\n $result: $result * map-get($unit-map, $dividend-unit);\n }\n @return $result;\n}\n\n// Remove px-unit from $rfs-base-value for calculations\n@if $rfs-base-value-unit == px {\n $rfs-base-value: divide($rfs-base-value, $rfs-base-value * 0 + 1);\n}\n@else if $rfs-base-value-unit == rem {\n $rfs-base-value: divide($rfs-base-value, divide($rfs-base-value * 0 + 1, $rfs-rem-value));\n}\n\n// Cache $rfs-breakpoint unit to prevent multiple calls\n$rfs-breakpoint-unit-cache: unit($rfs-breakpoint);\n\n// Remove unit from $rfs-breakpoint for calculations\n@if $rfs-breakpoint-unit-cache == px {\n $rfs-breakpoint: divide($rfs-breakpoint, $rfs-breakpoint * 0 + 1);\n}\n@else if $rfs-breakpoint-unit-cache == rem or $rfs-breakpoint-unit-cache == \"em\" {\n $rfs-breakpoint: divide($rfs-breakpoint, divide($rfs-breakpoint * 0 + 1, $rfs-rem-value));\n}\n\n// Calculate the media query value\n$rfs-mq-value: if($rfs-breakpoint-unit == px, #{$rfs-breakpoint}px, #{divide($rfs-breakpoint, $rfs-rem-value)}#{$rfs-breakpoint-unit});\n$rfs-mq-property-width: if($rfs-mode == max-media-query, max-width, min-width);\n$rfs-mq-property-height: if($rfs-mode == max-media-query, max-height, min-height);\n\n// Internal mixin used to determine which media query needs to be used\n@mixin _rfs-media-query {\n @if $rfs-two-dimensional {\n @if $rfs-mode == max-media-query {\n @media (#{$rfs-mq-property-width}: #{$rfs-mq-value}), (#{$rfs-mq-property-height}: #{$rfs-mq-value}) {\n @content;\n }\n }\n @else {\n @media (#{$rfs-mq-property-width}: #{$rfs-mq-value}) and (#{$rfs-mq-property-height}: #{$rfs-mq-value}) {\n @content;\n }\n }\n }\n @else {\n @media (#{$rfs-mq-property-width}: #{$rfs-mq-value}) {\n @content;\n }\n }\n}\n\n// Internal mixin that adds disable classes to the selector if needed.\n@mixin _rfs-rule {\n @if $rfs-class == disable and $rfs-mode == max-media-query {\n // Adding an extra class increases specificity, which prevents the media query to override the property\n &,\n .disable-rfs &,\n &.disable-rfs {\n @content;\n }\n }\n @else if $rfs-class == enable and $rfs-mode == min-media-query {\n .enable-rfs &,\n &.enable-rfs {\n @content;\n }\n } @else {\n @content;\n }\n}\n\n// Internal mixin that adds enable classes to the selector if needed.\n@mixin _rfs-media-query-rule {\n\n @if $rfs-class == enable {\n @if $rfs-mode == min-media-query {\n @content;\n }\n\n @include _rfs-media-query () {\n .enable-rfs &,\n &.enable-rfs {\n @content;\n }\n }\n }\n @else {\n @if $rfs-class == disable and $rfs-mode == min-media-query {\n .disable-rfs &,\n &.disable-rfs {\n @content;\n }\n }\n @include _rfs-media-query () {\n @content;\n }\n }\n}\n\n// Helper function to get the formatted non-responsive value\n@function rfs-value($values) {\n // Convert to list\n $values: if(type-of($values) != list, ($values,), $values);\n\n $val: \"\";\n\n // Loop over each value and calculate value\n @each $value in $values {\n @if $value == 0 {\n $val: $val + \" 0\";\n }\n @else {\n // Cache $value unit\n $unit: if(type-of($value) == \"number\", unit($value), false);\n\n @if $unit == px {\n // Convert to rem if needed\n $val: $val + \" \" + if($rfs-unit == rem, #{divide($value, $value * 0 + $rfs-rem-value)}rem, $value);\n }\n @else if $unit == rem {\n // Convert to px if needed\n $val: $val + \" \" + if($rfs-unit == px, #{divide($value, $value * 0 + 1) * $rfs-rem-value}px, $value);\n } @else {\n // If $value isn't a number (like inherit) or $value has a unit (not px or rem, like 1.5em) or $ is 0, just print the value\n $val: $val + \" \" + $value;\n }\n }\n }\n\n // Remove first space\n @return unquote(str-slice($val, 2));\n}\n\n// Helper function to get the responsive value calculated by RFS\n@function rfs-fluid-value($values) {\n // Convert to list\n $values: if(type-of($values) != list, ($values,), $values);\n\n $val: \"\";\n\n // Loop over each value and calculate value\n @each $value in $values {\n @if $value == 0 {\n $val: $val + \" 0\";\n } @else {\n // Cache $value unit\n $unit: if(type-of($value) == \"number\", unit($value), false);\n\n // If $value isn't a number (like inherit) or $value has a unit (not px or rem, like 1.5em) or $ is 0, just print the value\n @if not $unit or $unit != px and $unit != rem {\n $val: $val + \" \" + $value;\n } @else {\n // Remove unit from $value for calculations\n $value: divide($value, $value * 0 + if($unit == px, 1, divide(1, $rfs-rem-value)));\n\n // Only add the media query if the value is greater than the minimum value\n @if abs($value) <= $rfs-base-value or not $enable-rfs {\n $val: $val + \" \" + if($rfs-unit == rem, #{divide($value, $rfs-rem-value)}rem, #{$value}px);\n }\n @else {\n // Calculate the minimum value\n $value-min: $rfs-base-value + divide(abs($value) - $rfs-base-value, $rfs-factor);\n\n // Calculate difference between $value and the minimum value\n $value-diff: abs($value) - $value-min;\n\n // Base value formatting\n $min-width: if($rfs-unit == rem, #{divide($value-min, $rfs-rem-value)}rem, #{$value-min}px);\n\n // Use negative value if needed\n $min-width: if($value < 0, -$min-width, $min-width);\n\n // Use `vmin` if two-dimensional is enabled\n $variable-unit: if($rfs-two-dimensional, vmin, vw);\n\n // Calculate the variable width between 0 and $rfs-breakpoint\n $variable-width: #{divide($value-diff * 100, $rfs-breakpoint)}#{$variable-unit};\n\n // Return the calculated value\n $val: $val + \" calc(\" + $min-width + if($value < 0, \" - \", \" + \") + $variable-width + \")\";\n }\n }\n }\n }\n\n // Remove first space\n @return unquote(str-slice($val, 2));\n}\n\n// RFS mixin\n@mixin rfs($values, $property: font-size) {\n @if $values != null {\n $val: rfs-value($values);\n $fluid-val: rfs-fluid-value($values);\n\n // Do not print the media query if responsive & non-responsive values are the same\n @if $val == $fluid-val {\n #{$property}: $val;\n }\n @else {\n @include _rfs-rule () {\n #{$property}: if($rfs-mode == max-media-query, $val, $fluid-val);\n\n // Include safari iframe resize fix if needed\n min-width: if($rfs-safari-iframe-resize-bug-fix, (0 * 1vw), null);\n }\n\n @include _rfs-media-query-rule () {\n #{$property}: if($rfs-mode == max-media-query, $fluid-val, $val);\n }\n }\n }\n}\n\n// Shorthand helper mixins\n@mixin font-size($value) {\n @include rfs($value);\n}\n\n@mixin padding($value) {\n @include rfs($value, padding);\n}\n\n@mixin padding-top($value) {\n @include rfs($value, padding-top);\n}\n\n@mixin padding-right($value) {\n @include rfs($value, padding-right);\n}\n\n@mixin padding-bottom($value) {\n @include rfs($value, padding-bottom);\n}\n\n@mixin padding-left($value) {\n @include rfs($value, padding-left);\n}\n\n@mixin margin($value) {\n @include rfs($value, margin);\n}\n\n@mixin margin-top($value) {\n @include rfs($value, margin-top);\n}\n\n@mixin margin-right($value) {\n @include rfs($value, margin-right);\n}\n\n@mixin margin-bottom($value) {\n @include rfs($value, margin-bottom);\n}\n\n@mixin margin-left($value) {\n @include rfs($value, margin-left);\n}\n","// scss-docs-start color-mode-mixin\n@mixin color-mode($mode: light, $root: false) {\n @if $color-mode-type == \"media-query\" {\n @if $root == true {\n @media (prefers-color-scheme: $mode) {\n :root {\n @content;\n }\n }\n } @else {\n @media (prefers-color-scheme: $mode) {\n @content;\n }\n }\n } @else {\n [data-bs-theme=\"#{$mode}\"] {\n @content;\n }\n }\n}\n// scss-docs-end color-mode-mixin\n","// stylelint-disable declaration-no-important, selector-no-qualifying-type, property-no-vendor-prefix\n\n\n// Reboot\n//\n// Normalization of HTML elements, manually forked from Normalize.css to remove\n// styles targeting irrelevant browsers while applying new styles.\n//\n// Normalize is licensed MIT. https://github.com/necolas/normalize.css\n\n\n// Document\n//\n// Change from `box-sizing: content-box` so that `width` is not affected by `padding` or `border`.\n\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n\n// Root\n//\n// Ability to the value of the root font sizes, affecting the value of `rem`.\n// null by default, thus nothing is generated.\n\n:root {\n @if $font-size-root != null {\n @include font-size(var(--#{$prefix}root-font-size));\n }\n\n @if $enable-smooth-scroll {\n @media (prefers-reduced-motion: no-preference) {\n scroll-behavior: smooth;\n }\n }\n}\n\n\n// Body\n//\n// 1. Remove the margin in all browsers.\n// 2. As a best practice, apply a default `background-color`.\n// 3. Prevent adjustments of font size after orientation changes in iOS.\n// 4. Change the default tap highlight to be completely transparent in iOS.\n\n// scss-docs-start reboot-body-rules\nbody {\n margin: 0; // 1\n font-family: var(--#{$prefix}body-font-family);\n @include font-size(var(--#{$prefix}body-font-size));\n font-weight: var(--#{$prefix}body-font-weight);\n line-height: var(--#{$prefix}body-line-height);\n color: var(--#{$prefix}body-color);\n text-align: var(--#{$prefix}body-text-align);\n background-color: var(--#{$prefix}body-bg); // 2\n -webkit-text-size-adjust: 100%; // 3\n -webkit-tap-highlight-color: rgba($black, 0); // 4\n}\n// scss-docs-end reboot-body-rules\n\n\n// Content grouping\n//\n// 1. Reset Firefox's gray color\n\nhr {\n margin: $hr-margin-y 0;\n color: $hr-color; // 1\n border: 0;\n border-top: $hr-border-width solid $hr-border-color;\n opacity: $hr-opacity;\n}\n\n\n// Typography\n//\n// 1. Remove top margins from headings\n// By default, `

        `-`

        ` all receive top and bottom margins. We nuke the top\n// margin for easier control within type scales as it avoids margin collapsing.\n\n%heading {\n margin-top: 0; // 1\n margin-bottom: $headings-margin-bottom;\n font-family: $headings-font-family;\n font-style: $headings-font-style;\n font-weight: $headings-font-weight;\n line-height: $headings-line-height;\n color: var(--#{$prefix}heading-color);\n}\n\nh1 {\n @extend %heading;\n @include font-size($h1-font-size);\n}\n\nh2 {\n @extend %heading;\n @include font-size($h2-font-size);\n}\n\nh3 {\n @extend %heading;\n @include font-size($h3-font-size);\n}\n\nh4 {\n @extend %heading;\n @include font-size($h4-font-size);\n}\n\nh5 {\n @extend %heading;\n @include font-size($h5-font-size);\n}\n\nh6 {\n @extend %heading;\n @include font-size($h6-font-size);\n}\n\n\n// Reset margins on paragraphs\n//\n// Similarly, the top margin on `

        `s get reset. However, we also reset the\n// bottom margin to use `rem` units instead of `em`.\n\np {\n margin-top: 0;\n margin-bottom: $paragraph-margin-bottom;\n}\n\n\n// Abbreviations\n//\n// 1. Add the correct text decoration in Chrome, Edge, Opera, and Safari.\n// 2. Add explicit cursor to indicate changed behavior.\n// 3. Prevent the text-decoration to be skipped.\n\nabbr[title] {\n text-decoration: underline dotted; // 1\n cursor: help; // 2\n text-decoration-skip-ink: none; // 3\n}\n\n\n// Address\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\n\n// Lists\n\nol,\nul {\n padding-left: 2rem;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: $dt-font-weight;\n}\n\n// 1. Undo browser default\n\ndd {\n margin-bottom: .5rem;\n margin-left: 0; // 1\n}\n\n\n// Blockquote\n\nblockquote {\n margin: 0 0 1rem;\n}\n\n\n// Strong\n//\n// Add the correct font weight in Chrome, Edge, and Safari\n\nb,\nstrong {\n font-weight: $font-weight-bolder;\n}\n\n\n// Small\n//\n// Add the correct font size in all browsers\n\nsmall {\n @include font-size($small-font-size);\n}\n\n\n// Mark\n\nmark {\n padding: $mark-padding;\n color: var(--#{$prefix}highlight-color);\n background-color: var(--#{$prefix}highlight-bg);\n}\n\n\n// Sub and Sup\n//\n// Prevent `sub` and `sup` elements from affecting the line height in\n// all browsers.\n\nsub,\nsup {\n position: relative;\n @include font-size($sub-sup-font-size);\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub { bottom: -.25em; }\nsup { top: -.5em; }\n\n\n// Links\n\na {\n color: rgba(var(--#{$prefix}link-color-rgb), var(--#{$prefix}link-opacity, 1));\n text-decoration: $link-decoration;\n\n &:hover {\n --#{$prefix}link-color-rgb: var(--#{$prefix}link-hover-color-rgb);\n text-decoration: $link-hover-decoration;\n }\n}\n\n// And undo these styles for placeholder links/named anchors (without href).\n// It would be more straightforward to just use a[href] in previous block, but that\n// causes specificity issues in many other styles that are too complex to fix.\n// See https://github.com/twbs/bootstrap/issues/19402\n\na:not([href]):not([class]) {\n &,\n &:hover {\n color: inherit;\n text-decoration: none;\n }\n}\n\n\n// Code\n\npre,\ncode,\nkbd,\nsamp {\n font-family: $font-family-code;\n @include font-size(1em); // Correct the odd `em` font sizing in all browsers.\n}\n\n// 1. Remove browser default top margin\n// 2. Reset browser default of `1em` to use `rem`s\n// 3. Don't allow content to break outside\n\npre {\n display: block;\n margin-top: 0; // 1\n margin-bottom: 1rem; // 2\n overflow: auto; // 3\n @include font-size($code-font-size);\n color: $pre-color;\n\n // Account for some code outputs that place code tags in pre tags\n code {\n @include font-size(inherit);\n color: inherit;\n word-break: normal;\n }\n}\n\ncode {\n @include font-size($code-font-size);\n color: var(--#{$prefix}code-color);\n word-wrap: break-word;\n\n // Streamline the style when inside anchors to avoid broken underline and more\n a > & {\n color: inherit;\n }\n}\n\nkbd {\n padding: $kbd-padding-y $kbd-padding-x;\n @include font-size($kbd-font-size);\n color: $kbd-color;\n background-color: $kbd-bg;\n @include border-radius($border-radius-sm);\n\n kbd {\n padding: 0;\n @include font-size(1em);\n font-weight: $nested-kbd-font-weight;\n }\n}\n\n\n// Figures\n//\n// Apply a consistent margin strategy (matches our type styles).\n\nfigure {\n margin: 0 0 1rem;\n}\n\n\n// Images and content\n\nimg,\nsvg {\n vertical-align: middle;\n}\n\n\n// Tables\n//\n// Prevent double borders\n\ntable {\n caption-side: bottom;\n border-collapse: collapse;\n}\n\ncaption {\n padding-top: $table-cell-padding-y;\n padding-bottom: $table-cell-padding-y;\n color: $table-caption-color;\n text-align: left;\n}\n\n// 1. Removes font-weight bold by inheriting\n// 2. Matches default `` alignment by inheriting `text-align`.\n// 3. Fix alignment for Safari\n\nth {\n font-weight: $table-th-font-weight; // 1\n text-align: inherit; // 2\n text-align: -webkit-match-parent; // 3\n}\n\nthead,\ntbody,\ntfoot,\ntr,\ntd,\nth {\n border-color: inherit;\n border-style: solid;\n border-width: 0;\n}\n\n\n// Forms\n//\n// 1. Allow labels to use `margin` for spacing.\n\nlabel {\n display: inline-block; // 1\n}\n\n// Remove the default `border-radius` that macOS Chrome adds.\n// See https://github.com/twbs/bootstrap/issues/24093\n\nbutton {\n // stylelint-disable-next-line property-disallowed-list\n border-radius: 0;\n}\n\n// Explicitly remove focus outline in Chromium when it shouldn't be\n// visible (e.g. as result of mouse click or touch tap). It already\n// should be doing this automatically, but seems to currently be\n// confused and applies its very visible two-tone outline anyway.\n\nbutton:focus:not(:focus-visible) {\n outline: 0;\n}\n\n// 1. Remove the margin in Firefox and Safari\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0; // 1\n font-family: inherit;\n @include font-size(inherit);\n line-height: inherit;\n}\n\n// Remove the inheritance of text transform in Firefox\nbutton,\nselect {\n text-transform: none;\n}\n// Set the cursor for non-`'+this.options.header+"

        "),this.options.liveSearch&&(o=''),this.multiple&&this.options.actionsBox&&(l='
        "),this.multiple&&this.options.doneButton&&(r='
        "),this.options.allowClear&&(a='×'),e='",$(e)},setPositionData:function(){this.selectpicker.view.canHighlight=[],this.selectpicker.view.size=0,this.selectpicker.view.firstHighlightIndex=!1;for(var e=0;e=this.options.virtualScroll||!0===this.options.virtualScroll},createView:function(y,e,t){var x=this,i=0;function E(e,t){var i,s=x.selectpicker.current.data.length,n=[],o=!0,l=x.isVirtual();x.selectpicker.view.scrollTop=e;for(var r,a=x.options.chunkSize,c=Math.ceil(s/a)||1,d=0;ds-1?0:x.selectpicker.current.data[s-1].position-x.selectpicker.current.data[x.selectpicker.view.position1-1].position,p.firstChild.style.marginTop=g+"px",p.firstChild.style.marginBottom=o+"px"):(p.firstChild.style.marginTop=0,p.firstChild.style.marginBottom=0),p.firstChild.appendChild(u),!0===l&&x.sizeInfo.hasScrollBar&&(f=p.firstChild.offsetWidth,t&&fx.sizeInfo.selectWidth?p.firstChild.style.minWidth=x.sizeInfo.menuInnerInnerWidth+"px":f>x.sizeInfo.menuInnerInnerWidth&&(x.$menu[0].style.minWidth=0,(g=p.firstChild.offsetWidth)>x.sizeInfo.menuInnerInnerWidth&&(x.sizeInfo.menuInnerInnerWidth=g,p.firstChild.style.minWidth=x.sizeInfo.menuInnerInnerWidth+"px"),x.$menu[0].style.minWidth=""))}(!y&&x.options.source.data||y&&x.options.source.search)&&x.selectpicker.current.hasMore&&i===c-1&&0 *:not(:disabled)"))?o.index:0,l=(l=window.performance&&window.performance.getEntriesByType("navigation"))&&l.length?"back_forward"!==l[0].type:2!==window.performance.navigation.type,i&&(this.selectpicker.view.titleOption.className="bs-title-option",this.selectpicker.view.titleOption.value="",t=!n||s===o&&!1===n.defaultSelected&&void 0===this.$element.data("selected")),!i&&0===this.selectpicker.view.titleOption.index||e.insertBefore(this.selectpicker.view.titleOption,e.firstChild),t&&l?e.selectedIndex=0:"complete"!==document.readyState&&window.addEventListener("pageshow",function(){r.selectpicker.view.displayedValue!==e.value&&r.render()})),a},fetchData:function(n,o,e,t){e=e||1,o=o||"data";var l,r=this,i=this.options.source[o];i?(this.options.virtualScroll=!0,"function"==typeof i?i.call(this,function(e,t,i){var s=r.selectpicker["search"===o?"search":"main"];s.hasMore=t,s.totalItems=i,l=r.buildData(e,o),n.call(r,l),r.$element.trigger("fetched"+A)},e,t):Array.isArray(i)&&(l=r.buildData(i,o),n.call(r,l))):(l=this.buildData(!1,o),n.call(r,l))},buildData:function(h,e){var o=this,p=!1===h?N.fromOption:N.fromDataSource,u=':not([hidden]):not([data-hidden="true"]):not([style*="display: none"])',f=[],l=this.selectpicker.main.data?this.selectpicker.main.data.length:0,m=0,v=this.setPlaceholder()&&!h?1:0,t=("search"===e&&(l=this.selectpicker.search.data.length),this.options.hideDisabled&&(u+=":not(:disabled)"),h?h.filter(ne,this):this.$element[0].querySelectorAll("select > *"+u));function g(e){var t=f[f.length-1];t&&"divider"===t.type&&(t.optID||e.optID)||((e=e||{}).type="divider",f.push(e))}function b(e,t){var i,s,n;(t=t||{}).divider=p(e,"divider"),!0===t.divider?g({optID:t.optID}):(i=f.length+l,s=(s=p(e,"style"))?k(s):"",n=(e.className||"")+(t.optgroupClass||""),t.optID&&(n="opt "+n),t.optionClass=n.trim(),t.inlineStyle=s,t.text=p(e,"text"),t.title=p(e,"title"),t.content=p(e,"content"),t.tokens=p(e,"tokens"),t.subtext=p(e,"subtext"),t.icon=p(e,"icon"),t.display=t.content||t.text,t.value=void 0===e.value?e.text:e.value,t.type="option",t.index=i,t.option=e.option||e,t.option.liIndex=i,t.selected=!!e.selected,t.disabled=t.disabled||!!e.disabled,!1!==h&&(o.selectpicker.optionValuesDataMap[t.value]?t=$.extend(o.selectpicker.optionValuesDataMap[t.value],t):o.selectpicker.optionValuesDataMap[t.value]=t),f.push(t))}function i(e,t){var i=t[e],s=!(e-1 li")},render:function(e){var i=this,t=this.$element[0],s=this.setPlaceholder()&&0===t.selectedIndex,n=y.call(this),o=n.length,l=x.call(this,n),r=this.$button[0],a=r.querySelector(".filter-option-inner-inner"),c=document.createTextNode(this.options.multipleSeparator),d=D.fragment.cloneNode(!1),h=!1;if(this.options.source.data&&e&&(n.map(function e(t){t.selected?i.createOption(t,!0):t.children&&t.children.length&&t.children.map(e)}),t.appendChild(this.selectpicker.main.optionQueue),s=s&&0===t.selectedIndex),r.classList.toggle("bs-placeholder",i.multiple?!o:!l&&0!==l),i.multiple||1!==n.length||(i.selectpicker.view.displayedValue=l),"static"===this.options.selectedTextFormat)d=L.text.call(this,{text:this.options.placeholder},!0);else if(!1===(this.multiple&&-1!==this.options.selectedTextFormat.indexOf("count")&&0")).length&&o>e[1]||1===e.length&&2<=o))){if(!s){for(var p=0;p option"+t+", optgroup"+t+" option"+t).length),e="function"==typeof this.options.countSelectedText?this.options.countSelectedText(o,l):this.options.countSelectedText,d=L.text.call(this,{text:e.replace("{0}",o.toString()).replace("{1}",l.toString())},!0);d.childNodes.length||(d=L.text.call(this,{text:this.options.placeholder||this.options.noneSelectedText},!0)),r.title=d.textContent.replace(/<[^>]*>?/g,"").trim(),this.options.sanitize&&h&&S([d],i.options.whiteList,i.options.sanitizeFn),a.innerHTML="",a.appendChild(d),g.major<4&&this.$newElement[0].classList.contains("bs3-has-addon")&&(s=r.querySelector(".filter-expand"),(t=a.cloneNode(!0)).className="filter-expand",s?r.replaceChild(t,s):r.appendChild(t)),this.$element.trigger("rendered"+A)},setStyle:function(e,t){var i=this.$button[0],s=this.$newElement[0],n=this.options.style.trim();this.$element.attr("class")&&this.$newElement.addClass(this.$element.attr("class").replace(/selectpicker|mobile-device|bs-select-hidden|validate\[.*\]/gi,"")),g.major<4&&(s.classList.add("bs3"),s.parentNode.classList&&s.parentNode.classList.contains("input-group")&&(s.previousElementSibling||s.nextElementSibling)&&(s.previousElementSibling||s.nextElementSibling).classList.contains("input-group-addon")&&s.classList.add("bs3-has-addon")),s=e?e.trim():n,"add"==t?s&&i.classList.add.apply(i.classList,s.split(" ")):"remove"==t?s&&i.classList.remove.apply(i.classList,s.split(" ")):(n&&i.classList.remove.apply(i.classList,n.split(" ")),s&&i.classList.add.apply(i.classList,s.split(" ")))},liHeight:function(e){if(e||!1!==this.options.size&&!Object.keys(this.sizeInfo).length){var t,e=D.div.cloneNode(!1),i=D.div.cloneNode(!1),s=D.div.cloneNode(!1),n=document.createElement("ul"),o=D.li.cloneNode(!1),l=D.li.cloneNode(!1),r=D.a.cloneNode(!1),a=D.span.cloneNode(!1),c=this.options.header&&0this.sizeInfo.menuExtras.vert&&l+this.sizeInfo.menuExtras.vert+50>this.sizeInfo.selectOffsetBot,!0===this.selectpicker.isSearching&&(l=this.selectpicker.dropup),this.$newElement.toggleClass(T.DROPUP,l),this.selectpicker.dropup=l),"auto"===this.options.size)l=3this.options.size){for(var v=0;vthis.sizeInfo.menuInnerHeight&&(this.sizeInfo.hasScrollBar=!0,this.sizeInfo.totalMenuWidth=this.sizeInfo.menuWidth+this.sizeInfo.scrollBarWidth),"auto"===this.options.dropdownAlignRight&&this.$menu.toggleClass(T.MENURIGHT,this.sizeInfo.selectOffsetLeft>this.sizeInfo.selectOffsetRight&&this.sizeInfo.selectOffsetRightthis.options.size&&i.off("resize"+A+"."+this.selectId+".setMenuSize scroll"+A+"."+this.selectId+".setMenuSize")),this.createView(!1,!0,e)},setWidth:function(){var i=this;"auto"===this.options.width?requestAnimationFrame(function(){i.$menu.css("min-width","0"),i.$element.on("loaded"+A,function(){i.liHeight(),i.setMenuSize();var e=i.$newElement.clone().appendTo("body"),t=e.css("width","auto").children("button").outerWidth();e.remove(),i.sizeInfo.selectWidth=Math.max(i.sizeInfo.totalMenuWidth,t),i.$newElement.css("width",i.sizeInfo.selectWidth+"px")})}):"fit"===this.options.width?(this.$menu.css("min-width",""),this.$newElement.css("width","").addClass("fit-width")):this.options.width?(this.$menu.css("min-width",""),this.$newElement.css("width",this.options.width)):(this.$menu.css("min-width",""),this.$newElement.css("width","")),this.$newElement.hasClass("fit-width")&&"fit"!==this.options.width&&this.$newElement[0].classList.remove("fit-width")},selectPosition:function(){this.$bsContainer=$('
        ');function e(e){var t={},i=l.options.display||!!$.fn.dropdown.Constructor.Default&&$.fn.dropdown.Constructor.Default.display;l.$bsContainer.addClass(e.attr("class").replace(/form-control|fit-width/gi,"")).toggleClass(T.DROPUP,e.hasClass(T.DROPUP)),s=e.offset(),r.is("body")?n={top:0,left:0}:((n=r.offset()).top+=parseInt(r.css("borderTopWidth"))-r.scrollTop(),n.left+=parseInt(r.css("borderLeftWidth"))-r.scrollLeft()),o=e.hasClass(T.DROPUP)?0:e[0].offsetHeight,(g.major<4||"static"===i)&&(t.top=s.top-n.top+o,t.left=s.left-n.left),t.width=e[0].offsetWidth,l.$bsContainer.css(t)}var s,n,o,l=this,r=$(this.options.container);this.$button.on("click.bs.dropdown.data-api",function(){l.isDisabled()||(e(l.$newElement),l.$bsContainer.appendTo(l.options.container).toggleClass(T.SHOW,!l.$button.hasClass(T.SHOW)).append(l.$menu))}),$(window).off("resize"+A+"."+this.selectId+" scroll"+A+"."+this.selectId).on("resize"+A+"."+this.selectId+" scroll"+A+"."+this.selectId,function(){l.$newElement.hasClass(T.SHOW)&&e(l.$newElement)}),this.$element.on("hide"+A,function(){l.$menu.data("height",l.$menu.height()),l.$bsContainer.detach()})},createOption:function(e,t){var i,s=e.option||e;s&&1!==s.nodeType&&(i=(t?D.selectedOption:D.option).cloneNode(!0),void 0!==s.value&&(i.value=s.value),i.textContent=s.text,i.selected=!0,void 0!==s.liIndex?i.liIndex=s.liIndex:t||(i.liIndex=e.index),e.option=i,this.selectpicker.main.optionQueue.appendChild(i))},setOptionStatus:function(e){var t=this;if(t.noScroll=!1,t.selectpicker.view.visibleElements&&t.selectpicker.view.visibleElements.length){for(var i=0;i
        ');d[2]&&(b=b.replace("{var}",d[2][1"+b+"
        ")),c=!1,I.$element.trigger("maxReached"+A)),h&&t&&(k.append($("
        "+w+"
        ")),c=!1,I.$element.trigger("maxReachedGrp"+A)),setTimeout(function(){I.setSelected(o,!1)},10),k[0].classList.add("fadeOut"),setTimeout(function(){k.remove()},1050)}}}else a&&I.setSelected(a,!1),I.setSelected(o,!0);I.options.source.data&&I.$element[0].appendChild(I.selectpicker.main.optionQueue),!I.multiple||I.multiple&&1===I.options.maxOptions?I.$button.trigger("focus"):I.options.liveSearch&&I.$searchbox.trigger("focus"),!c||!I.multiple&&r===s.selectedIndex||(E=[e.index,i.prop("selected"),l],I.$element.triggerNative("change"))}}),this.$menu.on("click","li."+T.DISABLED+" a, ."+T.POPOVERHEADER+", ."+T.POPOVERHEADER+" :not(.close)",function(e){e.currentTarget==this&&(e.preventDefault(),e.stopPropagation(),(I.options.liveSearch&&!$(e.target).hasClass("close")?I.$searchbox:I.$button).trigger("focus"))}),this.$menuInner.on("click",".divider, .dropdown-header",function(e){e.preventDefault(),e.stopPropagation(),(I.options.liveSearch?I.$searchbox:I.$button).trigger("focus")}),this.$menu.on("click","."+T.POPOVERHEADER+" .close",function(){I.$button.trigger("click")}),this.$searchbox.on("click",function(e){e.stopPropagation()}),this.$menu.on("click",".actions-btn",function(e){(I.options.liveSearch?I.$searchbox:I.$button).trigger("focus"),e.preventDefault(),e.stopPropagation(),$(this).hasClass("bs-select-all")?I.selectAll():I.deselectAll()}),this.$button.on("focus"+A,function(e){var t=I.$element[0].getAttribute("tabindex");void 0!==t&&e.originalEvent&&e.originalEvent.isTrusted&&(this.setAttribute("tabindex",t),I.$element[0].setAttribute("tabindex",-1),I.selectpicker.view.tabindex=t)}).on("blur"+A,function(e){void 0!==I.selectpicker.view.tabindex&&e.originalEvent&&e.originalEvent.isTrusted&&(I.$element[0].setAttribute("tabindex",I.selectpicker.view.tabindex),this.setAttribute("tabindex",-1),I.selectpicker.view.tabindex=void 0)}),this.$element.on("change"+A,function(){I.render(),I.$element.trigger("changed"+A,E),E=null}).on("focus"+A,function(){I.options.mobile||I.$button[0].focus()})},liveSearchListener:function(){var p=this;this.$button.on("click.bs.dropdown.data-api",function(){p.$searchbox.val()&&(p.$searchbox.val(""),p.selectpicker.search.previousValue=void 0)}),this.$searchbox.on("click.bs.dropdown.data-api focus.bs.dropdown.data-api touchend.bs.dropdown.data-api",function(e){e.stopPropagation()}),this.$searchbox.on("input propertychange",function(){var t=p.$searchbox[0].value;if(p.selectpicker.search.elements=[],p.selectpicker.search.data=[],t)if(p.selectpicker.search.previousValue=t,p.options.source.search)p.fetchData(function(e){p.render(),p.buildList(void 0,!0),p.noScroll=!0,p.$menuInner.scrollTop(0),p.createView(!0),se.call(p,e,t)},"search",0,t);else{var e=[],i=t.toUpperCase(),s={},n=[],o=p._searchStyle(),l=p.options.liveSearchNormalize;l&&(i=u(i));for(var r=0;r=r.selectpicker.view.canHighlight.length&&(d=r.selectpicker.view.firstHighlightIndex),r.selectpicker.view.canHighlight[d+p]||(d=d+1+r.selectpicker.view.canHighlight.slice(d+p+1).indexOf(!0))),e.preventDefault();var u=p+d;e.which===C?0===p&&d===a.length-1?(r.$menuInner[0].scrollTop=r.$menuInner[0].scrollHeight,u=r.selectpicker.current.elements.length-1):(s=r.selectpicker.current.data[u])&&(c=(n=s.position-s.height)h+r.sizeInfo.menuInnerHeight),i=r.selectpicker.main.elements[l],r.activeElement=i,r.focusItem(i),i&&i.firstChild.focus(),c&&(r.$menuInner[0].scrollTop=n),o.trigger("focus"))}t&&(e.which===w&&!r.selectpicker.keydown.keyHistory||e.which===J||e.which===I&&r.options.selectOnTab)&&(e.which!==w&&e.preventDefault(),r.options.liveSearch&&e.which===w||(r.$menuInner.find(".active a").trigger("click",!0),o.trigger("focus"),r.options.liveSearch||(e.preventDefault(),$(document).data("spaceSelect",!0))))}},mobile:function(){this.options.mobile=!0,this.$element[0].classList.add("mobile-device")},refresh:function(){var e=this,t=$.extend({},this.options,d(this.$element),this.$element.data());this.options=t,this.options.source.data?(this.render(),this.buildList()):this.fetchData(function(){e.render(),e.buildList()}),this.checkDisabled(),this.setStyle(),this.setWidth(),this.setSize(!0),this.$element.trigger("refreshed"+A)},hide:function(){this.$newElement.hide()},show:function(){this.$newElement.show()},remove:function(){this.$newElement.remove(),this.$element.remove()},destroy:function(){this.$newElement.before(this.$element).remove(),(this.$bsContainer||this.$menu).remove(),this.selectpicker.view.titleOption&&this.selectpicker.view.titleOption.parentNode&&this.selectpicker.view.titleOption.parentNode.removeChild(this.selectpicker.view.titleOption),this.$element.off(A).removeData("selectpicker").removeClass("bs-select-hidden selectpicker mobile-device"),$(window).off(A+"."+this.selectId)}};var le=$.fn.selectpicker;function re(){return g.major<5?$.fn.dropdown?($.fn.dropdown.Constructor._dataApiKeydownHandler||$.fn.dropdown.Constructor.prototype.keydown).apply(this,arguments):void 0:m.dataApiKeydownHandler}$.fn.selectpicker=oe,$.fn.selectpicker.Constructor=H,$.fn.selectpicker.noConflict=function(){return $.fn.selectpicker=le,this},$(document).off("keydown.bs.dropdown.data-api").on("keydown.bs.dropdown.data-api",":not(.bootstrap-select) > ["+z.DATA_TOGGLE+"]",re).on("keydown.bs.dropdown.data-api",":not(.bootstrap-select) > .dropdown-menu",re).on("keydown"+A,".bootstrap-select ["+z.DATA_TOGGLE+'], .bootstrap-select [role="listbox"], .bootstrap-select .bs-searchbox input',H.prototype.keydown).on("focusin.modal",".bootstrap-select ["+z.DATA_TOGGLE+'], .bootstrap-select [role="listbox"], .bootstrap-select .bs-searchbox input',function(e){e.stopPropagation()}),document.addEventListener("DOMContentLoaded",function(){$(".selectpicker").each(function(){var e=$(this);oe.call(e,e.data())})})}(e)}); diff --git a/dejacode/static/css/dejacode_admin.css b/dejacode/static/css/dejacode_admin.css new file mode 100644 index 00000000..35a8c407 --- /dev/null +++ b/dejacode/static/css/dejacode_admin.css @@ -0,0 +1,247 @@ +/* +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# DejaCode is a trademark of nexB Inc. +# SPDX-License-Identifier: AGPL-3.0-only +# See https://github.com/nexB/dejacode for support or download. +# See https://aboutcode.org for more information about AboutCode FOSS projects. +# +*/ + +/* Dejacode Admin CSS */ + +/* + Reduce the white space between the header and the content. Set top to 0 (it is + 80 by default) because Annotator uses that value to compute where to position + its button. The default value tricks Annotator into positioning its button + below where it should. +*/ +#grp-content { padding-top: 60px; top: 0; } +body.grp-popup #grp-content { padding-top: 0; top: 0; } +.grp-messagelist {padding: 10px 0 5px;} +#grp-content th {text-align: start !important;} + +/* Header nav-global */ +header#grp-header li.nav-header { padding: 4px 10px; color: #808080; } +.user-options-handler { text-align: end; } + +#logout-form { + display: inline; +} +#logout-form button { + background: none; + border: none; + cursor: pointer; + padding: 0; + text-decoration: underline; +} + +#grp-user-tools>li button { + display: block; + padding: 10px 10px; +} + +#grp-navigation button { + color: #4fb2d3; +} + +/* Icon to edit the object next to a "select" input */ +a.edit-icon, .grp-tools li a.edit-icon-related {height: 16px; width: 16px; margin-left: 5px;} +a.edit-icon:link, a.edit-icon:visited {background-image:url("../img/icon-trash-list-toggle-handler-hover.png");} +a.edit-icon:hover, a.edit-icon:active {background-image:url("../img/icon-trash-list-toggle-handler.png");} +a.edit-icon-autocomplete {display: inline-block; } + +/* Workaround since raw_id fields do not have the widget wrapper that adds "add other" interface */ +a.raw-id-add {width: 21px; height: 16px; display: inline-block; top: 0 !important;} + +/* Icon for the hierarchy link in changelist */ +a.hierarchy-icon {display: inline-block; height: 14px; width: 16px;} +a.hierarchy-icon:link, a.hierarchy-icon:visited {background-image:url("../img/icon-hierarchy.png");} +a.hierarchy-icon:hover, a.hierarchy-icon:active {background-image:url("../img/icon-hierarchy-hover.png");} + +/* Grappelli Dashboard */ +#column_1, #column_2 { width: 330px; } +.grp-link-internal { font-size: 12px; font-weight: bold;} + +/* Help texts */ +#short_description {margin-bottom:7px; font-style:italic;} +#long_description {margin-bottom:7px; font-style:italic; display:none;} + +/* Dataspace logo preview */ +p#logo_preview {margin-top: 5px;} + +/* Highlight for the error block */ +.grp-delete-confirmation .grp-errors { border-color: #bf3030; } + +/* Wider columns for readability */ +#grp-changelist-form .media {overflow: hidden; *overflow: visible; zoom: 1;} +#grp-changelist-form .media img {float: left; margin-right: 5px; margin-bottom: 3px;} +#grp-changelist-form .media p {float: left;} + +/* date/time column of the history views */ +table#grp-change-history tbody th {width: 210px;} + +/* Font color when a filter is active */ +.grp-pulldown-container a.filter-active {color: #C14545;} + +/* Large input test for the advance search model in Admin */ +#advanced_search_dialog input[type="text"] { width: 300px; } + +/* Fix the content over the 'Recent Actions' box for #6180 */ +.grp-listing-small .grp-row { word-wrap: break-word; } + +/* Style for the License Annotation view */ +body.license-annotation #licensetext pre { + width:63%; float:right; white-space: pre-wrap; word-wrap: break-word; background-color: #F5F5F5; + color: #333333; font-family: Menlo,Monaco,Consolas,"Courier New",monospace; + padding: 5px; border: 1px solid rgba(0, 0, 0, 0.15); border-radius: 4px 4px 4px 4px;} +body.license-annotation #tags-list {width:33%; float:left;} +select#annotator-field-1 {width: 100%;} +body.license-annotation em.help-text {display: none; font-style: italic; font-size: 11px; color:#9A9A9A;} + +/* Larger textarea for all the Tabular Inlines */ +.grp-tabular textarea {width: 500px; !important} + +/* Reduce width for description column in changelist */ +table#result_list thead th.column-description { width:35%; } + +/* Remove the dotted border for readonly fields */ +div.grp-readonly, p.grp-readonly { border: none; } + +/* list with disc for changelist table */ +ul.with-disc { list-style: disc outside none; margin: 0 0 0 10px; font-weight: normal;} + +/* Read-only text and guidance in the Inlines */ +.grp-dynamic-form .text {width: 45%; white-space: normal !important;} +.grp-dynamic-form .guidance {width: 45%; white-space: normal !important;} + +/* Clean tab header for the license change form page */ +#license_form .ui-widget-header {background: none; border:none;} +/* Hide the edit related icon in the assigned tag inline */ +#license_form #licenseassignedtag_set-group a.edit-icon {display: none !important;} +/* Do not show help for this */ +#license_form #licenseassignedtag_set-group .grp-help {display: none !important;} +/* groupbox header */ +#license_form .groupbox .grp-tr .grp-td { + border-left: none; border-right: none; font-weight: bold; font-size: 13px; + background: #a1d4e5; + background: -moz-linear-gradient(top, #bcdfeb, #a1d4e5); + background: -webkit-gradient(linear, left top, left bottom, from(#bcdfeb), to(#a1d4e5)); + background: -o-linear-gradient(top, #bcdfeb, #a1d4e5); +} +/* Force the ui-dialog to be over the content */ +.ui-dialog { z-index: 500; } + +/* Fixed size for the License and Component columns in the changelist */ +body.grp-change-list .width200 {width: 200px;} +body.grp-change-list .width300 {width: 300px;} + +/* Used to break URLs value in td for example */ +.word-break { word-break: break-all; } + +.pull-right { float: right; } +.lighter-font { font-weight: lighter; } +.error-alert, .error-alert a { color: #bf3030; font-weight: bold; } + +li.grp-results a.reference-data-link { color: #ff9d2e; border-color: #ff9d2e; } + +/* Style for the admin Component Hierarchy view +----------------------------------------------- */ +.hierarchy_table { width: 100%; margin: 0 auto; } +.hierarchy_table tbody tr td { border-left: none; } +/* Put the connector lines behind the content */ +._jsPlumb_connector { z-index: -1; } +.hierarchy-box { + width: 370px; + margin-bottom: 5px; + padding: 2px; + overflow: hidden; + background-color:#888888; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} +/* No bottom margin when the box is alone or the last */ +.hierarchy-box:last-of-type { margin-bottom: 0; } +.hierarchy-box-content { + margin: 0; + padding: 5px 10px; + font-size: 14px; + line-height: 18px; + background-color: #F5F5F5; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} +.hierarchy-box-content ul.icons-ul {list-style: none;} +.small-bold {font-size: 55%; font-weight: bold;} + +ul.inline {margin-left:0;list-style:none} +ul.inline > li {display:inline-block;*display:inline;*zoom:1;padding-left:5px;} + +/* Improved usage of free space in ComponentAssignedPackage inlines, both in Component and Package form views */ +#componentassignedpackage_set-group .grp-autocomplete-wrapper-fk {width: 100%; max-width: 100%;} +#componentassignedpackage_set-group .ui-autocomplete-input {width: 600px;} + +/* Permission multi-select in the GroupAdmin */ +#group_form .selector {width: 1100px;} +#group_form .selector .selector-available, #group_form .selector .selector-chosen {width: 530px;} +#group_form .selector select[multiple] {height: 350px; max-width: 532px !important; width: 532px !important; padding-right: 5px; overflow: auto;} +#group_form .selector .selector-chosen select[multiple] {height: 385px !important;} +@media screen and (-webkit-min-device-pixel-ratio:0) { + select[multiple] {background-image: none;} +} + +/* RelatedLookupListFilter in changelist view */ +.grp-filter .grp-row .grp-autocomplete-wrapper-fk a.related-lookup {margin: -25px 0;} +.grp-filter .grp-row .grp-autocomplete-wrapper-fk a.grp-related-remove {padding: 0; margin: 0;} + +/* vForeignKeyRawIdAdminField in mass_update view */ +body.mass_update .ui-autocomplete-input {width: 570px} + +/* Fix the add/edit icons next to FK and autocomplete fields in changeform tabular inlines */ +.grp-tabular ul.grp-related-widget-tools {width: 50px;} + +/* Fixed the "Currently:" for AdminURLFieldWidget */ +p.url .vURLField {margin-top: 5px; width: 710px;} + +/* Groups details popup in user form */ +#grp-container article.group-details {padding: 20px;} + +/* Fix for the filter input in related-widget-wrapper with Firefox */ +fieldset.grp-module p.selector-filter label {display: none;} + +/* Fix for the visibility of the awesomplete license expression builder choices */ +fieldset.grp-module div.license_expression, +fieldset.grp-module div.from_expression, +fieldset.grp-module div.to_expression, +fieldset.grp-module div.primary_language, +fieldset.grp-module div.keywords, +fieldset.grp-module div.feature {overflow: visible;} +body.mass_update div.awesomplete {display: block;} +div.awesomplete > ul {z-index: 1000 !important;} + +/* License choices */ +code.license_expression {border: 1px solid #ccc; background-color: #EEE; color: black; padding: 2px 4px; border-radius: 3px;} +button.submit-inline-button {font-weight: bold; padding: 0 5px; width: auto; background-color: #40A7C9; border: 1px solid #2b8aab;} +.license-expression-header {line-height: 25px;} +input#test_expression {width: 300px;} + +/* ProductComponent.feature autocomplete */ +.get_feature_datalist {display: none;} + +/* Product relationships inlines */ +#productcomponents-group .extra-attribution-text, #productpackages-group .extra-attribution-text {min-width: 130px;} +#productcomponents-group .license-expression, #productpackages-group .license-expression {min-width: 120px;} +#productcomponents-group .grp-td, #productpackages-group .grp-td {padding: 5px;} +#productcomponents-group .grp-th, #productpackages-group .grp-th {padding: 1px 10px;} +#productcomponents-group .grp-tools, #productpackages-group .grp-tools {left: 225px; float: left; right: auto;} + +/* Dataspace tab permissions form */ +.underline-dotted {border-bottom: dotted 1px #cccccc;} + +/* Do not display the delete related link */ +.grp-delete-related {display: none !important;} + +/* Break Package.download_url in the changelist */ +.grp-changelist-results .field-download_url {word-break: break-all; min-width: 250px;} \ No newline at end of file diff --git a/dejacode/static/css/dejacode_bootstrap.css b/dejacode/static/css/dejacode_bootstrap.css new file mode 100644 index 00000000..902a00cb --- /dev/null +++ b/dejacode/static/css/dejacode_bootstrap.css @@ -0,0 +1,921 @@ +/* +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# DejaCode is a trademark of nexB Inc. +# SPDX-License-Identifier: AGPL-3.0-only +# See https://github.com/nexB/dejacode for support or download. +# See https://aboutcode.org for more information about AboutCode FOSS projects. +# +*/ + +/* -- Main -- */ +main { + height: 100%; + min-height: 36em; +} +body { + padding-top: 4rem; + font-family: Cerebri Sans,sans-serif; + font-size: .9375rem; + font-weight: 400; + line-height: 1.5; + color: #12263f; + background-color: #fff; +} +body.body-background, .bg-light { + background-color: #f9fbfd; +} +.white-background { + background-color: #fff; +} +a { + color: #2e73d0; + text-decoration: none; +} +a:hover { + color: #0056b3; + text-decoration: underline; +} +a.btn:hover, +a.dropdown-item:hover { + text-decoration: none; +} +.badge-light { + background-color: #ddd; +} +.fs-85pct { + font-size: 85%; +} +.header { + margin-bottom: 1rem; +} +.header-body { + padding-top: 0.5rem; + padding-bottom: 1.0rem; + border-bottom: 0.0625rem solid #e3ebf6; +} +.header-pretitle { + letter-spacing: .08rem; + text-transform: uppercase; + color: #4f5763; + font-size: 0.625rem; + margin-bottom: 0.5rem; + font-weight: 500; + line-height: 1.2; +} +.header-pretitle a { + text-decoration: none; + color: #22589f; +} +.header-title { + margin-bottom: 0; + font-size: 1.625rem; +} +.transparent { + color: transparent; +} +.smaller { + font-size: 90%; +} +code { + color: #ac1459; +} +.fa, .fas, .far { + align-items: center; + display: inline-flex; + justify-content: center; + height: 1rem; + width: 1rem; +} + +/* -- Navbar -- */ +nav.navbar { + padding: .375rem 1rem; + height: 54px; +} +nav.navbar #search-input { + width: 220px; +} +nav.navbar.fixed-top { + background-color: #4b70aa; +} +.navbar-dark .navbar-nav .nav-link { + color: #fff; +} +.navbar .notification-active { + position: absolute; + top: 5px; + left: 25px; + width: 10px; + height: 10px; + content: ''; + border-radius: 50%; +} +.navbar-dark .navbar-nav .active>.nav-link, +.navbar-dark .navbar-nav .nav-link.active, +.navbar-dark .navbar-nav .nav-link.show, +.navbar-dark .navbar-nav .show>.nav-link { + color: #fff; + text-decoration: underline; + text-underline-position: under; +} +.navbar-nav a.nav-link:hover { + text-underline-position: under; +} + +/* -- Pagination -- */ +nav ul.pagination .disabled { + --bs-pagination-disabled-bg: white; +} + +/* -- Back to Top link -- */ +#back-to-top{ + position: fixed; + bottom: 1.25rem; + right: 1.25rem; + cursor: pointer; + display: none; + text-decoration: none; + border-radius: 2.1875rem; + +} +#back-to-top:hover i { + position: relative; + color: #fff; + top: -.4375rem; +} + +/* -- clipboard.js -- */ +.clipboard { + position: relative; +} +.btn-clipboard, +.btn-expend { + position: absolute; + display: none; + top: .375rem; + right: 0; + z-index: 10; + color: #818a91; + cursor: pointer; + background-color: transparent; + border: 0; +} +/*
        has a 15px right-padding that need to be added */ +dd .btn-clipboard { + right: 1.7rem; +} +dd .btn-expend { + right: 3.1rem; +} +.clipboard:hover .btn-clipboard, +.btn-clipboard:focus, +.clipboard:hover .btn-expend, +.btn-expend:focus { + display: block; +} +.btn-clipboard:hover, +.btn-expend:hover { + color: #4b70aa; +} +dd.reduced pre { + max-height: 110px; + overflow: scroll; +} +.tab-content pre { + white-space: pre-wrap; + word-wrap: break-word; +} + +/* -- Footer -- */ +.footer { + font-size: 95%; + background-color: #fff; + border-top: 0.0625rem solid #dae1e9; +} +.footer a, .footer a:hover, +.footer a:focus { + font-weight: 500; + color: #4e5c6e; +} +.footer li { + display: inline-block; +} +.footer li + li { + margin-left: 1rem; +} +@media (min-width: 576px) { + .footer { + text-align: left; + } +} + +/* -- Homepage card layout -- */ +@media (min-width:576px) { + .card-deck.sm-gutter { + margin-right: -0.5rem; + margin-left: -0.5rem; + } + .card-deck.sm-gutter .card{ + margin-right: 0.5rem; + margin-left: 0.5rem; + } +} +.row.sm-gutter { + margin-right: -0.5rem; + margin-left: -0.5rem; +} +.row.sm-gutter > .col { + padding-right: 0.5rem; + padding-left: 0.5rem; +} + +/* -- Object list table -- */ +#object-list-table thead { + position: sticky; + top: 52px; + z-index: 1; + font-size: 0.875rem; +} +th.column-selection { + width: 27px; + max-width: 27px; +} + +/* -- Licenses List -- */ +.licenses-table th.column-name { + min-width: 200px; +} +.licenses-table th.column-usage_policy { + width: 80px; + min-width: 80px; +} +.licenses-table th.column-category { + width: 125px; + min-width: 125px; +} +.licenses-table th.column-category__license_type { + width: 84px; +} +.licenses-table th.column-license_profile { + min-width: 155px; + width: 155px; +} +.licenses-table th[class*=' column-tag__'], +.licenses-table th[class^='column-tag__'] { + width: 70px; +} + +/* -- Owners List -- */ +table.owners-table .column-type { + width: 105px; + max-width: 105px; +} + +/* -- Components List -- */ +table.components-table .column-name { + min-width: 150px; +} +table.components-table .column-version { + min-width: 85px; +} +table.components-table .column-usage_policy { + min-width: 80px; + width: 80px; +} +table.components-table .column-type { + min-width: 70px; + width: 70px; +} +table.components-table .column-license_expression { + width: 200px; +} +table.components-table .column-grouping { + width: 22px; +} +table.components-table .column-keywords { + min-width: 110px; +} +table.components-table .column-owner { + min-width: 57px; +} +table.components-table .column-primary_language { + min-width: 130px; +} + +/* -- Products List -- */ +table.products-table .column-name { + min-width: 180px; +} +table.products-table .column-version { + min-width: 82px; +} +table.products-table .column-primary_language { + min-width: 130px; +} +table.products-table .column-configuration_status { + min-width: 110px; +} +table.products-table .column-keywords { + min-width: 110px; +} +table.products-table .column-license_expression { + min-width: 120px; +} +table.products-table .column-owner { + min-width: 75px; +} + +/* -- Package List -- */ +table.packages-table .column-usage_policy { + width: 80px; +} +table.packages-table .column-primary_language { + width: 130px; +} +.list-inline-margin-sm .list-inline-item:not(:last-child) { + margin-right: 0.3rem; +} + +/* -- Package Details -- */ +textarea.licenseexpressionwidget { + height: 62px; +} +#add-to-product-form .alert ul { + margin-bottom: 0; + padding-left: 10px; +} +#add-to-product-form .invalid-feedback { + display: block; +} + +/* -- PurlDB List -- */ +table.purldb-table .column-type { + min-width: 70px; +} +table.purldb-table .column-name { + min-width: 75px; +} +table.purldb-table .column-version { + min-width: 90px; +} +table.purldb-table .column-license_expression { + max-width: 250px; +} + +/* -- Request button -- */ +.btn-request { + color: #fff; + background-color: #6b15a1; + border-color: #6b15a1; +} +.btn-request:hover, +.btn-request:active, +.btn.show.btn-request { + color: #fff; + background-color: #4d0f74; + border-color: #4d0f74; +} +.btn-request:focus { + box-shadow: 0 0 0 0.2rem rgba(107,21,161,.25); +} +.btn-outline-request { + color: #6b15a1; + border-color: #6b15a1; +} +.btn-outline-request:hover { + color: #fff; + background-color: #6b15a1; + border-color: #6b15a1; +} +.btn-outline-request:focus { + box-shadow: 0 0 0 0.2rem rgba(107,21,161,.25); +} +.btn-outline-request:not(:disabled):not(.disabled):active { + color: #fff; + background-color: #4d0f74; + border-color: #4d0f74; +} + +/* -- Requests list -- */ +.requests-list .card-header ul.nav li { + margin-left: 1rem; +} +.requests-list .card-header ul.nav li > a { + color: #5d6777; + font-size: 0.875rem; +} +.requests-list .card-header ul.nav li > a:hover { + text-decoration: none; +} +.text-bg-request { + background-color: #6b15a1; + color: white; + vertical-align: middle; +} + +/* -- Requests form -- */ +#workflow-request-form fieldset.right-side label { + color: #495057; + font-size: 0.875rem; + font-weight: 600; + margin-bottom: .25rem; +} +#workflow-request-form fieldset.right-side .small-label { + color: #495057; + font-size: 0.875rem; +} + +/* -- Requests details -- */ +.requests-details #comments_section .card-header { + padding: 0.5rem 1rem; +} +.requests-details #comments_section .card-body { + padding: 1rem; +} +.requests-details #comments_section .card-body :last-child { + margin-bottom: 0; +} +.requests-details #comments_section .bg-light { + background-color: #fcfcfc !important; +} +.requests-details #comments_section .border-highlight { + border-color: #5872AA; +} +.requests-details #comments_section blockquote { + padding: 0 1rem; + border-left: 0.25rem solid #ccc; +} +.requests-details #comments_section ul, +.requests-details #comments_section ol { + padding-inline-start: 30px; +} + +/* -- Vulnerabilities -- */ +.vulnerability { + color: #dc3545; +} +.badge-vulnerability { + color: #fff; + background-color: #dc3545; + vertical-align: middle; +} + +/* -- Object form (add/edit) -- */ +.datepicker { + width: 130px; +} +body.object-form .invalid-feedback { + display: block; +} +body.object-form .alert ul { + margin-bottom: 0; + padding-left: 10px; +} +div.awesomplete { + display: block !important; +} +.awesomplete > ul { + max-height: 50vh; + overflow-y: auto; +} +#div_id_component .awesomplete { + display: inline-block !important; +} + +/* -- Products comparison -- */ +body.product-comparison tr.unchanged { + display: none; +} +body.product-comparison ul.left-diff { + background-color: #ffdddd; +} +body.product-comparison ul.right-diff { + background-color: #eaffea; +} +body.product-comparison #div_id_exclude { + margin-bottom: 0.5rem; +} + +/* -- Dynamic columns size in the license tab -- */ +#tab_license tr th { + width: 15%; +} +#tab_license .col-count-1 tr td { + width: 85%; +} +#tab_license .col-count-2 tr td { + width: 42%; +} +#tab_license .col-count-3 tr td { + width: 28%; +} +#tab_license .col-count-4 tr td { + width: 21%; +} + +/* -- Tables -- */ +td.version_grouping { + text-align: center; +} +tr.active td.version_grouping { + border-right: 0.125rem solid #ffc107; + border-top: none; +} +tr.extra-details { + display: none; + font-size: 95%; +} +td.sub-header { + color: #95aac9; + background-color: #f9fbfd; + text-align: center; +} +.table-hover tbody tr:hover { + background-color: #f9fbfd; +} +.table-striped>tbody>tr:nth-of-type(odd)>* { + --bs-table-bg-type: #f9fbfd; +} +.table-fixed-layout { + table-layout: fixed; + word-break: break-all; +} +.table thead tr th { + vertical-align: middle; +} +.table thead tr th a { + color: #000000; +} +.table thead tr th a.sort, +.table thead tr th a.sort:hover { + color: #ccc; + text-decoration: none; + margin-left: 0.0625rem; +} +.table thead tr th a.sort.active { + color: black; +} +.table { + background-color: white; +} + +/* -- Better looks for the popover fake links -- */ +.tag_popover, +a[data-category-pk], +.help_text { + cursor: help; + color: #333333; + border-bottom: dotted 0.0625rem #cccccc; +} +.tag_popover:hover, +.tag_popover:focus, +a[data-category-pk]:hover, +a[data-category-pk]:focus, +.help_text:hover, +.help_text:focus { + text-decoration: none; + border-bottom: dotted 0.0625rem #ff8800; + color: #ff8800; +} + +/* -- Sign Up/In Forms -- */ +body.signup-form { + background-color: #4b70aa; +} +body.signup-form label { + display: none; +} +body.signup-form #div_id_updates_email_notification label { + display: block; + margin-bottom: 0!important; +} +#div_id_updates_email_notification { + margin-bottom: 0!important; +} +body.signup-form div.alert > div { + margin-bottom: 0!important; +} +body.signup-form .custom-checkbox label { + display: initial; + padding-top: 3px; +} +body.signup-form form .alert ul, +form[id^="2fa"] .alert ul { + margin: 0; + padding: 0; + list-style: none; +} +body.signup-form form#registration p.eula { + margin-bottom: 0.3125rem; +} +body.signup-form h1 { + text-align: center; + margin: 25px 0; + color: white; + font-size: 30px; + font-weight: normal; +} +body.signup-form p.eula { + font-weight: bold; +} +body.signup-form .asteriskField { + display: none; +} +body.signup-form .hcaptchawidget { + height:85px !important; + border:0; + text-align:center; +} + +/* -- Cards -- */ +.card-border-color { + border-top: 0.1875rem solid #5872AA; +} +.card-header-divider { + border-bottom: 0.0625rem solid #d9d9d9; + margin: 0 1.538rem 0.5rem; + padding-left: 0; + padding-right: 0; + padding-bottom: 1.2307rem; +} +.card-subtitle { + display: block; + font-size: 1rem; + color: #707070; + margin-top: 0; + margin-bottom: .4615rem; +} + +/* -- Buttons -- */ +.btn-xs { + padding: .25rem .3125rem; + font-size: .6rem; + line-height: normal; + border-radius: .2rem; +} + +.btn-xs .fa, +.btn-xs .fas, +.btn-xs .far { + height: auto; + width: auto; +} + +/* -- Misc -- */ +pre.log { + color: #f1f1f1; + font-family: monospace; + white-space: pre-wrap; + word-wrap: break-word; + background-color: #222; +} +.nav-pills .nav-link.active, +.nav-pills .show>.nav-link { + background-color: #4b70aa; +} +.card, +.table { + box-shadow: rgba(0, 0, 0, 0.05) 0 0.0625rem 0.125rem; +} +.table-md th, +.table td { + padding: .5em; +} +.reference-data-label, .cursor-help { + cursor: help; +} +.text-bg-purldb { + color: #fff; + background-color: #f87060; + vertical-align: middle; +} +.nav-tabs .nav-item.show .nav-link, +.nav-tabs .nav-link.active { + border-top-width: 0.125rem; + border-top-color: #f7bf3c; +} +mark { + background-color: rgba(255, 193, 7, 0.3); + padding: .15em; +} +.bg-light { + background-color: #f9fbfd!important; +} +.annotation_link, +.reverse_annotation_link { + cursor: pointer; +} +.license-expression .policy-icon, .field-license-expression .policy-icon { + margin-left: 0.125rem; +} +.text-muted-darker { + color: #767676; +} +.text-muted-light { + color: #999; +} +a.no-style, +a.no-style:hover { + text-decoration: none; +} +code.key { + background-color: transparent; + color: black; + padding: 0.125rem 0.25rem; + border: 0.0625rem solid #e1e1e8; + border-radius: 0.1875rem; + white-space: nowrap; +} +.white-space-normal { + white-space: normal !important; +} +.dropdown-item.active, +.dropdown-item:active { + background-color: #4b70aa; +} +table thead .dropdown-toggle::after { + margin-left: 0; +} +.color-true { + color: #8eb64e; +} +.fa-md { + font-size: 1.15em; +} +.fa-stack.fa-small { + font-size: 0.6em; +} +pre { + background-color: white; +} +pre.pre-bg-light, +.requests-details #comments_section pre { + border: 0.0625rem solid #dee2e6; + border-radius: .25rem; + padding: 0.5rem; + background-color: #f9fbfd; + box-shadow: inset 0 .15rem .125rem 0 rgba(222,226,230,0.5); +} +pre.json-document { + padding: 0.5em 1em 0.5em 2em !important; +} +.jumbotron-heading { + font-weight: 300; +} +.break-word { + word-break: break-word; +} +.break-all { + word-break: break-all; +} +h4.feature-title { + color: #95aac9; + padding-top: .5rem; +} +h4.feature-title:first-child { + padding-top: 0; +} + +/* -- File content source code -- */ +pre .line-number { + float: left; + margin: 0 1em 0 -1em; + text-align: right; +} +pre .line-number span { + display: block; + padding: 0 .5em 0 1em; + color: #999; +} +#key-files-modal pre code { + white-space: pre; + display: block; + word-wrap: normal; +} +pre .clearfix { + /* Fix display in Firefox */ + white-space: normal; +} +#key-files-modal dt { + font-size: 0.85rem; +} + +/* -- bootstrap-select DejaCode compatibility -- */ +select.bootstrap-select-filter { + display: none; + visibility: hidden; +} +.dropdown-menu-end { + right: 0; + left: auto; +} +.bs-searchbox { + padding: 0.5rem; + background-color: #f7f7f7; + border-bottom: 0.0625rem solid #ebebeb; +} +.bs-searchbox input { + height: 2rem; +} +.bootstrap-select button.dropdown-toggle { + height: 1.3rem; +} +.bootstrap-select.active button.dropdown-toggle { + color: #fff; + background-color: #6c757d; + border-color: #6c757d; +} +.bootstrap-select .popover-header { + padding: 0.5rem 0.75rem; + margin-bottom: 0; + font-size: 1rem; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; + border-top-left-radius: calc(0.3rem - 1px); + border-top-right-radius: calc(0.3rem - 1px); +} +.bootstrap-select .popover-header button.close { + padding: 0; + background-color: transparent; + border: 0; + float: right; + font-size: 1.5rem; + font-weight: 700; + line-height: 1; + color: #000; + text-shadow: 0 1px 0 #fff; + opacity: .5; +} +.bootstrap-select li.selected a { + background-color: #4b70aa; + color: #fff; + text-decoration:none; + outline:0; +} +.bootstrap-select .filter-option { + display: none !important; +} +.bootstrap-select .hidden { + display: none !important; +} +.bootstrap-select .dropdown-menu { + width: 250px; +} +.bootstrap-select .dropdown-menu li a { + padding: 0.3125rem 15px; + border-bottom: 0.0625rem solid #ebebeb; + white-space: normal; + font-size: 14px; +} +/* Custom filters dropdown menu sizes */ +.column-license_expression .dropdown-menu { + width: 350px; +} +.column-primary_language .dropdown-menu { + max-width: 240px; +} +.column-keywords .dropdown-menu { + max-width: 240px; +} +.column-configuration_status .dropdown-menu { + max-width: 300px; +} + +/* -- History timeline -- */ +.timeline { + list-style-type: none; + position: relative; +} +.timeline-item:before, .timeline:before { + content: " "; + display: inline-block; + position: absolute; + z-index: 1; +} +.timeline:before { + background: #dee2e6; + left: 9px; + width: 2px; + height: 100%; +} +.timeline-item:before { + background: #fff; + border-radius: 50%; + border: 3px solid #47bac1; + left: 0; + width: 20px; + height: 20px; +} +.timeline-item.timeline-item-addition:before { + border-color: #28a745; +} +.timeline-item.timeline-item-deletion:before { + border-color: #dc3545; +} +.timeline-item:before, .timeline:before { + content: " "; + display: inline-block; + position: absolute; + z-index: 1; +} diff --git a/dejacode/static/css/hint-2.7.0.min.css b/dejacode/static/css/hint-2.7.0.min.css new file mode 100644 index 00000000..6fafb55d --- /dev/null +++ b/dejacode/static/css/hint-2.7.0.min.css @@ -0,0 +1,5 @@ +/*! Hint.css - v2.7.0 - 2021-10-01 +* https://kushagra.dev/lab/hint/ +* Copyright (c) 2021 Kushagra Gour */ + +[class*=hint--]{position:relative;display:inline-block}[class*=hint--]:after,[class*=hint--]:before{position:absolute;-webkit-transform:translate3d(0,0,0);-moz-transform:translate3d(0,0,0);transform:translate3d(0,0,0);visibility:hidden;opacity:0;z-index:1000000;pointer-events:none;-webkit-transition:.3s ease;-moz-transition:.3s ease;transition:.3s ease;-webkit-transition-delay:0s;-moz-transition-delay:0s;transition-delay:0s}[class*=hint--]:hover:after,[class*=hint--]:hover:before{visibility:visible;opacity:1;-webkit-transition-delay:.1s;-moz-transition-delay:.1s;transition-delay:.1s}[class*=hint--]:before{content:'';position:absolute;background:0 0;border:6px solid transparent;z-index:1000001}[class*=hint--]:after{background:#383838;color:#fff;padding:8px 10px;font-size:12px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;line-height:12px;white-space:nowrap;text-shadow:0 -1px 0 #000;box-shadow:4px 4px 8px rgba(0,0,0,.3)}[class*=hint--][aria-label]:after{content:attr(aria-label)}[class*=hint--][data-hint]:after{content:attr(data-hint)}[aria-label='']:after,[aria-label='']:before,[data-hint='']:after,[data-hint='']:before{display:none!important}.hint--top-left:before,.hint--top-right:before,.hint--top:before{border-top-color:#383838}.hint--bottom-left:before,.hint--bottom-right:before,.hint--bottom:before{border-bottom-color:#383838}.hint--top:after,.hint--top:before{bottom:100%;left:50%}.hint--top:before{margin-bottom:-11px;left:calc(50% - 6px)}.hint--top:after{-webkit-transform:translateX(-50%);-moz-transform:translateX(-50%);transform:translateX(-50%)}.hint--top:hover:before{-webkit-transform:translateY(-8px);-moz-transform:translateY(-8px);transform:translateY(-8px)}.hint--top:hover:after{-webkit-transform:translateX(-50%) translateY(-8px);-moz-transform:translateX(-50%) translateY(-8px);transform:translateX(-50%) translateY(-8px)}.hint--bottom:after,.hint--bottom:before{top:100%;left:50%}.hint--bottom:before{margin-top:-11px;left:calc(50% - 6px)}.hint--bottom:after{-webkit-transform:translateX(-50%);-moz-transform:translateX(-50%);transform:translateX(-50%)}.hint--bottom:hover:before{-webkit-transform:translateY(8px);-moz-transform:translateY(8px);transform:translateY(8px)}.hint--bottom:hover:after{-webkit-transform:translateX(-50%) translateY(8px);-moz-transform:translateX(-50%) translateY(8px);transform:translateX(-50%) translateY(8px)}.hint--right:before{border-right-color:#383838;margin-left:-11px;margin-bottom:-6px}.hint--right:after{margin-bottom:-14px}.hint--right:after,.hint--right:before{left:100%;bottom:50%}.hint--right:hover:after,.hint--right:hover:before{-webkit-transform:translateX(8px);-moz-transform:translateX(8px);transform:translateX(8px)}.hint--left:before{border-left-color:#383838;margin-right:-11px;margin-bottom:-6px}.hint--left:after{margin-bottom:-14px}.hint--left:after,.hint--left:before{right:100%;bottom:50%}.hint--left:hover:after,.hint--left:hover:before{-webkit-transform:translateX(-8px);-moz-transform:translateX(-8px);transform:translateX(-8px)}.hint--top-left:after,.hint--top-left:before{bottom:100%;left:50%}.hint--top-left:before{margin-bottom:-11px;left:calc(50% - 6px)}.hint--top-left:after{-webkit-transform:translateX(-100%);-moz-transform:translateX(-100%);transform:translateX(-100%);margin-left:12px}.hint--top-left:hover:before{-webkit-transform:translateY(-8px);-moz-transform:translateY(-8px);transform:translateY(-8px)}.hint--top-left:hover:after{-webkit-transform:translateX(-100%) translateY(-8px);-moz-transform:translateX(-100%) translateY(-8px);transform:translateX(-100%) translateY(-8px)}.hint--top-right:after,.hint--top-right:before{bottom:100%;left:50%}.hint--top-right:before{margin-bottom:-11px;left:calc(50% - 6px)}.hint--top-right:after{-webkit-transform:translateX(0);-moz-transform:translateX(0);transform:translateX(0);margin-left:-12px}.hint--top-right:hover:after,.hint--top-right:hover:before{-webkit-transform:translateY(-8px);-moz-transform:translateY(-8px);transform:translateY(-8px)}.hint--bottom-left:after,.hint--bottom-left:before{top:100%;left:50%}.hint--bottom-left:before{margin-top:-11px;left:calc(50% - 6px)}.hint--bottom-left:after{-webkit-transform:translateX(-100%);-moz-transform:translateX(-100%);transform:translateX(-100%);margin-left:12px}.hint--bottom-left:hover:before{-webkit-transform:translateY(8px);-moz-transform:translateY(8px);transform:translateY(8px)}.hint--bottom-left:hover:after{-webkit-transform:translateX(-100%) translateY(8px);-moz-transform:translateX(-100%) translateY(8px);transform:translateX(-100%) translateY(8px)}.hint--bottom-right:after,.hint--bottom-right:before{top:100%;left:50%}.hint--bottom-right:before{margin-top:-11px;left:calc(50% - 6px)}.hint--bottom-right:after{-webkit-transform:translateX(0);-moz-transform:translateX(0);transform:translateX(0);margin-left:-12px}.hint--bottom-right:hover:after,.hint--bottom-right:hover:before{-webkit-transform:translateY(8px);-moz-transform:translateY(8px);transform:translateY(8px)}.hint--large:after,.hint--medium:after,.hint--small:after{white-space:normal;line-height:1.4em;word-wrap:break-word}.hint--small:after{width:80px}.hint--medium:after{width:150px}.hint--large:after{width:300px}.hint--error:after{background-color:#b34e4d;text-shadow:0 -1px 0 #592726}.hint--error.hint--top-left:before,.hint--error.hint--top-right:before,.hint--error.hint--top:before{border-top-color:#b34e4d}.hint--error.hint--bottom-left:before,.hint--error.hint--bottom-right:before,.hint--error.hint--bottom:before{border-bottom-color:#b34e4d}.hint--error.hint--left:before{border-left-color:#b34e4d}.hint--error.hint--right:before{border-right-color:#b34e4d}.hint--warning:after{background-color:#c09854;text-shadow:0 -1px 0 #6c5328}.hint--warning.hint--top-left:before,.hint--warning.hint--top-right:before,.hint--warning.hint--top:before{border-top-color:#c09854}.hint--warning.hint--bottom-left:before,.hint--warning.hint--bottom-right:before,.hint--warning.hint--bottom:before{border-bottom-color:#c09854}.hint--warning.hint--left:before{border-left-color:#c09854}.hint--warning.hint--right:before{border-right-color:#c09854}.hint--info:after{background-color:#3986ac;text-shadow:0 -1px 0 #1a3c4d}.hint--info.hint--top-left:before,.hint--info.hint--top-right:before,.hint--info.hint--top:before{border-top-color:#3986ac}.hint--info.hint--bottom-left:before,.hint--info.hint--bottom-right:before,.hint--info.hint--bottom:before{border-bottom-color:#3986ac}.hint--info.hint--left:before{border-left-color:#3986ac}.hint--info.hint--right:before{border-right-color:#3986ac}.hint--success:after{background-color:#458746;text-shadow:0 -1px 0 #1a321a}.hint--success.hint--top-left:before,.hint--success.hint--top-right:before,.hint--success.hint--top:before{border-top-color:#458746}.hint--success.hint--bottom-left:before,.hint--success.hint--bottom-right:before,.hint--success.hint--bottom:before{border-bottom-color:#458746}.hint--success.hint--left:before{border-left-color:#458746}.hint--success.hint--right:before{border-right-color:#458746}.hint--always:after,.hint--always:before{opacity:1;visibility:visible}.hint--always.hint--top:before{-webkit-transform:translateY(-8px);-moz-transform:translateY(-8px);transform:translateY(-8px)}.hint--always.hint--top:after{-webkit-transform:translateX(-50%) translateY(-8px);-moz-transform:translateX(-50%) translateY(-8px);transform:translateX(-50%) translateY(-8px)}.hint--always.hint--top-left:before{-webkit-transform:translateY(-8px);-moz-transform:translateY(-8px);transform:translateY(-8px)}.hint--always.hint--top-left:after{-webkit-transform:translateX(-100%) translateY(-8px);-moz-transform:translateX(-100%) translateY(-8px);transform:translateX(-100%) translateY(-8px)}.hint--always.hint--top-right:after,.hint--always.hint--top-right:before{-webkit-transform:translateY(-8px);-moz-transform:translateY(-8px);transform:translateY(-8px)}.hint--always.hint--bottom:before{-webkit-transform:translateY(8px);-moz-transform:translateY(8px);transform:translateY(8px)}.hint--always.hint--bottom:after{-webkit-transform:translateX(-50%) translateY(8px);-moz-transform:translateX(-50%) translateY(8px);transform:translateX(-50%) translateY(8px)}.hint--always.hint--bottom-left:before{-webkit-transform:translateY(8px);-moz-transform:translateY(8px);transform:translateY(8px)}.hint--always.hint--bottom-left:after{-webkit-transform:translateX(-100%) translateY(8px);-moz-transform:translateX(-100%) translateY(8px);transform:translateX(-100%) translateY(8px)}.hint--always.hint--bottom-right:after,.hint--always.hint--bottom-right:before{-webkit-transform:translateY(8px);-moz-transform:translateY(8px);transform:translateY(8px)}.hint--always.hint--left:after,.hint--always.hint--left:before{-webkit-transform:translateX(-8px);-moz-transform:translateX(-8px);transform:translateX(-8px)}.hint--always.hint--right:after,.hint--always.hint--right:before{-webkit-transform:translateX(8px);-moz-transform:translateX(8px);transform:translateX(8px)}.hint--rounded:after{border-radius:4px}.hint--no-animate:after,.hint--no-animate:before{-webkit-transition-duration:0s;-moz-transition-duration:0s;transition-duration:0s}.hint--bounce:after,.hint--bounce:before{-webkit-transition:opacity .3s ease,visibility .3s ease,-webkit-transform .3s cubic-bezier(.71,1.7,.77,1.24);-moz-transition:opacity .3s ease,visibility .3s ease,-moz-transform .3s cubic-bezier(.71,1.7,.77,1.24);transition:opacity .3s ease,visibility .3s ease,transform .3s cubic-bezier(.71,1.7,.77,1.24)}.hint--no-shadow:after,.hint--no-shadow:before{text-shadow:initial;box-shadow:initial}.hint--no-arrow:before{display:none} \ No newline at end of file diff --git a/dejacode/static/flatpickr/flatpickr-4.5.2.min.css b/dejacode/static/flatpickr/flatpickr-4.5.2.min.css new file mode 100644 index 00000000..5bcd1ff8 --- /dev/null +++ b/dejacode/static/flatpickr/flatpickr-4.5.2.min.css @@ -0,0 +1,13 @@ +.flatpickr-calendar{background:transparent;opacity:0;display:none;text-align:center;visibility:hidden;padding:0;-webkit-animation:none;animation:none;direction:ltr;border:0;font-size:14px;line-height:24px;border-radius:5px;position:absolute;width:307.875px;-webkit-box-sizing:border-box;box-sizing:border-box;-ms-touch-action:manipulation;touch-action:manipulation;background:#fff;-webkit-box-shadow:1px 0 0 #e6e6e6,-1px 0 0 #e6e6e6,0 1px 0 #e6e6e6,0 -1px 0 #e6e6e6,0 3px 13px rgba(0,0,0,0.08);box-shadow:1px 0 0 #e6e6e6,-1px 0 0 #e6e6e6,0 1px 0 #e6e6e6,0 -1px 0 #e6e6e6,0 3px 13px rgba(0,0,0,0.08);}.flatpickr-calendar.open,.flatpickr-calendar.inline{opacity:1;max-height:640px;visibility:visible}.flatpickr-calendar.open{display:inline-block;z-index:99999}.flatpickr-calendar.animate.open{-webkit-animation:fpFadeInDown 300ms cubic-bezier(.23,1,.32,1);animation:fpFadeInDown 300ms cubic-bezier(.23,1,.32,1)}.flatpickr-calendar.inline{display:block;position:relative;top:2px}.flatpickr-calendar.static{position:absolute;top:calc(100% + 2px);}.flatpickr-calendar.static.open{z-index:999;display:block}.flatpickr-calendar.multiMonth .flatpickr-days .dayContainer:nth-child(n+1) .flatpickr-day.inRange:nth-child(7n+7){-webkit-box-shadow:none !important;box-shadow:none !important}.flatpickr-calendar.multiMonth .flatpickr-days .dayContainer:nth-child(n+2) .flatpickr-day.inRange:nth-child(7n+1){-webkit-box-shadow:-2px 0 0 #e6e6e6,5px 0 0 #e6e6e6;box-shadow:-2px 0 0 #e6e6e6,5px 0 0 #e6e6e6}.flatpickr-calendar .hasWeeks .dayContainer,.flatpickr-calendar .hasTime .dayContainer{border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.flatpickr-calendar .hasWeeks .dayContainer{border-left:0}.flatpickr-calendar.showTimeInput.hasTime .flatpickr-time{height:40px;border-top:1px solid #e6e6e6}.flatpickr-calendar.noCalendar.hasTime .flatpickr-time{height:auto}.flatpickr-calendar:before,.flatpickr-calendar:after{position:absolute;display:block;pointer-events:none;border:solid transparent;content:'';height:0;width:0;left:22px}.flatpickr-calendar.rightMost:before,.flatpickr-calendar.rightMost:after{left:auto;right:22px}.flatpickr-calendar:before{border-width:5px;margin:0 -5px}.flatpickr-calendar:after{border-width:4px;margin:0 -4px}.flatpickr-calendar.arrowTop:before,.flatpickr-calendar.arrowTop:after{bottom:100%}.flatpickr-calendar.arrowTop:before{border-bottom-color:#e6e6e6}.flatpickr-calendar.arrowTop:after{border-bottom-color:#fff}.flatpickr-calendar.arrowBottom:before,.flatpickr-calendar.arrowBottom:after{top:100%}.flatpickr-calendar.arrowBottom:before{border-top-color:#e6e6e6}.flatpickr-calendar.arrowBottom:after{border-top-color:#fff}.flatpickr-calendar:focus{outline:0}.flatpickr-wrapper{position:relative;display:inline-block}.flatpickr-months{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;}.flatpickr-months .flatpickr-month{background:transparent;color:rgba(0,0,0,0.9);fill:rgba(0,0,0,0.9);height:28px;line-height:1;text-align:center;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;overflow:hidden;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1}.flatpickr-months .flatpickr-prev-month,.flatpickr-months .flatpickr-next-month{text-decoration:none;cursor:pointer;position:absolute;top:0;line-height:16px;height:28px;padding:10px;z-index:3;color:rgba(0,0,0,0.9);fill:rgba(0,0,0,0.9);}.flatpickr-months .flatpickr-prev-month.disabled,.flatpickr-months .flatpickr-next-month.disabled{display:none}.flatpickr-months .flatpickr-prev-month i,.flatpickr-months .flatpickr-next-month i{position:relative}.flatpickr-months .flatpickr-prev-month.flatpickr-prev-month,.flatpickr-months .flatpickr-next-month.flatpickr-prev-month{/* + /*rtl:begin:ignore*/left:0;/* + /*rtl:end:ignore*/}/* + /*rtl:begin:ignore*/ +/* + /*rtl:end:ignore*/ +.flatpickr-months .flatpickr-prev-month.flatpickr-next-month,.flatpickr-months .flatpickr-next-month.flatpickr-next-month{/* + /*rtl:begin:ignore*/right:0;/* + /*rtl:end:ignore*/}/* + /*rtl:begin:ignore*/ +/* + /*rtl:end:ignore*/ +.flatpickr-months .flatpickr-prev-month:hover,.flatpickr-months .flatpickr-next-month:hover{color:#959ea9;}.flatpickr-months .flatpickr-prev-month:hover svg,.flatpickr-months .flatpickr-next-month:hover svg{fill:#f64747}.flatpickr-months .flatpickr-prev-month svg,.flatpickr-months .flatpickr-next-month svg{width:14px;height:14px;}.flatpickr-months .flatpickr-prev-month svg path,.flatpickr-months .flatpickr-next-month svg path{-webkit-transition:fill .1s;transition:fill .1s;fill:inherit}.numInputWrapper{position:relative;height:auto;}.numInputWrapper input,.numInputWrapper span{display:inline-block}.numInputWrapper input{width:100%;}.numInputWrapper input::-ms-clear{display:none}.numInputWrapper span{position:absolute;right:0;width:14px;padding:0 4px 0 2px;height:50%;line-height:50%;opacity:0;cursor:pointer;border:1px solid rgba(57,57,57,0.15);-webkit-box-sizing:border-box;box-sizing:border-box;}.numInputWrapper span:hover{background:rgba(0,0,0,0.1)}.numInputWrapper span:active{background:rgba(0,0,0,0.2)}.numInputWrapper span:after{display:block;content:"";position:absolute}.numInputWrapper span.arrowUp{top:0;border-bottom:0;}.numInputWrapper span.arrowUp:after{border-left:4px solid transparent;border-right:4px solid transparent;border-bottom:4px solid rgba(57,57,57,0.6);top:26%}.numInputWrapper span.arrowDown{top:50%;}.numInputWrapper span.arrowDown:after{border-left:4px solid transparent;border-right:4px solid transparent;border-top:4px solid rgba(57,57,57,0.6);top:40%}.numInputWrapper span svg{width:inherit;height:auto;}.numInputWrapper span svg path{fill:rgba(0,0,0,0.5)}.numInputWrapper:hover{background:rgba(0,0,0,0.05);}.numInputWrapper:hover span{opacity:1}.flatpickr-current-month{font-size:135%;line-height:inherit;font-weight:300;color:inherit;position:absolute;width:75%;left:12.5%;padding:6.16px 0 0 0;line-height:1;height:28px;display:inline-block;text-align:center;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);}.flatpickr-current-month span.cur-month{font-family:inherit;font-weight:700;color:inherit;display:inline-block;margin-left:.5ch;padding:0;}.flatpickr-current-month span.cur-month:hover{background:rgba(0,0,0,0.05)}.flatpickr-current-month .numInputWrapper{width:6ch;width:7ch\0;display:inline-block;}.flatpickr-current-month .numInputWrapper span.arrowUp:after{border-bottom-color:rgba(0,0,0,0.9)}.flatpickr-current-month .numInputWrapper span.arrowDown:after{border-top-color:rgba(0,0,0,0.9)}.flatpickr-current-month input.cur-year{background:transparent;-webkit-box-sizing:border-box;box-sizing:border-box;color:inherit;cursor:text;padding:0 0 0 .5ch;margin:0;display:inline-block;font-size:inherit;font-family:inherit;font-weight:300;line-height:inherit;height:auto;border:0;border-radius:0;vertical-align:initial;}.flatpickr-current-month input.cur-year:focus{outline:0}.flatpickr-current-month input.cur-year[disabled],.flatpickr-current-month input.cur-year[disabled]:hover{font-size:100%;color:rgba(0,0,0,0.5);background:transparent;pointer-events:none}.flatpickr-weekdays{background:transparent;text-align:center;overflow:hidden;width:100%;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;height:28px;}.flatpickr-weekdays .flatpickr-weekdaycontainer{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1}span.flatpickr-weekday{cursor:default;font-size:90%;background:transparent;color:rgba(0,0,0,0.54);line-height:1;margin:0;text-align:center;display:block;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;font-weight:bolder}.dayContainer,.flatpickr-weeks{padding:1px 0 0 0}.flatpickr-days{position:relative;overflow:hidden;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-align:start;-webkit-align-items:flex-start;-ms-flex-align:start;align-items:flex-start;width:307.875px;}.flatpickr-days:focus{outline:0}.dayContainer{padding:0;outline:0;text-align:left;width:307.875px;min-width:307.875px;max-width:307.875px;-webkit-box-sizing:border-box;box-sizing:border-box;display:inline-block;display:-ms-flexbox;display:-webkit-box;display:-webkit-flex;display:flex;-webkit-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-wrap:wrap;-ms-flex-pack:justify;-webkit-justify-content:space-around;justify-content:space-around;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1;}.dayContainer + .dayContainer{-webkit-box-shadow:-1px 0 0 #e6e6e6;box-shadow:-1px 0 0 #e6e6e6}.flatpickr-day{background:none;border:1px solid transparent;border-radius:150px;-webkit-box-sizing:border-box;box-sizing:border-box;color:#393939;cursor:pointer;font-weight:400;width:14.2857143%;-webkit-flex-basis:14.2857143%;-ms-flex-preferred-size:14.2857143%;flex-basis:14.2857143%;max-width:39px;height:39px;line-height:39px;margin:0;display:inline-block;position:relative;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;text-align:center;}.flatpickr-day.inRange,.flatpickr-day.prevMonthDay.inRange,.flatpickr-day.nextMonthDay.inRange,.flatpickr-day.today.inRange,.flatpickr-day.prevMonthDay.today.inRange,.flatpickr-day.nextMonthDay.today.inRange,.flatpickr-day:hover,.flatpickr-day.prevMonthDay:hover,.flatpickr-day.nextMonthDay:hover,.flatpickr-day:focus,.flatpickr-day.prevMonthDay:focus,.flatpickr-day.nextMonthDay:focus{cursor:pointer;outline:0;background:#e6e6e6;border-color:#e6e6e6}.flatpickr-day.today{border-color:#959ea9;}.flatpickr-day.today:hover,.flatpickr-day.today:focus{border-color:#959ea9;background:#959ea9;color:#fff}.flatpickr-day.selected,.flatpickr-day.startRange,.flatpickr-day.endRange,.flatpickr-day.selected.inRange,.flatpickr-day.startRange.inRange,.flatpickr-day.endRange.inRange,.flatpickr-day.selected:focus,.flatpickr-day.startRange:focus,.flatpickr-day.endRange:focus,.flatpickr-day.selected:hover,.flatpickr-day.startRange:hover,.flatpickr-day.endRange:hover,.flatpickr-day.selected.prevMonthDay,.flatpickr-day.startRange.prevMonthDay,.flatpickr-day.endRange.prevMonthDay,.flatpickr-day.selected.nextMonthDay,.flatpickr-day.startRange.nextMonthDay,.flatpickr-day.endRange.nextMonthDay{background:#569ff7;-webkit-box-shadow:none;box-shadow:none;color:#fff;border-color:#569ff7}.flatpickr-day.selected.startRange,.flatpickr-day.startRange.startRange,.flatpickr-day.endRange.startRange{border-radius:50px 0 0 50px}.flatpickr-day.selected.endRange,.flatpickr-day.startRange.endRange,.flatpickr-day.endRange.endRange{border-radius:0 50px 50px 0}.flatpickr-day.selected.startRange + .endRange:not(:nth-child(7n+1)),.flatpickr-day.startRange.startRange + .endRange:not(:nth-child(7n+1)),.flatpickr-day.endRange.startRange + .endRange:not(:nth-child(7n+1)){-webkit-box-shadow:-10px 0 0 #569ff7;box-shadow:-10px 0 0 #569ff7}.flatpickr-day.selected.startRange.endRange,.flatpickr-day.startRange.startRange.endRange,.flatpickr-day.endRange.startRange.endRange{border-radius:50px}.flatpickr-day.inRange{border-radius:0;-webkit-box-shadow:-5px 0 0 #e6e6e6,5px 0 0 #e6e6e6;box-shadow:-5px 0 0 #e6e6e6,5px 0 0 #e6e6e6}.flatpickr-day.disabled,.flatpickr-day.disabled:hover,.flatpickr-day.prevMonthDay,.flatpickr-day.nextMonthDay,.flatpickr-day.notAllowed,.flatpickr-day.notAllowed.prevMonthDay,.flatpickr-day.notAllowed.nextMonthDay{color:rgba(57,57,57,0.3);background:transparent;border-color:transparent;cursor:default}.flatpickr-day.disabled,.flatpickr-day.disabled:hover{cursor:not-allowed;color:rgba(57,57,57,0.1)}.flatpickr-day.week.selected{border-radius:0;-webkit-box-shadow:-5px 0 0 #569ff7,5px 0 0 #569ff7;box-shadow:-5px 0 0 #569ff7,5px 0 0 #569ff7}.flatpickr-day.hidden{visibility:hidden}.rangeMode .flatpickr-day{margin-top:1px}.flatpickr-weekwrapper{display:inline-block;float:left;}.flatpickr-weekwrapper .flatpickr-weeks{padding:0 12px;-webkit-box-shadow:1px 0 0 #e6e6e6;box-shadow:1px 0 0 #e6e6e6}.flatpickr-weekwrapper .flatpickr-weekday{float:none;width:100%;line-height:28px}.flatpickr-weekwrapper span.flatpickr-day,.flatpickr-weekwrapper span.flatpickr-day:hover{display:block;width:100%;max-width:none;color:rgba(57,57,57,0.3);background:transparent;cursor:default;border:none}.flatpickr-innerContainer{display:block;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-sizing:border-box;box-sizing:border-box;overflow:hidden;}.flatpickr-rContainer{display:inline-block;padding:0;-webkit-box-sizing:border-box;box-sizing:border-box}.flatpickr-time{text-align:center;outline:0;display:block;height:0;line-height:40px;max-height:40px;-webkit-box-sizing:border-box;box-sizing:border-box;overflow:hidden;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;}.flatpickr-time:after{content:"";display:table;clear:both}.flatpickr-time .numInputWrapper{-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;width:40%;height:40px;float:left;}.flatpickr-time .numInputWrapper span.arrowUp:after{border-bottom-color:#393939}.flatpickr-time .numInputWrapper span.arrowDown:after{border-top-color:#393939}.flatpickr-time.hasSeconds .numInputWrapper{width:26%}.flatpickr-time.time24hr .numInputWrapper{width:49%}.flatpickr-time input{background:transparent;-webkit-box-shadow:none;box-shadow:none;border:0;border-radius:0;text-align:center;margin:0;padding:0;height:inherit;line-height:inherit;color:#393939;font-size:14px;position:relative;-webkit-box-sizing:border-box;box-sizing:border-box;}.flatpickr-time input.flatpickr-hour{font-weight:bold}.flatpickr-time input.flatpickr-minute,.flatpickr-time input.flatpickr-second{font-weight:400}.flatpickr-time input:focus{outline:0;border:0}.flatpickr-time .flatpickr-time-separator,.flatpickr-time .flatpickr-am-pm{height:inherit;display:inline-block;float:left;line-height:inherit;color:#393939;font-weight:bold;width:2%;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-align-self:center;-ms-flex-item-align:center;align-self:center}.flatpickr-time .flatpickr-am-pm{outline:0;width:18%;cursor:pointer;text-align:center;font-weight:400}.flatpickr-time input:hover,.flatpickr-time .flatpickr-am-pm:hover,.flatpickr-time input:focus,.flatpickr-time .flatpickr-am-pm:focus{background:#f3f3f3}.flatpickr-input[readonly]{cursor:pointer}@-webkit-keyframes fpFadeInDown{from{opacity:0;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}to{opacity:1;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}@keyframes fpFadeInDown{from{opacity:0;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}to{opacity:1;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}} \ No newline at end of file diff --git a/dejacode/static/flatpickr/flatpickr-4.5.2.min.js b/dejacode/static/flatpickr/flatpickr-4.5.2.min.js new file mode 100644 index 00000000..e9602014 --- /dev/null +++ b/dejacode/static/flatpickr/flatpickr-4.5.2.min.js @@ -0,0 +1,2 @@ +/* flatpickr v4.5.2,, @license MIT */ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.flatpickr=t()}(this,function(){"use strict";var X=function(e){return("0"+e).slice(-2)},ee=function(e){return!0===e?1:0};function te(n,a,i){var o;return void 0===i&&(i=!1),function(){var e=this,t=arguments;null!==o&&clearTimeout(o),o=window.setTimeout(function(){o=null,i||n.apply(e,t)},a),i&&!o&&n.apply(e,t)}}var ne=function(e){return e instanceof Array?e:[e]},e=function(){},ae=function(e,t,n){return n.months[t?"shorthand":"longhand"][e]},w={D:e,F:function(e,t,n){e.setMonth(n.months.longhand.indexOf(t))},G:function(e,t){e.setHours(parseFloat(t))},H:function(e,t){e.setHours(parseFloat(t))},J:function(e,t){e.setDate(parseFloat(t))},K:function(e,t,n){e.setHours(e.getHours()%12+12*ee(new RegExp(n.amPM[1],"i").test(t)))},M:function(e,t,n){e.setMonth(n.months.shorthand.indexOf(t))},S:function(e,t){e.setSeconds(parseFloat(t))},U:function(e,t){return new Date(1e3*parseFloat(t))},W:function(e,t){var n=parseInt(t);return new Date(e.getFullYear(),0,2+7*(n-1),0,0,0,0)},Y:function(e,t){e.setFullYear(parseFloat(t))},Z:function(e,t){return new Date(t)},d:function(e,t){e.setDate(parseFloat(t))},h:function(e,t){e.setHours(parseFloat(t))},i:function(e,t){e.setMinutes(parseFloat(t))},j:function(e,t){e.setDate(parseFloat(t))},l:e,m:function(e,t){e.setMonth(parseFloat(t)-1)},n:function(e,t){e.setMonth(parseFloat(t)-1)},s:function(e,t){e.setSeconds(parseFloat(t))},w:e,y:function(e,t){e.setFullYear(2e3+parseFloat(t))}},ie={D:"(\\w+)",F:"(\\w+)",G:"(\\d\\d|\\d)",H:"(\\d\\d|\\d)",J:"(\\d\\d|\\d)\\w+",K:"",M:"(\\w+)",S:"(\\d\\d|\\d)",U:"(.+)",W:"(\\d\\d|\\d)",Y:"(\\d{4})",Z:"(.+)",d:"(\\d\\d|\\d)",h:"(\\d\\d|\\d)",i:"(\\d\\d|\\d)",j:"(\\d\\d|\\d)",l:"(\\w+)",m:"(\\d\\d|\\d)",n:"(\\d\\d|\\d)",s:"(\\d\\d|\\d)",w:"(\\d\\d|\\d)",y:"(\\d{2})"},l={Z:function(e){return e.toISOString()},D:function(e,t,n){return t.weekdays.shorthand[l.w(e,t,n)]},F:function(e,t,n){return ae(l.n(e,t,n)-1,!1,t)},G:function(e,t,n){return X(l.h(e,t,n))},H:function(e){return X(e.getHours())},J:function(e,t){return void 0!==t.ordinal?e.getDate()+t.ordinal(e.getDate()):e.getDate()},K:function(e,t){return t.amPM[ee(11Math.min(t,n)&&e",noCalendar:!1,now:new Date,onChange:[],onClose:[],onDayCreate:[],onDestroy:[],onKeyDown:[],onMonthChange:[],onOpen:[],onParseConfig:[],onReady:[],onValueUpdate:[],onYearChange:[],onPreCalendarPosition:[],plugins:[],position:"auto",positionElement:void 0,prevArrow:"",shorthandCurrentMonth:!1,showMonths:1,static:!1,time_24hr:!1,weekNumbers:!1,wrap:!1};function fe(e,t,n){if(!0===n)return e.classList.add(t);e.classList.remove(t)}function me(e,t,n){var a=window.document.createElement(e);return t=t||"",n=n||"",a.className=t,void 0!==n&&(a.textContent=n),a}function ge(e){for(;e.firstChild;)e.removeChild(e.firstChild)}function pe(e,t){var n=me("div","numInputWrapper"),a=me("input","numInput "+e),i=me("span","arrowUp"),o=me("span","arrowDown");if(a.type="text",a.pattern="\\d*",void 0!==t)for(var r in t)a.setAttribute(r,t[r]);return n.appendChild(a),n.appendChild(i),n.appendChild(o),n}"function"!=typeof Object.assign&&(Object.assign=function(n){if(!n)throw TypeError("Cannot convert undefined or null to object");for(var e=arguments.length,a=new Array(1o)&&(h.amPM.textContent=h.l10n.amPM[ee(h.amPM.textContent===h.l10n.amPM[0])]),n.value=X(c)}}(e);var t=h._input.value;m(),Z(),h._input.value!==t&&h._debouncedChange()}}function m(){if(void 0!==h.hourElement&&void 0!==h.minuteElement){var e,t,n=(parseInt(h.hourElement.value.slice(-2),10)||0)%24,a=(parseInt(h.minuteElement.value,10)||0)%60,i=void 0!==h.secondElement?(parseInt(h.secondElement.value,10)||0)%60:0;void 0!==h.amPM&&(e=n,t=h.amPM.textContent,n=e%12+12*ee(t===h.l10n.amPM[1]));var o=void 0!==h.config.minTime||h.config.minDate&&h.minDateHasTime&&h.latestSelectedDateObj&&0===ce(h.latestSelectedDateObj,h.config.minDate,!0);if(void 0!==h.config.maxTime||h.config.maxDate&&h.maxDateHasTime&&h.latestSelectedDateObj&&0===ce(h.latestSelectedDateObj,h.config.maxDate,!0)){var r=void 0!==h.config.maxTime?h.config.maxTime:h.config.maxDate;(n=Math.min(n,r.getHours()))===r.getHours()&&(a=Math.min(a,r.getMinutes())),a===r.getMinutes()&&(i=Math.min(i,r.getSeconds()))}if(o){var l=void 0!==h.config.minTime?h.config.minTime:h.config.minDate;(n=Math.max(n,l.getHours()))===l.getHours()&&(a=Math.max(a,l.getMinutes())),a===l.getMinutes()&&(i=Math.max(i,l.getSeconds()))}c(n,a,i)}}function i(e){var t=e||h.latestSelectedDateObj;t&&c(t.getHours(),t.getMinutes(),t.getSeconds())}function a(){var e=h.config.defaultHour,t=h.config.defaultMinute,n=h.config.defaultSeconds;if(void 0!==h.config.minDate){var a=h.config.minDate.getHours(),i=h.config.minDate.getMinutes();(e=Math.max(e,a))===a&&(t=Math.max(i,t)),e===a&&t===i&&(n=h.config.minDate.getSeconds())}if(void 0!==h.config.maxDate){var o=h.config.maxDate.getHours(),r=h.config.maxDate.getMinutes();(e=Math.min(e,o))===o&&(t=Math.min(r,t)),e===o&&t===r&&(n=h.config.maxDate.getSeconds())}c(e,t,n)}function c(e,t,n){void 0!==h.latestSelectedDateObj&&h.latestSelectedDateObj.setHours(e%24,t,n||0,0),h.hourElement&&h.minuteElement&&!h.isMobile&&(h.hourElement.value=X(h.config.time_24hr?e:(12+e)%12+12*ee(e%12==0)),h.minuteElement.value=X(t),void 0!==h.amPM&&(h.amPM.textContent=h.l10n.amPM[ee(12<=e)]),void 0!==h.secondElement&&(h.secondElement.value=X(n)))}function n(e){var t=parseInt(e.target.value)+(e.delta||0);(1h.now?h.config.minDate:h.config.maxDate&&h.config.maxDate"+h.config.getWeek(t)+""),q("onDayCreate",r),r}function b(e){e.focus(),"range"===h.config.mode&&P(e)}function w(e){for(var t=0=Math.abs(t))return b(s)}h.changeMonth(i),C(w(i),0)}(a,t):b(a)}function y(e,t){for(var n=(new Date(e,t,1).getDay()-h.l10n.firstDayOfWeek+7)%7,a=h.utils.getDaysInMonth((t-1+12)%12),i=h.utils.getDaysInMonth(t),o=window.document.createDocumentFragment(),r=1\n "+t.join("")+"\n \n "}function I(e,t){void 0===t&&(t=!0);var n=t?e:e-h.currentMonth;n<0&&!0===h._hidePrevMonthArrow||0h.config.maxDate.getFullYear())){var t=e,n=h.currentYear!==t;h.currentYear=t||h.currentYear,h.config.maxDate&&h.currentYear===h.config.maxDate.getFullYear()?h.currentMonth=Math.min(h.config.maxDate.getMonth(),h.currentMonth):h.config.minDate&&h.currentYear===h.config.minDate.getFullYear()&&(h.currentMonth=Math.max(h.config.minDate.getMonth(),h.currentMonth)),n&&(h.redraw(),q("onYearChange"))}}function N(e,t){void 0===t&&(t=!0);var n=h.parseDate(e,void 0,t);if(h.config.minDate&&n&&ce(n,h.config.minDate,void 0!==t?t:!h.minDateHasTime)<0||h.config.maxDate&&n&&0=a.from.getTime()&&n.getTime()<=a.to.getTime())return i}return!i}function F(e){return void 0!==h.daysContainer&&(-1===e.className.indexOf("hidden")&&h.daysContainer.contains(e))}function A(e){var t=e.target===h._input,n=h.config.allowInput,a=h.isOpen&&(!n||!t),i=h.config.inline&&t&&!n;if(13===e.keyCode&&t){if(n)return h.setDate(h._input.value,!0,e.target===h.altInput?h.config.altFormat:h.config.dateFormat),e.target.blur();h.open()}else if(O(e.target)||a||i){var o=!!h.timeContainer&&h.timeContainer.contains(e.target);switch(e.keyCode){case 13:o?f():B(e);break;case 27:e.preventDefault(),R();break;case 8:case 46:t&&!h.config.allowInput&&(e.preventDefault(),h.clear());break;case 37:case 39:if(o)h.hourElement&&h.hourElement.focus();else if(e.preventDefault(),void 0!==h.daysContainer&&(!1===n||F(document.activeElement))){var r=39===e.keyCode?1:-1;e.ctrlKey?(I(r),C(w(1),0)):C(void 0,r)}break;case 38:case 40:e.preventDefault();var l=40===e.keyCode?1:-1;h.daysContainer&&void 0!==e.target.$i?e.ctrlKey?(_(h.currentYear-l),C(w(1),0)):o||C(void 0,7*l):h.config.enableTime&&(!o&&h.hourElement&&h.hourElement.focus(),f(e),h._debouncedChange());break;case 9:if(!o){h.element.focus();break}var c=[h.hourElement,h.minuteElement,h.secondElement,h.amPM].filter(function(e){return e}),d=c.indexOf(e.target);if(-1!==d){var s=c[d+(e.shiftKey?-1:1)];void 0!==s?(e.preventDefault(),s.focus()):h.element.focus()}}}if(void 0!==h.amPM&&e.target===h.amPM)switch(e.key){case h.l10n.amPM[0].charAt(0):case h.l10n.amPM[0].charAt(0).toLowerCase():h.amPM.textContent=h.l10n.amPM[0],m(),Z();break;case h.l10n.amPM[1].charAt(0):case h.l10n.amPM[1].charAt(0).toLowerCase():h.amPM.textContent=h.l10n.amPM[1],m(),Z()}q("onKeyDown",e)}function P(o){if(1===h.selectedDates.length&&(!o||o.classList.contains("flatpickr-day")&&!o.classList.contains("disabled"))){for(var r=o?o.dateObj.getTime():h.days.firstElementChild.dateObj.getTime(),l=h.parseDate(h.selectedDates[0],void 0,!0).getTime(),e=Math.min(r,h.selectedDates[0].getTime()),t=Math.max(r,h.selectedDates[0].getTime()),n=h.daysContainer.lastChild.lastChild.dateObj.getTime(),c=!1,d=0,s=0,a=e;a=a||(ln,s=window.pageYOffset+l.top+(d?-n-2:t.offsetHeight+2);if(fe(h.calendarContainer,"arrowTop",!d),fe(h.calendarContainer,"arrowBottom",d),!h.config.inline){var u=window.pageXOffset+l.left-(null!=r&&"center"===r?(a-l.width)/2:0),f=window.document.body.offsetWidth-l.right,m=u+a>window.document.body.offsetWidth;fe(h.calendarContainer,"rightMost",m),h.config.static||(h.calendarContainer.style.top=s+"px",m?(h.calendarContainer.style.left="auto",h.calendarContainer.style.right=f+"px"):(h.calendarContainer.style.left=u+"px",h.calendarContainer.style.right="auto"))}}}function W(){h.config.noCalendar||h.isMobile||(G(),M())}function R(){h._input.focus(),-1!==window.navigator.userAgent.indexOf("MSIE")||void 0!==navigator.msMaxTouchPoints?setTimeout(h.close,0):h.close()}function B(e){e.preventDefault(),e.stopPropagation();var t=function e(t,n){return n(t)?t:t.parentNode?e(t.parentNode,n):void 0}(e.target,function(e){return e.classList&&e.classList.contains("flatpickr-day")&&!e.classList.contains("disabled")&&!e.classList.contains("notAllowed")});if(void 0!==t){var n=t,a=h.latestSelectedDateObj=new Date(n.dateObj.getTime()),i=(a.getMonth()h.currentMonth+h.config.showMonths-1)&&"range"!==h.config.mode;if(h.selectedDateElem=n,"single"===h.config.mode)h.selectedDates=[a];else if("multiple"===h.config.mode){var o=z(a);o?h.selectedDates.splice(parseInt(o),1):h.selectedDates.push(a)}else"range"===h.config.mode&&(2===h.selectedDates.length&&h.clear(!1),h.selectedDates.push(a),0!==ce(a,h.selectedDates[0],!0)&&h.selectedDates.sort(function(e,t){return e.getTime()-t.getTime()}));if(m(),i){var r=h.currentYear!==a.getFullYear();h.currentYear=a.getFullYear(),h.currentMonth=a.getMonth(),r&&q("onYearChange"),q("onMonthChange")}if(G(),M(),Z(),h.config.enableTime&&setTimeout(function(){return h.showTimeInput=!0},50),i||"range"===h.config.mode||1!==h.config.showMonths?h.selectedDateElem&&h.selectedDateElem.focus():b(n),void 0!==h.hourElement&&setTimeout(function(){return void 0!==h.hourElement&&h.hourElement.select()},451),h.config.closeOnSelect){var l="single"===h.config.mode&&!h.config.enableTime,c="range"===h.config.mode&&2===h.selectedDates.length&&!h.config.enableTime;(l||c)&&R()}g()}}h.parseDate=le({config:h.config,l10n:h.l10n}),h._handlers=[],h._bind=o,h._setHoursFromDate=i,h._positionCalendar=L,h.changeMonth=I,h.changeYear=_,h.clear=function(e){void 0===e&&(e=!0);h.input.value="",void 0!==h.altInput&&(h.altInput.value="");void 0!==h.mobileInput&&(h.mobileInput.value="");h.selectedDates=[],h.latestSelectedDateObj=void 0,!(h.showTimeInput=!1)===h.config.enableTime&&a();h.redraw(),e&&q("onChange")},h.close=function(){h.isOpen=!1,h.isMobile||(h.calendarContainer.classList.remove("open"),h._input.classList.remove("active"));q("onClose")},h._createElement=me,h.destroy=function(){void 0!==h.config&&q("onDestroy");for(var e=h._handlers.length;e--;){var t=h._handlers[e];t.element.removeEventListener(t.event,t.handler,t.options)}if(h._handlers=[],h.mobileInput)h.mobileInput.parentNode&&h.mobileInput.parentNode.removeChild(h.mobileInput),h.mobileInput=void 0;else if(h.calendarContainer&&h.calendarContainer.parentNode)if(h.config.static&&h.calendarContainer.parentNode){var n=h.calendarContainer.parentNode;if(n.lastChild&&n.removeChild(n.lastChild),n.parentNode){for(;n.firstChild;)n.parentNode.insertBefore(n.firstChild,n);n.parentNode.removeChild(n)}}else h.calendarContainer.parentNode.removeChild(h.calendarContainer);h.altInput&&(h.input.type="text",h.altInput.parentNode&&h.altInput.parentNode.removeChild(h.altInput),delete h.altInput);h.input&&(h.input.type=h.input._type,h.input.classList.remove("flatpickr-input"),h.input.removeAttribute("readonly"),h.input.value="");["_showTimeInput","latestSelectedDateObj","_hideNextMonthArrow","_hidePrevMonthArrow","__hideNextMonthArrow","__hidePrevMonthArrow","isMobile","isOpen","selectedDateElem","minDateHasTime","maxDateHasTime","days","daysContainer","_input","_positionElement","innerContainer","rContainer","monthNav","todayDateElem","calendarContainer","weekdayContainer","prevMonthNav","nextMonthNav","currentMonthElement","currentYearElement","navigationCurrentMonth","selectedDateElem","config"].forEach(function(e){try{delete h[e]}catch(e){}})},h.isEnabled=N,h.jumpToDate=l,h.open=function(e,t){void 0===t&&(t=h._positionElement);if(!0===h.isMobile)return e&&(e.preventDefault(),e.target&&e.target.blur()),void 0!==h.mobileInput&&(h.mobileInput.focus(),h.mobileInput.click()),void q("onOpen");if(h._input.disabled||h.config.inline)return;var n=h.isOpen;h.isOpen=!0,n||(h.calendarContainer.classList.add("open"),h._input.classList.add("active"),q("onOpen"),L(t));!0===h.config.enableTime&&!0===h.config.noCalendar&&(0===h.selectedDates.length&&(h.setDate(void 0!==h.config.minDate?new Date(h.config.minDate.getTime()):new Date,!1),a(),Z()),!1!==h.config.allowInput||void 0!==e&&h.timeContainer.contains(e.relatedTarget)||setTimeout(function(){return h.hourElement.select()},50))},h.redraw=W,h.set=function(e,t){null!==e&&"object"==typeof e?Object.assign(h.config,e):(h.config[e]=t,void 0!==K[e]?K[e].forEach(function(e){return e()}):-1h.config.maxDate.getMonth():h.currentYear>h.config.maxDate.getFullYear()))}function V(t){return h.selectedDates.map(function(e){return h.formatDate(e,t)}).filter(function(e,t,n){return"range"!==h.config.mode||h.config.enableTime||n.indexOf(e)===t}).join("range"!==h.config.mode?h.config.conjunction:h.l10n.rangeSeparator)}function Z(e){if(void 0===e&&(e=!0),0===h.selectedDates.length)return h.clear(e);void 0!==h.mobileInput&&h.mobileFormatStr&&(h.mobileInput.value=void 0!==h.latestSelectedDateObj?h.formatDate(h.latestSelectedDateObj,h.mobileFormatStr):""),h.input.value=V(h.config.dateFormat),void 0!==h.altInput&&(h.altInput.value=V(h.config.altFormat)),!1!==e&&q("onValueUpdate")}function Q(e){e.preventDefault();var t=h.prevMonthNav.contains(e.target),n=h.nextMonthNav.contains(e.target);t||n?I(t?-1:1):0<=h.yearElements.indexOf(e.target)?e.target.select():e.target.classList.contains("arrowUp")?h.changeYear(h.currentYear+1):e.target.classList.contains("arrowDown")&&h.changeYear(h.currentYear-1)}return function(){h.element=h.input=d,h.isOpen=!1,function(){var e=["wrap","weekNumbers","allowInput","clickOpens","time_24hr","enableTime","noCalendar","altInput","shorthandCurrentMonth","inline","static","enableSeconds","disableMobile"],t=Object.assign({},s,JSON.parse(JSON.stringify(d.dataset||{}))),n={};h.config.parseDate=t.parseDate,h.config.formatDate=t.formatDate,Object.defineProperty(h.config,"enable",{get:function(){return h.config._enable},set:function(e){h.config._enable=U(e)}}),Object.defineProperty(h.config,"disable",{get:function(){return h.config._disable},set:function(e){h.config._disable=U(e)}});var a="time"===t.mode;t.dateFormat||!t.enableTime&&!a||(n.dateFormat=t.noCalendar||a?"H:i"+(t.enableSeconds?":S":""):ve.defaultConfig.dateFormat+" H:i"+(t.enableSeconds?":S":"")),t.altInput&&(t.enableTime||a)&&!t.altFormat&&(n.altFormat=t.noCalendar||a?"h:i"+(t.enableSeconds?":S K":" K"):ve.defaultConfig.altFormat+" h:i"+(t.enableSeconds?":S":"")+" K"),Object.defineProperty(h.config,"minDate",{get:function(){return h.config._minDate},set:Y("min")}),Object.defineProperty(h.config,"maxDate",{get:function(){return h.config._maxDate},set:Y("max")});var i=function(t){return function(e){h.config["min"===t?"_minTime":"_maxTime"]=h.parseDate(e,"H:i")}};Object.defineProperty(h.config,"minTime",{get:function(){return h.config._minTime},set:i("min")}),Object.defineProperty(h.config,"maxTime",{get:function(){return h.config._maxTime},set:i("max")}),"time"===t.mode&&(h.config.noCalendar=!0,h.config.enableTime=!0),Object.assign(h.config,n,t);for(var o=0;oh.now.getTime()?h.config.minDate:h.config.maxDate&&h.config.maxDate.getTime()li{position:relative}.fa-li{left:calc(var(--fa-li-width, 2em)*-1);position:absolute;text-align:center;width:var(--fa-li-width,2em);line-height:inherit}.fa-border{border-radius:var(--fa-border-radius,.1em);border:var(--fa-border-width,.08em) var(--fa-border-style,solid) var(--fa-border-color,#eee);padding:var(--fa-border-padding,.2em .25em .15em)}.fa-pull-left{float:left;margin-right:var(--fa-pull-margin,.3em)}.fa-pull-right{float:right;margin-left:var(--fa-pull-margin,.3em)}.fa-beat{-webkit-animation-name:fa-beat;animation-name:fa-beat;-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,ease-in-out);animation-timing-function:var(--fa-animation-timing,ease-in-out)}.fa-bounce{-webkit-animation-name:fa-bounce;animation-name:fa-bounce;-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,cubic-bezier(.28,.84,.42,1));animation-timing-function:var(--fa-animation-timing,cubic-bezier(.28,.84,.42,1))}.fa-fade{-webkit-animation-name:fa-fade;animation-name:fa-fade;-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1));animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1))}.fa-beat-fade,.fa-fade{-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s)}.fa-beat-fade{-webkit-animation-name:fa-beat-fade;animation-name:fa-beat-fade;-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1));animation-timing-function:var(--fa-animation-timing,cubic-bezier(.4,0,.6,1))}.fa-flip{-webkit-animation-name:fa-flip;animation-name:fa-flip;-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,ease-in-out);animation-timing-function:var(--fa-animation-timing,ease-in-out)}.fa-shake{-webkit-animation-name:fa-shake;animation-name:fa-shake;-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,linear);animation-timing-function:var(--fa-animation-timing,linear)}.fa-shake,.fa-spin{-webkit-animation-delay:var(--fa-animation-delay,0s);animation-delay:var(--fa-animation-delay,0s);-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal)}.fa-spin{-webkit-animation-name:fa-spin;animation-name:fa-spin;-webkit-animation-duration:var(--fa-animation-duration,2s);animation-duration:var(--fa-animation-duration,2s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,linear);animation-timing-function:var(--fa-animation-timing,linear)}.fa-spin-reverse{--fa-animation-direction:reverse}.fa-pulse,.fa-spin-pulse{-webkit-animation-name:fa-spin;animation-name:fa-spin;-webkit-animation-direction:var(--fa-animation-direction,normal);animation-direction:var(--fa-animation-direction,normal);-webkit-animation-duration:var(--fa-animation-duration,1s);animation-duration:var(--fa-animation-duration,1s);-webkit-animation-iteration-count:var(--fa-animation-iteration-count,infinite);animation-iteration-count:var(--fa-animation-iteration-count,infinite);-webkit-animation-timing-function:var(--fa-animation-timing,steps(8));animation-timing-function:var(--fa-animation-timing,steps(8))}@media (prefers-reduced-motion:reduce){.fa-beat,.fa-beat-fade,.fa-bounce,.fa-fade,.fa-flip,.fa-pulse,.fa-shake,.fa-spin,.fa-spin-pulse{-webkit-animation-delay:-1ms;animation-delay:-1ms;-webkit-animation-duration:1ms;animation-duration:1ms;-webkit-animation-iteration-count:1;animation-iteration-count:1;-webkit-transition-delay:0s;transition-delay:0s;-webkit-transition-duration:0s;transition-duration:0s}}@-webkit-keyframes fa-beat{0%,90%{-webkit-transform:scale(1);transform:scale(1)}45%{-webkit-transform:scale(var(--fa-beat-scale,1.25));transform:scale(var(--fa-beat-scale,1.25))}}@keyframes fa-beat{0%,90%{-webkit-transform:scale(1);transform:scale(1)}45%{-webkit-transform:scale(var(--fa-beat-scale,1.25));transform:scale(var(--fa-beat-scale,1.25))}}@-webkit-keyframes fa-bounce{0%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}10%{-webkit-transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0);transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0)}30%{-webkit-transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em));transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em))}50%{-webkit-transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0);transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0)}57%{-webkit-transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em));transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em))}64%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}to{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}}@keyframes fa-bounce{0%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}10%{-webkit-transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0);transform:scale(var(--fa-bounce-start-scale-x,1.1),var(--fa-bounce-start-scale-y,.9)) translateY(0)}30%{-webkit-transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em));transform:scale(var(--fa-bounce-jump-scale-x,.9),var(--fa-bounce-jump-scale-y,1.1)) translateY(var(--fa-bounce-height,-.5em))}50%{-webkit-transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0);transform:scale(var(--fa-bounce-land-scale-x,1.05),var(--fa-bounce-land-scale-y,.95)) translateY(0)}57%{-webkit-transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em));transform:scale(1) translateY(var(--fa-bounce-rebound,-.125em))}64%{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}to{-webkit-transform:scale(1) translateY(0);transform:scale(1) translateY(0)}}@-webkit-keyframes fa-fade{50%{opacity:var(--fa-fade-opacity,.4)}}@keyframes fa-fade{50%{opacity:var(--fa-fade-opacity,.4)}}@-webkit-keyframes fa-beat-fade{0%,to{opacity:var(--fa-beat-fade-opacity,.4);-webkit-transform:scale(1);transform:scale(1)}50%{opacity:1;-webkit-transform:scale(var(--fa-beat-fade-scale,1.125));transform:scale(var(--fa-beat-fade-scale,1.125))}}@keyframes fa-beat-fade{0%,to{opacity:var(--fa-beat-fade-opacity,.4);-webkit-transform:scale(1);transform:scale(1)}50%{opacity:1;-webkit-transform:scale(var(--fa-beat-fade-scale,1.125));transform:scale(var(--fa-beat-fade-scale,1.125))}}@-webkit-keyframes fa-flip{50%{-webkit-transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg));transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg))}}@keyframes fa-flip{50%{-webkit-transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg));transform:rotate3d(var(--fa-flip-x,0),var(--fa-flip-y,1),var(--fa-flip-z,0),var(--fa-flip-angle,-180deg))}}@-webkit-keyframes fa-shake{0%{-webkit-transform:rotate(-15deg);transform:rotate(-15deg)}4%{-webkit-transform:rotate(15deg);transform:rotate(15deg)}8%,24%{-webkit-transform:rotate(-18deg);transform:rotate(-18deg)}12%,28%{-webkit-transform:rotate(18deg);transform:rotate(18deg)}16%{-webkit-transform:rotate(-22deg);transform:rotate(-22deg)}20%{-webkit-transform:rotate(22deg);transform:rotate(22deg)}32%{-webkit-transform:rotate(-12deg);transform:rotate(-12deg)}36%{-webkit-transform:rotate(12deg);transform:rotate(12deg)}40%,to{-webkit-transform:rotate(0deg);transform:rotate(0deg)}}@keyframes fa-shake{0%{-webkit-transform:rotate(-15deg);transform:rotate(-15deg)}4%{-webkit-transform:rotate(15deg);transform:rotate(15deg)}8%,24%{-webkit-transform:rotate(-18deg);transform:rotate(-18deg)}12%,28%{-webkit-transform:rotate(18deg);transform:rotate(18deg)}16%{-webkit-transform:rotate(-22deg);transform:rotate(-22deg)}20%{-webkit-transform:rotate(22deg);transform:rotate(22deg)}32%{-webkit-transform:rotate(-12deg);transform:rotate(-12deg)}36%{-webkit-transform:rotate(12deg);transform:rotate(12deg)}40%,to{-webkit-transform:rotate(0deg);transform:rotate(0deg)}}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.fa-rotate-90{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-webkit-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-webkit-transform:scaleX(-1);transform:scaleX(-1)}.fa-flip-vertical{-webkit-transform:scaleY(-1);transform:scaleY(-1)}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical{-webkit-transform:scale(-1);transform:scale(-1)}.fa-rotate-by{-webkit-transform:rotate(var(--fa-rotate-angle,none));transform:rotate(var(--fa-rotate-angle,none))}.fa-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2.5em}.fa-stack-1x,.fa-stack-2x{left:0;position:absolute;text-align:center;width:100%;z-index:var(--fa-stack-z-index,auto)}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:var(--fa-inverse,#fff)} + +.fa-0:before{content:"\30"}.fa-1:before{content:"\31"}.fa-2:before{content:"\32"}.fa-3:before{content:"\33"}.fa-4:before{content:"\34"}.fa-5:before{content:"\35"}.fa-6:before{content:"\36"}.fa-7:before{content:"\37"}.fa-8:before{content:"\38"}.fa-9:before{content:"\39"}.fa-fill-drip:before{content:"\f576"}.fa-arrows-to-circle:before{content:"\e4bd"}.fa-chevron-circle-right:before,.fa-circle-chevron-right:before{content:"\f138"}.fa-at:before{content:"\40"}.fa-trash-alt:before,.fa-trash-can:before{content:"\f2ed"}.fa-text-height:before{content:"\f034"}.fa-user-times:before,.fa-user-xmark:before{content:"\f235"}.fa-stethoscope:before{content:"\f0f1"}.fa-comment-alt:before,.fa-message:before{content:"\f27a"}.fa-info:before{content:"\f129"}.fa-compress-alt:before,.fa-down-left-and-up-right-to-center:before{content:"\f422"}.fa-explosion:before{content:"\e4e9"}.fa-file-alt:before,.fa-file-lines:before,.fa-file-text:before{content:"\f15c"}.fa-wave-square:before{content:"\f83e"}.fa-ring:before{content:"\f70b"}.fa-building-un:before{content:"\e4d9"}.fa-dice-three:before{content:"\f527"}.fa-calendar-alt:before,.fa-calendar-days:before{content:"\f073"}.fa-anchor-circle-check:before{content:"\e4aa"}.fa-building-circle-arrow-right:before{content:"\e4d1"}.fa-volleyball-ball:before,.fa-volleyball:before{content:"\f45f"}.fa-arrows-up-to-line:before{content:"\e4c2"}.fa-sort-desc:before,.fa-sort-down:before{content:"\f0dd"}.fa-circle-minus:before,.fa-minus-circle:before{content:"\f056"}.fa-door-open:before{content:"\f52b"}.fa-right-from-bracket:before,.fa-sign-out-alt:before{content:"\f2f5"}.fa-atom:before{content:"\f5d2"}.fa-soap:before{content:"\e06e"}.fa-heart-music-camera-bolt:before,.fa-icons:before{content:"\f86d"}.fa-microphone-alt-slash:before,.fa-microphone-lines-slash:before{content:"\f539"}.fa-bridge-circle-check:before{content:"\e4c9"}.fa-pump-medical:before{content:"\e06a"}.fa-fingerprint:before{content:"\f577"}.fa-hand-point-right:before{content:"\f0a4"}.fa-magnifying-glass-location:before,.fa-search-location:before{content:"\f689"}.fa-forward-step:before,.fa-step-forward:before{content:"\f051"}.fa-face-smile-beam:before,.fa-smile-beam:before{content:"\f5b8"}.fa-flag-checkered:before{content:"\f11e"}.fa-football-ball:before,.fa-football:before{content:"\f44e"}.fa-school-circle-exclamation:before{content:"\e56c"}.fa-crop:before{content:"\f125"}.fa-angle-double-down:before,.fa-angles-down:before{content:"\f103"}.fa-users-rectangle:before{content:"\e594"}.fa-people-roof:before{content:"\e537"}.fa-people-line:before{content:"\e534"}.fa-beer-mug-empty:before,.fa-beer:before{content:"\f0fc"}.fa-diagram-predecessor:before{content:"\e477"}.fa-arrow-up-long:before,.fa-long-arrow-up:before{content:"\f176"}.fa-burn:before,.fa-fire-flame-simple:before{content:"\f46a"}.fa-male:before,.fa-person:before{content:"\f183"}.fa-laptop:before{content:"\f109"}.fa-file-csv:before{content:"\f6dd"}.fa-menorah:before{content:"\f676"}.fa-truck-plane:before{content:"\e58f"}.fa-record-vinyl:before{content:"\f8d9"}.fa-face-grin-stars:before,.fa-grin-stars:before{content:"\f587"}.fa-bong:before{content:"\f55c"}.fa-pastafarianism:before,.fa-spaghetti-monster-flying:before{content:"\f67b"}.fa-arrow-down-up-across-line:before{content:"\e4af"}.fa-spoon:before,.fa-utensil-spoon:before{content:"\f2e5"}.fa-jar-wheat:before{content:"\e517"}.fa-envelopes-bulk:before,.fa-mail-bulk:before{content:"\f674"}.fa-file-circle-exclamation:before{content:"\e4eb"}.fa-circle-h:before,.fa-hospital-symbol:before{content:"\f47e"}.fa-pager:before{content:"\f815"}.fa-address-book:before,.fa-contact-book:before{content:"\f2b9"}.fa-strikethrough:before{content:"\f0cc"}.fa-k:before{content:"\4b"}.fa-landmark-flag:before{content:"\e51c"}.fa-pencil-alt:before,.fa-pencil:before{content:"\f303"}.fa-backward:before{content:"\f04a"}.fa-caret-right:before{content:"\f0da"}.fa-comments:before{content:"\f086"}.fa-file-clipboard:before,.fa-paste:before{content:"\f0ea"}.fa-code-pull-request:before{content:"\e13c"}.fa-clipboard-list:before{content:"\f46d"}.fa-truck-loading:before,.fa-truck-ramp-box:before{content:"\f4de"}.fa-user-check:before{content:"\f4fc"}.fa-vial-virus:before{content:"\e597"}.fa-sheet-plastic:before{content:"\e571"}.fa-blog:before{content:"\f781"}.fa-user-ninja:before{content:"\f504"}.fa-person-arrow-up-from-line:before{content:"\e539"}.fa-scroll-torah:before,.fa-torah:before{content:"\f6a0"}.fa-broom-ball:before,.fa-quidditch-broom-ball:before,.fa-quidditch:before{content:"\f458"}.fa-toggle-off:before{content:"\f204"}.fa-archive:before,.fa-box-archive:before{content:"\f187"}.fa-person-drowning:before{content:"\e545"}.fa-arrow-down-9-1:before,.fa-sort-numeric-desc:before,.fa-sort-numeric-down-alt:before{content:"\f886"}.fa-face-grin-tongue-squint:before,.fa-grin-tongue-squint:before{content:"\f58a"}.fa-spray-can:before{content:"\f5bd"}.fa-truck-monster:before{content:"\f63b"}.fa-w:before{content:"\57"}.fa-earth-africa:before,.fa-globe-africa:before{content:"\f57c"}.fa-rainbow:before{content:"\f75b"}.fa-circle-notch:before{content:"\f1ce"}.fa-tablet-alt:before,.fa-tablet-screen-button:before{content:"\f3fa"}.fa-paw:before{content:"\f1b0"}.fa-cloud:before{content:"\f0c2"}.fa-trowel-bricks:before{content:"\e58a"}.fa-face-flushed:before,.fa-flushed:before{content:"\f579"}.fa-hospital-user:before{content:"\f80d"}.fa-tent-arrow-left-right:before{content:"\e57f"}.fa-gavel:before,.fa-legal:before{content:"\f0e3"}.fa-binoculars:before{content:"\f1e5"}.fa-microphone-slash:before{content:"\f131"}.fa-box-tissue:before{content:"\e05b"}.fa-motorcycle:before{content:"\f21c"}.fa-bell-concierge:before,.fa-concierge-bell:before{content:"\f562"}.fa-pen-ruler:before,.fa-pencil-ruler:before{content:"\f5ae"}.fa-people-arrows-left-right:before,.fa-people-arrows:before{content:"\e068"}.fa-mars-and-venus-burst:before{content:"\e523"}.fa-caret-square-right:before,.fa-square-caret-right:before{content:"\f152"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-sun-plant-wilt:before{content:"\e57a"}.fa-toilets-portable:before{content:"\e584"}.fa-hockey-puck:before{content:"\f453"}.fa-table:before{content:"\f0ce"}.fa-magnifying-glass-arrow-right:before{content:"\e521"}.fa-digital-tachograph:before,.fa-tachograph-digital:before{content:"\f566"}.fa-users-slash:before{content:"\e073"}.fa-clover:before{content:"\e139"}.fa-mail-reply:before,.fa-reply:before{content:"\f3e5"}.fa-star-and-crescent:before{content:"\f699"}.fa-house-fire:before{content:"\e50c"}.fa-minus-square:before,.fa-square-minus:before{content:"\f146"}.fa-helicopter:before{content:"\f533"}.fa-compass:before{content:"\f14e"}.fa-caret-square-down:before,.fa-square-caret-down:before{content:"\f150"}.fa-file-circle-question:before{content:"\e4ef"}.fa-laptop-code:before{content:"\f5fc"}.fa-swatchbook:before{content:"\f5c3"}.fa-prescription-bottle:before{content:"\f485"}.fa-bars:before,.fa-navicon:before{content:"\f0c9"}.fa-people-group:before{content:"\e533"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-heart-broken:before,.fa-heart-crack:before{content:"\f7a9"}.fa-external-link-square-alt:before,.fa-square-up-right:before{content:"\f360"}.fa-face-kiss-beam:before,.fa-kiss-beam:before{content:"\f597"}.fa-film:before{content:"\f008"}.fa-ruler-horizontal:before{content:"\f547"}.fa-people-robbery:before{content:"\e536"}.fa-lightbulb:before{content:"\f0eb"}.fa-caret-left:before{content:"\f0d9"}.fa-circle-exclamation:before,.fa-exclamation-circle:before{content:"\f06a"}.fa-school-circle-xmark:before{content:"\e56d"}.fa-arrow-right-from-bracket:before,.fa-sign-out:before{content:"\f08b"}.fa-chevron-circle-down:before,.fa-circle-chevron-down:before{content:"\f13a"}.fa-unlock-alt:before,.fa-unlock-keyhole:before{content:"\f13e"}.fa-cloud-showers-heavy:before{content:"\f740"}.fa-headphones-alt:before,.fa-headphones-simple:before{content:"\f58f"}.fa-sitemap:before{content:"\f0e8"}.fa-circle-dollar-to-slot:before,.fa-donate:before{content:"\f4b9"}.fa-memory:before{content:"\f538"}.fa-road-spikes:before{content:"\e568"}.fa-fire-burner:before{content:"\e4f1"}.fa-flag:before{content:"\f024"}.fa-hanukiah:before{content:"\f6e6"}.fa-feather:before{content:"\f52d"}.fa-volume-down:before,.fa-volume-low:before{content:"\f027"}.fa-comment-slash:before{content:"\f4b3"}.fa-cloud-sun-rain:before{content:"\f743"}.fa-compress:before{content:"\f066"}.fa-wheat-alt:before,.fa-wheat-awn:before{content:"\e2cd"}.fa-ankh:before{content:"\f644"}.fa-hands-holding-child:before{content:"\e4fa"}.fa-asterisk:before{content:"\2a"}.fa-check-square:before,.fa-square-check:before{content:"\f14a"}.fa-peseta-sign:before{content:"\e221"}.fa-header:before,.fa-heading:before{content:"\f1dc"}.fa-ghost:before{content:"\f6e2"}.fa-list-squares:before,.fa-list:before{content:"\f03a"}.fa-phone-square-alt:before,.fa-square-phone-flip:before{content:"\f87b"}.fa-cart-plus:before{content:"\f217"}.fa-gamepad:before{content:"\f11b"}.fa-circle-dot:before,.fa-dot-circle:before{content:"\f192"}.fa-dizzy:before,.fa-face-dizzy:before{content:"\f567"}.fa-egg:before{content:"\f7fb"}.fa-house-medical-circle-xmark:before{content:"\e513"}.fa-campground:before{content:"\f6bb"}.fa-folder-plus:before{content:"\f65e"}.fa-futbol-ball:before,.fa-futbol:before,.fa-soccer-ball:before{content:"\f1e3"}.fa-paint-brush:before,.fa-paintbrush:before{content:"\f1fc"}.fa-lock:before{content:"\f023"}.fa-gas-pump:before{content:"\f52f"}.fa-hot-tub-person:before,.fa-hot-tub:before{content:"\f593"}.fa-map-location:before,.fa-map-marked:before{content:"\f59f"}.fa-house-flood-water:before{content:"\e50e"}.fa-tree:before{content:"\f1bb"}.fa-bridge-lock:before{content:"\e4cc"}.fa-sack-dollar:before{content:"\f81d"}.fa-edit:before,.fa-pen-to-square:before{content:"\f044"}.fa-car-side:before{content:"\f5e4"}.fa-share-alt:before,.fa-share-nodes:before{content:"\f1e0"}.fa-heart-circle-minus:before{content:"\e4ff"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-microscope:before{content:"\f610"}.fa-sink:before{content:"\e06d"}.fa-bag-shopping:before,.fa-shopping-bag:before{content:"\f290"}.fa-arrow-down-z-a:before,.fa-sort-alpha-desc:before,.fa-sort-alpha-down-alt:before{content:"\f881"}.fa-mitten:before{content:"\f7b5"}.fa-person-rays:before{content:"\e54d"}.fa-users:before{content:"\f0c0"}.fa-eye-slash:before{content:"\f070"}.fa-flask-vial:before{content:"\e4f3"}.fa-hand-paper:before,.fa-hand:before{content:"\f256"}.fa-om:before{content:"\f679"}.fa-worm:before{content:"\e599"}.fa-house-circle-xmark:before{content:"\e50b"}.fa-plug:before{content:"\f1e6"}.fa-chevron-up:before{content:"\f077"}.fa-hand-spock:before{content:"\f259"}.fa-stopwatch:before{content:"\f2f2"}.fa-face-kiss:before,.fa-kiss:before{content:"\f596"}.fa-bridge-circle-xmark:before{content:"\e4cb"}.fa-face-grin-tongue:before,.fa-grin-tongue:before{content:"\f589"}.fa-chess-bishop:before{content:"\f43a"}.fa-face-grin-wink:before,.fa-grin-wink:before{content:"\f58c"}.fa-deaf:before,.fa-deafness:before,.fa-ear-deaf:before,.fa-hard-of-hearing:before{content:"\f2a4"}.fa-road-circle-check:before{content:"\e564"}.fa-dice-five:before{content:"\f523"}.fa-rss-square:before,.fa-square-rss:before{content:"\f143"}.fa-land-mine-on:before{content:"\e51b"}.fa-i-cursor:before{content:"\f246"}.fa-stamp:before{content:"\f5bf"}.fa-stairs:before{content:"\e289"}.fa-i:before{content:"\49"}.fa-hryvnia-sign:before,.fa-hryvnia:before{content:"\f6f2"}.fa-pills:before{content:"\f484"}.fa-face-grin-wide:before,.fa-grin-alt:before{content:"\f581"}.fa-tooth:before{content:"\f5c9"}.fa-v:before{content:"\56"}.fa-bangladeshi-taka-sign:before{content:"\e2e6"}.fa-bicycle:before{content:"\f206"}.fa-rod-asclepius:before,.fa-rod-snake:before,.fa-staff-aesculapius:before,.fa-staff-snake:before{content:"\e579"}.fa-head-side-cough-slash:before{content:"\e062"}.fa-ambulance:before,.fa-truck-medical:before{content:"\f0f9"}.fa-wheat-awn-circle-exclamation:before{content:"\e598"}.fa-snowman:before{content:"\f7d0"}.fa-mortar-pestle:before{content:"\f5a7"}.fa-road-barrier:before{content:"\e562"}.fa-school:before{content:"\f549"}.fa-igloo:before{content:"\f7ae"}.fa-joint:before{content:"\f595"}.fa-angle-right:before{content:"\f105"}.fa-horse:before{content:"\f6f0"}.fa-q:before{content:"\51"}.fa-g:before{content:"\47"}.fa-notes-medical:before{content:"\f481"}.fa-temperature-2:before,.fa-temperature-half:before,.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-dong-sign:before{content:"\e169"}.fa-capsules:before{content:"\f46b"}.fa-poo-bolt:before,.fa-poo-storm:before{content:"\f75a"}.fa-face-frown-open:before,.fa-frown-open:before{content:"\f57a"}.fa-hand-point-up:before{content:"\f0a6"}.fa-money-bill:before{content:"\f0d6"}.fa-bookmark:before{content:"\f02e"}.fa-align-justify:before{content:"\f039"}.fa-umbrella-beach:before{content:"\f5ca"}.fa-helmet-un:before{content:"\e503"}.fa-bullseye:before{content:"\f140"}.fa-bacon:before{content:"\f7e5"}.fa-hand-point-down:before{content:"\f0a7"}.fa-arrow-up-from-bracket:before{content:"\e09a"}.fa-folder-blank:before,.fa-folder:before{content:"\f07b"}.fa-file-medical-alt:before,.fa-file-waveform:before{content:"\f478"}.fa-radiation:before{content:"\f7b9"}.fa-chart-simple:before{content:"\e473"}.fa-mars-stroke:before{content:"\f229"}.fa-vial:before{content:"\f492"}.fa-dashboard:before,.fa-gauge-med:before,.fa-gauge:before,.fa-tachometer-alt-average:before{content:"\f624"}.fa-magic-wand-sparkles:before,.fa-wand-magic-sparkles:before{content:"\e2ca"}.fa-e:before{content:"\45"}.fa-pen-alt:before,.fa-pen-clip:before{content:"\f305"}.fa-bridge-circle-exclamation:before{content:"\e4ca"}.fa-user:before{content:"\f007"}.fa-school-circle-check:before{content:"\e56b"}.fa-dumpster:before{content:"\f793"}.fa-shuttle-van:before,.fa-van-shuttle:before{content:"\f5b6"}.fa-building-user:before{content:"\e4da"}.fa-caret-square-left:before,.fa-square-caret-left:before{content:"\f191"}.fa-highlighter:before{content:"\f591"}.fa-key:before{content:"\f084"}.fa-bullhorn:before{content:"\f0a1"}.fa-globe:before{content:"\f0ac"}.fa-synagogue:before{content:"\f69b"}.fa-person-half-dress:before{content:"\e548"}.fa-road-bridge:before{content:"\e563"}.fa-location-arrow:before{content:"\f124"}.fa-c:before{content:"\43"}.fa-tablet-button:before{content:"\f10a"}.fa-building-lock:before{content:"\e4d6"}.fa-pizza-slice:before{content:"\f818"}.fa-money-bill-wave:before{content:"\f53a"}.fa-area-chart:before,.fa-chart-area:before{content:"\f1fe"}.fa-house-flag:before{content:"\e50d"}.fa-person-circle-minus:before{content:"\e540"}.fa-ban:before,.fa-cancel:before{content:"\f05e"}.fa-camera-rotate:before{content:"\e0d8"}.fa-air-freshener:before,.fa-spray-can-sparkles:before{content:"\f5d0"}.fa-star:before{content:"\f005"}.fa-repeat:before{content:"\f363"}.fa-cross:before{content:"\f654"}.fa-box:before{content:"\f466"}.fa-venus-mars:before{content:"\f228"}.fa-arrow-pointer:before,.fa-mouse-pointer:before{content:"\f245"}.fa-expand-arrows-alt:before,.fa-maximize:before{content:"\f31e"}.fa-charging-station:before{content:"\f5e7"}.fa-shapes:before,.fa-triangle-circle-square:before{content:"\f61f"}.fa-random:before,.fa-shuffle:before{content:"\f074"}.fa-person-running:before,.fa-running:before{content:"\f70c"}.fa-mobile-retro:before{content:"\e527"}.fa-grip-lines-vertical:before{content:"\f7a5"}.fa-spider:before{content:"\f717"}.fa-hands-bound:before{content:"\e4f9"}.fa-file-invoice-dollar:before{content:"\f571"}.fa-plane-circle-exclamation:before{content:"\e556"}.fa-x-ray:before{content:"\f497"}.fa-spell-check:before{content:"\f891"}.fa-slash:before{content:"\f715"}.fa-computer-mouse:before,.fa-mouse:before{content:"\f8cc"}.fa-arrow-right-to-bracket:before,.fa-sign-in:before{content:"\f090"}.fa-shop-slash:before,.fa-store-alt-slash:before{content:"\e070"}.fa-server:before{content:"\f233"}.fa-virus-covid-slash:before{content:"\e4a9"}.fa-shop-lock:before{content:"\e4a5"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-blender-phone:before{content:"\f6b6"}.fa-building-wheat:before{content:"\e4db"}.fa-person-breastfeeding:before{content:"\e53a"}.fa-right-to-bracket:before,.fa-sign-in-alt:before{content:"\f2f6"}.fa-venus:before{content:"\f221"}.fa-passport:before{content:"\f5ab"}.fa-heart-pulse:before,.fa-heartbeat:before{content:"\f21e"}.fa-people-carry-box:before,.fa-people-carry:before{content:"\f4ce"}.fa-temperature-high:before{content:"\f769"}.fa-microchip:before{content:"\f2db"}.fa-crown:before{content:"\f521"}.fa-weight-hanging:before{content:"\f5cd"}.fa-xmarks-lines:before{content:"\e59a"}.fa-file-prescription:before{content:"\f572"}.fa-weight-scale:before,.fa-weight:before{content:"\f496"}.fa-user-friends:before,.fa-user-group:before{content:"\f500"}.fa-arrow-up-a-z:before,.fa-sort-alpha-up:before{content:"\f15e"}.fa-chess-knight:before{content:"\f441"}.fa-face-laugh-squint:before,.fa-laugh-squint:before{content:"\f59b"}.fa-wheelchair:before{content:"\f193"}.fa-arrow-circle-up:before,.fa-circle-arrow-up:before{content:"\f0aa"}.fa-toggle-on:before{content:"\f205"}.fa-person-walking:before,.fa-walking:before{content:"\f554"}.fa-l:before{content:"\4c"}.fa-fire:before{content:"\f06d"}.fa-bed-pulse:before,.fa-procedures:before{content:"\f487"}.fa-shuttle-space:before,.fa-space-shuttle:before{content:"\f197"}.fa-face-laugh:before,.fa-laugh:before{content:"\f599"}.fa-folder-open:before{content:"\f07c"}.fa-heart-circle-plus:before{content:"\e500"}.fa-code-fork:before{content:"\e13b"}.fa-city:before{content:"\f64f"}.fa-microphone-alt:before,.fa-microphone-lines:before{content:"\f3c9"}.fa-pepper-hot:before{content:"\f816"}.fa-unlock:before{content:"\f09c"}.fa-colon-sign:before{content:"\e140"}.fa-headset:before{content:"\f590"}.fa-store-slash:before{content:"\e071"}.fa-road-circle-xmark:before{content:"\e566"}.fa-user-minus:before{content:"\f503"}.fa-mars-stroke-up:before,.fa-mars-stroke-v:before{content:"\f22a"}.fa-champagne-glasses:before,.fa-glass-cheers:before{content:"\f79f"}.fa-clipboard:before{content:"\f328"}.fa-house-circle-exclamation:before{content:"\e50a"}.fa-file-arrow-up:before,.fa-file-upload:before{content:"\f574"}.fa-wifi-3:before,.fa-wifi-strong:before,.fa-wifi:before{content:"\f1eb"}.fa-bath:before,.fa-bathtub:before{content:"\f2cd"}.fa-underline:before{content:"\f0cd"}.fa-user-edit:before,.fa-user-pen:before{content:"\f4ff"}.fa-signature:before{content:"\f5b7"}.fa-stroopwafel:before{content:"\f551"}.fa-bold:before{content:"\f032"}.fa-anchor-lock:before{content:"\e4ad"}.fa-building-ngo:before{content:"\e4d7"}.fa-manat-sign:before{content:"\e1d5"}.fa-not-equal:before{content:"\f53e"}.fa-border-style:before,.fa-border-top-left:before{content:"\f853"}.fa-map-location-dot:before,.fa-map-marked-alt:before{content:"\f5a0"}.fa-jedi:before{content:"\f669"}.fa-poll:before,.fa-square-poll-vertical:before{content:"\f681"}.fa-mug-hot:before{content:"\f7b6"}.fa-battery-car:before,.fa-car-battery:before{content:"\f5df"}.fa-gift:before{content:"\f06b"}.fa-dice-two:before{content:"\f528"}.fa-chess-queen:before{content:"\f445"}.fa-glasses:before{content:"\f530"}.fa-chess-board:before{content:"\f43c"}.fa-building-circle-check:before{content:"\e4d2"}.fa-person-chalkboard:before{content:"\e53d"}.fa-mars-stroke-h:before,.fa-mars-stroke-right:before{content:"\f22b"}.fa-hand-back-fist:before,.fa-hand-rock:before{content:"\f255"}.fa-caret-square-up:before,.fa-square-caret-up:before{content:"\f151"}.fa-cloud-showers-water:before{content:"\e4e4"}.fa-bar-chart:before,.fa-chart-bar:before{content:"\f080"}.fa-hands-bubbles:before,.fa-hands-wash:before{content:"\e05e"}.fa-less-than-equal:before{content:"\f537"}.fa-train:before{content:"\f238"}.fa-eye-low-vision:before,.fa-low-vision:before{content:"\f2a8"}.fa-crow:before{content:"\f520"}.fa-sailboat:before{content:"\e445"}.fa-window-restore:before{content:"\f2d2"}.fa-plus-square:before,.fa-square-plus:before{content:"\f0fe"}.fa-torii-gate:before{content:"\f6a1"}.fa-frog:before{content:"\f52e"}.fa-bucket:before{content:"\e4cf"}.fa-image:before{content:"\f03e"}.fa-microphone:before{content:"\f130"}.fa-cow:before{content:"\f6c8"}.fa-caret-up:before{content:"\f0d8"}.fa-screwdriver:before{content:"\f54a"}.fa-folder-closed:before{content:"\e185"}.fa-house-tsunami:before{content:"\e515"}.fa-square-nfi:before{content:"\e576"}.fa-arrow-up-from-ground-water:before{content:"\e4b5"}.fa-glass-martini-alt:before,.fa-martini-glass:before{content:"\f57b"}.fa-rotate-back:before,.fa-rotate-backward:before,.fa-rotate-left:before,.fa-undo-alt:before{content:"\f2ea"}.fa-columns:before,.fa-table-columns:before{content:"\f0db"}.fa-lemon:before{content:"\f094"}.fa-head-side-mask:before{content:"\e063"}.fa-handshake:before{content:"\f2b5"}.fa-gem:before{content:"\f3a5"}.fa-dolly-box:before,.fa-dolly:before{content:"\f472"}.fa-smoking:before{content:"\f48d"}.fa-compress-arrows-alt:before,.fa-minimize:before{content:"\f78c"}.fa-monument:before{content:"\f5a6"}.fa-snowplow:before{content:"\f7d2"}.fa-angle-double-right:before,.fa-angles-right:before{content:"\f101"}.fa-cannabis:before{content:"\f55f"}.fa-circle-play:before,.fa-play-circle:before{content:"\f144"}.fa-tablets:before{content:"\f490"}.fa-ethernet:before{content:"\f796"}.fa-eur:before,.fa-euro-sign:before,.fa-euro:before{content:"\f153"}.fa-chair:before{content:"\f6c0"}.fa-check-circle:before,.fa-circle-check:before{content:"\f058"}.fa-circle-stop:before,.fa-stop-circle:before{content:"\f28d"}.fa-compass-drafting:before,.fa-drafting-compass:before{content:"\f568"}.fa-plate-wheat:before{content:"\e55a"}.fa-icicles:before{content:"\f7ad"}.fa-person-shelter:before{content:"\e54f"}.fa-neuter:before{content:"\f22c"}.fa-id-badge:before{content:"\f2c1"}.fa-marker:before{content:"\f5a1"}.fa-face-laugh-beam:before,.fa-laugh-beam:before{content:"\f59a"}.fa-helicopter-symbol:before{content:"\e502"}.fa-universal-access:before{content:"\f29a"}.fa-chevron-circle-up:before,.fa-circle-chevron-up:before{content:"\f139"}.fa-lari-sign:before{content:"\e1c8"}.fa-volcano:before{content:"\f770"}.fa-person-walking-dashed-line-arrow-right:before{content:"\e553"}.fa-gbp:before,.fa-pound-sign:before,.fa-sterling-sign:before{content:"\f154"}.fa-viruses:before{content:"\e076"}.fa-square-person-confined:before{content:"\e577"}.fa-user-tie:before{content:"\f508"}.fa-arrow-down-long:before,.fa-long-arrow-down:before{content:"\f175"}.fa-tent-arrow-down-to-line:before{content:"\e57e"}.fa-certificate:before{content:"\f0a3"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-suitcase:before{content:"\f0f2"}.fa-person-skating:before,.fa-skating:before{content:"\f7c5"}.fa-filter-circle-dollar:before,.fa-funnel-dollar:before{content:"\f662"}.fa-camera-retro:before{content:"\f083"}.fa-arrow-circle-down:before,.fa-circle-arrow-down:before{content:"\f0ab"}.fa-arrow-right-to-file:before,.fa-file-import:before{content:"\f56f"}.fa-external-link-square:before,.fa-square-arrow-up-right:before{content:"\f14c"}.fa-box-open:before{content:"\f49e"}.fa-scroll:before{content:"\f70e"}.fa-spa:before{content:"\f5bb"}.fa-location-pin-lock:before{content:"\e51f"}.fa-pause:before{content:"\f04c"}.fa-hill-avalanche:before{content:"\e507"}.fa-temperature-0:before,.fa-temperature-empty:before,.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-bomb:before{content:"\f1e2"}.fa-registered:before{content:"\f25d"}.fa-address-card:before,.fa-contact-card:before,.fa-vcard:before{content:"\f2bb"}.fa-balance-scale-right:before,.fa-scale-unbalanced-flip:before{content:"\f516"}.fa-subscript:before{content:"\f12c"}.fa-diamond-turn-right:before,.fa-directions:before{content:"\f5eb"}.fa-burst:before{content:"\e4dc"}.fa-house-laptop:before,.fa-laptop-house:before{content:"\e066"}.fa-face-tired:before,.fa-tired:before{content:"\f5c8"}.fa-money-bills:before{content:"\e1f3"}.fa-smog:before{content:"\f75f"}.fa-crutch:before{content:"\f7f7"}.fa-cloud-arrow-up:before,.fa-cloud-upload-alt:before,.fa-cloud-upload:before{content:"\f0ee"}.fa-palette:before{content:"\f53f"}.fa-arrows-turn-right:before{content:"\e4c0"}.fa-vest:before{content:"\e085"}.fa-ferry:before{content:"\e4ea"}.fa-arrows-down-to-people:before{content:"\e4b9"}.fa-seedling:before,.fa-sprout:before{content:"\f4d8"}.fa-arrows-alt-h:before,.fa-left-right:before{content:"\f337"}.fa-boxes-packing:before{content:"\e4c7"}.fa-arrow-circle-left:before,.fa-circle-arrow-left:before{content:"\f0a8"}.fa-group-arrows-rotate:before{content:"\e4f6"}.fa-bowl-food:before{content:"\e4c6"}.fa-candy-cane:before{content:"\f786"}.fa-arrow-down-wide-short:before,.fa-sort-amount-asc:before,.fa-sort-amount-down:before{content:"\f160"}.fa-cloud-bolt:before,.fa-thunderstorm:before{content:"\f76c"}.fa-remove-format:before,.fa-text-slash:before{content:"\f87d"}.fa-face-smile-wink:before,.fa-smile-wink:before{content:"\f4da"}.fa-file-word:before{content:"\f1c2"}.fa-file-powerpoint:before{content:"\f1c4"}.fa-arrows-h:before,.fa-arrows-left-right:before{content:"\f07e"}.fa-house-lock:before{content:"\e510"}.fa-cloud-arrow-down:before,.fa-cloud-download-alt:before,.fa-cloud-download:before{content:"\f0ed"}.fa-children:before{content:"\e4e1"}.fa-blackboard:before,.fa-chalkboard:before{content:"\f51b"}.fa-user-alt-slash:before,.fa-user-large-slash:before{content:"\f4fa"}.fa-envelope-open:before{content:"\f2b6"}.fa-handshake-alt-slash:before,.fa-handshake-simple-slash:before{content:"\e05f"}.fa-mattress-pillow:before{content:"\e525"}.fa-guarani-sign:before{content:"\e19a"}.fa-arrows-rotate:before,.fa-refresh:before,.fa-sync:before{content:"\f021"}.fa-fire-extinguisher:before{content:"\f134"}.fa-cruzeiro-sign:before{content:"\e152"}.fa-greater-than-equal:before{content:"\f532"}.fa-shield-alt:before,.fa-shield-halved:before{content:"\f3ed"}.fa-atlas:before,.fa-book-atlas:before{content:"\f558"}.fa-virus:before{content:"\e074"}.fa-envelope-circle-check:before{content:"\e4e8"}.fa-layer-group:before{content:"\f5fd"}.fa-arrows-to-dot:before{content:"\e4be"}.fa-archway:before{content:"\f557"}.fa-heart-circle-check:before{content:"\e4fd"}.fa-house-chimney-crack:before,.fa-house-damage:before{content:"\f6f1"}.fa-file-archive:before,.fa-file-zipper:before{content:"\f1c6"}.fa-square:before{content:"\f0c8"}.fa-glass-martini:before,.fa-martini-glass-empty:before{content:"\f000"}.fa-couch:before{content:"\f4b8"}.fa-cedi-sign:before{content:"\e0df"}.fa-italic:before{content:"\f033"}.fa-church:before{content:"\f51d"}.fa-comments-dollar:before{content:"\f653"}.fa-democrat:before{content:"\f747"}.fa-z:before{content:"\5a"}.fa-person-skiing:before,.fa-skiing:before{content:"\f7c9"}.fa-road-lock:before{content:"\e567"}.fa-a:before{content:"\41"}.fa-temperature-arrow-down:before,.fa-temperature-down:before{content:"\e03f"}.fa-feather-alt:before,.fa-feather-pointed:before{content:"\f56b"}.fa-p:before{content:"\50"}.fa-snowflake:before{content:"\f2dc"}.fa-newspaper:before{content:"\f1ea"}.fa-ad:before,.fa-rectangle-ad:before{content:"\f641"}.fa-arrow-circle-right:before,.fa-circle-arrow-right:before{content:"\f0a9"}.fa-filter-circle-xmark:before{content:"\e17b"}.fa-locust:before{content:"\e520"}.fa-sort:before,.fa-unsorted:before{content:"\f0dc"}.fa-list-1-2:before,.fa-list-numeric:before,.fa-list-ol:before{content:"\f0cb"}.fa-person-dress-burst:before{content:"\e544"}.fa-money-check-alt:before,.fa-money-check-dollar:before{content:"\f53d"}.fa-vector-square:before{content:"\f5cb"}.fa-bread-slice:before{content:"\f7ec"}.fa-language:before{content:"\f1ab"}.fa-face-kiss-wink-heart:before,.fa-kiss-wink-heart:before{content:"\f598"}.fa-filter:before{content:"\f0b0"}.fa-question:before{content:"\3f"}.fa-file-signature:before{content:"\f573"}.fa-arrows-alt:before,.fa-up-down-left-right:before{content:"\f0b2"}.fa-house-chimney-user:before{content:"\e065"}.fa-hand-holding-heart:before{content:"\f4be"}.fa-puzzle-piece:before{content:"\f12e"}.fa-money-check:before{content:"\f53c"}.fa-star-half-alt:before,.fa-star-half-stroke:before{content:"\f5c0"}.fa-code:before{content:"\f121"}.fa-glass-whiskey:before,.fa-whiskey-glass:before{content:"\f7a0"}.fa-building-circle-exclamation:before{content:"\e4d3"}.fa-magnifying-glass-chart:before{content:"\e522"}.fa-arrow-up-right-from-square:before,.fa-external-link:before{content:"\f08e"}.fa-cubes-stacked:before{content:"\e4e6"}.fa-krw:before,.fa-won-sign:before,.fa-won:before{content:"\f159"}.fa-virus-covid:before{content:"\e4a8"}.fa-austral-sign:before{content:"\e0a9"}.fa-f:before{content:"\46"}.fa-leaf:before{content:"\f06c"}.fa-road:before{content:"\f018"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-person-circle-plus:before{content:"\e541"}.fa-chart-pie:before,.fa-pie-chart:before{content:"\f200"}.fa-bolt-lightning:before{content:"\e0b7"}.fa-sack-xmark:before{content:"\e56a"}.fa-file-excel:before{content:"\f1c3"}.fa-file-contract:before{content:"\f56c"}.fa-fish-fins:before{content:"\e4f2"}.fa-building-flag:before{content:"\e4d5"}.fa-face-grin-beam:before,.fa-grin-beam:before{content:"\f582"}.fa-object-ungroup:before{content:"\f248"}.fa-poop:before{content:"\f619"}.fa-location-pin:before,.fa-map-marker:before{content:"\f041"}.fa-kaaba:before{content:"\f66b"}.fa-toilet-paper:before{content:"\f71e"}.fa-hard-hat:before,.fa-hat-hard:before,.fa-helmet-safety:before{content:"\f807"}.fa-eject:before{content:"\f052"}.fa-arrow-alt-circle-right:before,.fa-circle-right:before{content:"\f35a"}.fa-plane-circle-check:before{content:"\e555"}.fa-face-rolling-eyes:before,.fa-meh-rolling-eyes:before{content:"\f5a5"}.fa-object-group:before{content:"\f247"}.fa-chart-line:before,.fa-line-chart:before{content:"\f201"}.fa-mask-ventilator:before{content:"\e524"}.fa-arrow-right:before{content:"\f061"}.fa-map-signs:before,.fa-signs-post:before{content:"\f277"}.fa-cash-register:before{content:"\f788"}.fa-person-circle-question:before{content:"\e542"}.fa-h:before{content:"\48"}.fa-tarp:before{content:"\e57b"}.fa-screwdriver-wrench:before,.fa-tools:before{content:"\f7d9"}.fa-arrows-to-eye:before{content:"\e4bf"}.fa-plug-circle-bolt:before{content:"\e55b"}.fa-heart:before{content:"\f004"}.fa-mars-and-venus:before{content:"\f224"}.fa-home-user:before,.fa-house-user:before{content:"\e1b0"}.fa-dumpster-fire:before{content:"\f794"}.fa-house-crack:before{content:"\e3b1"}.fa-cocktail:before,.fa-martini-glass-citrus:before{content:"\f561"}.fa-face-surprise:before,.fa-surprise:before{content:"\f5c2"}.fa-bottle-water:before{content:"\e4c5"}.fa-circle-pause:before,.fa-pause-circle:before{content:"\f28b"}.fa-toilet-paper-slash:before{content:"\e072"}.fa-apple-alt:before,.fa-apple-whole:before{content:"\f5d1"}.fa-kitchen-set:before{content:"\e51a"}.fa-r:before{content:"\52"}.fa-temperature-1:before,.fa-temperature-quarter:before,.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-cube:before{content:"\f1b2"}.fa-bitcoin-sign:before{content:"\e0b4"}.fa-shield-dog:before{content:"\e573"}.fa-solar-panel:before{content:"\f5ba"}.fa-lock-open:before{content:"\f3c1"}.fa-elevator:before{content:"\e16d"}.fa-money-bill-transfer:before{content:"\e528"}.fa-money-bill-trend-up:before{content:"\e529"}.fa-house-flood-water-circle-arrow-right:before{content:"\e50f"}.fa-poll-h:before,.fa-square-poll-horizontal:before{content:"\f682"}.fa-circle:before{content:"\f111"}.fa-backward-fast:before,.fa-fast-backward:before{content:"\f049"}.fa-recycle:before{content:"\f1b8"}.fa-user-astronaut:before{content:"\f4fb"}.fa-plane-slash:before{content:"\e069"}.fa-trademark:before{content:"\f25c"}.fa-basketball-ball:before,.fa-basketball:before{content:"\f434"}.fa-satellite-dish:before{content:"\f7c0"}.fa-arrow-alt-circle-up:before,.fa-circle-up:before{content:"\f35b"}.fa-mobile-alt:before,.fa-mobile-screen-button:before{content:"\f3cd"}.fa-volume-high:before,.fa-volume-up:before{content:"\f028"}.fa-users-rays:before{content:"\e593"}.fa-wallet:before{content:"\f555"}.fa-clipboard-check:before{content:"\f46c"}.fa-file-audio:before{content:"\f1c7"}.fa-burger:before,.fa-hamburger:before{content:"\f805"}.fa-wrench:before{content:"\f0ad"}.fa-bugs:before{content:"\e4d0"}.fa-rupee-sign:before,.fa-rupee:before{content:"\f156"}.fa-file-image:before{content:"\f1c5"}.fa-circle-question:before,.fa-question-circle:before{content:"\f059"}.fa-plane-departure:before{content:"\f5b0"}.fa-handshake-slash:before{content:"\e060"}.fa-book-bookmark:before{content:"\e0bb"}.fa-code-branch:before{content:"\f126"}.fa-hat-cowboy:before{content:"\f8c0"}.fa-bridge:before{content:"\e4c8"}.fa-phone-alt:before,.fa-phone-flip:before{content:"\f879"}.fa-truck-front:before{content:"\e2b7"}.fa-cat:before{content:"\f6be"}.fa-anchor-circle-exclamation:before{content:"\e4ab"}.fa-truck-field:before{content:"\e58d"}.fa-route:before{content:"\f4d7"}.fa-clipboard-question:before{content:"\e4e3"}.fa-panorama:before{content:"\e209"}.fa-comment-medical:before{content:"\f7f5"}.fa-teeth-open:before{content:"\f62f"}.fa-file-circle-minus:before{content:"\e4ed"}.fa-tags:before{content:"\f02c"}.fa-wine-glass:before{content:"\f4e3"}.fa-fast-forward:before,.fa-forward-fast:before{content:"\f050"}.fa-face-meh-blank:before,.fa-meh-blank:before{content:"\f5a4"}.fa-parking:before,.fa-square-parking:before{content:"\f540"}.fa-house-signal:before{content:"\e012"}.fa-bars-progress:before,.fa-tasks-alt:before{content:"\f828"}.fa-faucet-drip:before{content:"\e006"}.fa-cart-flatbed:before,.fa-dolly-flatbed:before{content:"\f474"}.fa-ban-smoking:before,.fa-smoking-ban:before{content:"\f54d"}.fa-terminal:before{content:"\f120"}.fa-mobile-button:before{content:"\f10b"}.fa-house-medical-flag:before{content:"\e514"}.fa-basket-shopping:before,.fa-shopping-basket:before{content:"\f291"}.fa-tape:before{content:"\f4db"}.fa-bus-alt:before,.fa-bus-simple:before{content:"\f55e"}.fa-eye:before{content:"\f06e"}.fa-face-sad-cry:before,.fa-sad-cry:before{content:"\f5b3"}.fa-audio-description:before{content:"\f29e"}.fa-person-military-to-person:before{content:"\e54c"}.fa-file-shield:before{content:"\e4f0"}.fa-user-slash:before{content:"\f506"}.fa-pen:before{content:"\f304"}.fa-tower-observation:before{content:"\e586"}.fa-file-code:before{content:"\f1c9"}.fa-signal-5:before,.fa-signal-perfect:before,.fa-signal:before{content:"\f012"}.fa-bus:before{content:"\f207"}.fa-heart-circle-xmark:before{content:"\e501"}.fa-home-lg:before,.fa-house-chimney:before{content:"\e3af"}.fa-window-maximize:before{content:"\f2d0"}.fa-face-frown:before,.fa-frown:before{content:"\f119"}.fa-prescription:before{content:"\f5b1"}.fa-shop:before,.fa-store-alt:before{content:"\f54f"}.fa-floppy-disk:before,.fa-save:before{content:"\f0c7"}.fa-vihara:before{content:"\f6a7"}.fa-balance-scale-left:before,.fa-scale-unbalanced:before{content:"\f515"}.fa-sort-asc:before,.fa-sort-up:before{content:"\f0de"}.fa-comment-dots:before,.fa-commenting:before{content:"\f4ad"}.fa-plant-wilt:before{content:"\e5aa"}.fa-diamond:before{content:"\f219"}.fa-face-grin-squint:before,.fa-grin-squint:before{content:"\f585"}.fa-hand-holding-dollar:before,.fa-hand-holding-usd:before{content:"\f4c0"}.fa-bacterium:before{content:"\e05a"}.fa-hand-pointer:before{content:"\f25a"}.fa-drum-steelpan:before{content:"\f56a"}.fa-hand-scissors:before{content:"\f257"}.fa-hands-praying:before,.fa-praying-hands:before{content:"\f684"}.fa-arrow-right-rotate:before,.fa-arrow-rotate-forward:before,.fa-arrow-rotate-right:before,.fa-redo:before{content:"\f01e"}.fa-biohazard:before{content:"\f780"}.fa-location-crosshairs:before,.fa-location:before{content:"\f601"}.fa-mars-double:before{content:"\f227"}.fa-child-dress:before{content:"\e59c"}.fa-users-between-lines:before{content:"\e591"}.fa-lungs-virus:before{content:"\e067"}.fa-face-grin-tears:before,.fa-grin-tears:before{content:"\f588"}.fa-phone:before{content:"\f095"}.fa-calendar-times:before,.fa-calendar-xmark:before{content:"\f273"}.fa-child-reaching:before{content:"\e59d"}.fa-head-side-virus:before{content:"\e064"}.fa-user-cog:before,.fa-user-gear:before{content:"\f4fe"}.fa-arrow-up-1-9:before,.fa-sort-numeric-up:before{content:"\f163"}.fa-door-closed:before{content:"\f52a"}.fa-shield-virus:before{content:"\e06c"}.fa-dice-six:before{content:"\f526"}.fa-mosquito-net:before{content:"\e52c"}.fa-bridge-water:before{content:"\e4ce"}.fa-person-booth:before{content:"\f756"}.fa-text-width:before{content:"\f035"}.fa-hat-wizard:before{content:"\f6e8"}.fa-pen-fancy:before{content:"\f5ac"}.fa-digging:before,.fa-person-digging:before{content:"\f85e"}.fa-trash:before{content:"\f1f8"}.fa-gauge-simple-med:before,.fa-gauge-simple:before,.fa-tachometer-average:before{content:"\f629"}.fa-book-medical:before{content:"\f7e6"}.fa-poo:before{content:"\f2fe"}.fa-quote-right-alt:before,.fa-quote-right:before{content:"\f10e"}.fa-shirt:before,.fa-t-shirt:before,.fa-tshirt:before{content:"\f553"}.fa-cubes:before{content:"\f1b3"}.fa-divide:before{content:"\f529"}.fa-tenge-sign:before,.fa-tenge:before{content:"\f7d7"}.fa-headphones:before{content:"\f025"}.fa-hands-holding:before{content:"\f4c2"}.fa-hands-clapping:before{content:"\e1a8"}.fa-republican:before{content:"\f75e"}.fa-arrow-left:before{content:"\f060"}.fa-person-circle-xmark:before{content:"\e543"}.fa-ruler:before{content:"\f545"}.fa-align-left:before{content:"\f036"}.fa-dice-d6:before{content:"\f6d1"}.fa-restroom:before{content:"\f7bd"}.fa-j:before{content:"\4a"}.fa-users-viewfinder:before{content:"\e595"}.fa-file-video:before{content:"\f1c8"}.fa-external-link-alt:before,.fa-up-right-from-square:before{content:"\f35d"}.fa-table-cells:before,.fa-th:before{content:"\f00a"}.fa-file-pdf:before{content:"\f1c1"}.fa-bible:before,.fa-book-bible:before{content:"\f647"}.fa-o:before{content:"\4f"}.fa-medkit:before,.fa-suitcase-medical:before{content:"\f0fa"}.fa-user-secret:before{content:"\f21b"}.fa-otter:before{content:"\f700"}.fa-female:before,.fa-person-dress:before{content:"\f182"}.fa-comment-dollar:before{content:"\f651"}.fa-briefcase-clock:before,.fa-business-time:before{content:"\f64a"}.fa-table-cells-large:before,.fa-th-large:before{content:"\f009"}.fa-book-tanakh:before,.fa-tanakh:before{content:"\f827"}.fa-phone-volume:before,.fa-volume-control-phone:before{content:"\f2a0"}.fa-hat-cowboy-side:before{content:"\f8c1"}.fa-clipboard-user:before{content:"\f7f3"}.fa-child:before{content:"\f1ae"}.fa-lira-sign:before{content:"\f195"}.fa-satellite:before{content:"\f7bf"}.fa-plane-lock:before{content:"\e558"}.fa-tag:before{content:"\f02b"}.fa-comment:before{content:"\f075"}.fa-birthday-cake:before,.fa-cake-candles:before,.fa-cake:before{content:"\f1fd"}.fa-envelope:before{content:"\f0e0"}.fa-angle-double-up:before,.fa-angles-up:before{content:"\f102"}.fa-paperclip:before{content:"\f0c6"}.fa-arrow-right-to-city:before{content:"\e4b3"}.fa-ribbon:before{content:"\f4d6"}.fa-lungs:before{content:"\f604"}.fa-arrow-up-9-1:before,.fa-sort-numeric-up-alt:before{content:"\f887"}.fa-litecoin-sign:before{content:"\e1d3"}.fa-border-none:before{content:"\f850"}.fa-circle-nodes:before{content:"\e4e2"}.fa-parachute-box:before{content:"\f4cd"}.fa-indent:before{content:"\f03c"}.fa-truck-field-un:before{content:"\e58e"}.fa-hourglass-empty:before,.fa-hourglass:before{content:"\f254"}.fa-mountain:before{content:"\f6fc"}.fa-user-doctor:before,.fa-user-md:before{content:"\f0f0"}.fa-circle-info:before,.fa-info-circle:before{content:"\f05a"}.fa-cloud-meatball:before{content:"\f73b"}.fa-camera-alt:before,.fa-camera:before{content:"\f030"}.fa-square-virus:before{content:"\e578"}.fa-meteor:before{content:"\f753"}.fa-car-on:before{content:"\e4dd"}.fa-sleigh:before{content:"\f7cc"}.fa-arrow-down-1-9:before,.fa-sort-numeric-asc:before,.fa-sort-numeric-down:before{content:"\f162"}.fa-hand-holding-droplet:before,.fa-hand-holding-water:before{content:"\f4c1"}.fa-water:before{content:"\f773"}.fa-calendar-check:before{content:"\f274"}.fa-braille:before{content:"\f2a1"}.fa-prescription-bottle-alt:before,.fa-prescription-bottle-medical:before{content:"\f486"}.fa-landmark:before{content:"\f66f"}.fa-truck:before{content:"\f0d1"}.fa-crosshairs:before{content:"\f05b"}.fa-person-cane:before{content:"\e53c"}.fa-tent:before{content:"\e57d"}.fa-vest-patches:before{content:"\e086"}.fa-check-double:before{content:"\f560"}.fa-arrow-down-a-z:before,.fa-sort-alpha-asc:before,.fa-sort-alpha-down:before{content:"\f15d"}.fa-money-bill-wheat:before{content:"\e52a"}.fa-cookie:before{content:"\f563"}.fa-arrow-left-rotate:before,.fa-arrow-rotate-back:before,.fa-arrow-rotate-backward:before,.fa-arrow-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-hard-drive:before,.fa-hdd:before{content:"\f0a0"}.fa-face-grin-squint-tears:before,.fa-grin-squint-tears:before{content:"\f586"}.fa-dumbbell:before{content:"\f44b"}.fa-list-alt:before,.fa-rectangle-list:before{content:"\f022"}.fa-tarp-droplet:before{content:"\e57c"}.fa-house-medical-circle-check:before{content:"\e511"}.fa-person-skiing-nordic:before,.fa-skiing-nordic:before{content:"\f7ca"}.fa-calendar-plus:before{content:"\f271"}.fa-plane-arrival:before{content:"\f5af"}.fa-arrow-alt-circle-left:before,.fa-circle-left:before{content:"\f359"}.fa-subway:before,.fa-train-subway:before{content:"\f239"}.fa-chart-gantt:before{content:"\e0e4"}.fa-indian-rupee-sign:before,.fa-indian-rupee:before,.fa-inr:before{content:"\e1bc"}.fa-crop-alt:before,.fa-crop-simple:before{content:"\f565"}.fa-money-bill-1:before,.fa-money-bill-alt:before{content:"\f3d1"}.fa-left-long:before,.fa-long-arrow-alt-left:before{content:"\f30a"}.fa-dna:before{content:"\f471"}.fa-virus-slash:before{content:"\e075"}.fa-minus:before,.fa-subtract:before{content:"\f068"}.fa-chess:before{content:"\f439"}.fa-arrow-left-long:before,.fa-long-arrow-left:before{content:"\f177"}.fa-plug-circle-check:before{content:"\e55c"}.fa-street-view:before{content:"\f21d"}.fa-franc-sign:before{content:"\e18f"}.fa-volume-off:before{content:"\f026"}.fa-american-sign-language-interpreting:before,.fa-asl-interpreting:before,.fa-hands-american-sign-language-interpreting:before,.fa-hands-asl-interpreting:before{content:"\f2a3"}.fa-cog:before,.fa-gear:before{content:"\f013"}.fa-droplet-slash:before,.fa-tint-slash:before{content:"\f5c7"}.fa-mosque:before{content:"\f678"}.fa-mosquito:before{content:"\e52b"}.fa-star-of-david:before{content:"\f69a"}.fa-person-military-rifle:before{content:"\e54b"}.fa-cart-shopping:before,.fa-shopping-cart:before{content:"\f07a"}.fa-vials:before{content:"\f493"}.fa-plug-circle-plus:before{content:"\e55f"}.fa-place-of-worship:before{content:"\f67f"}.fa-grip-vertical:before{content:"\f58e"}.fa-arrow-turn-up:before,.fa-level-up:before{content:"\f148"}.fa-u:before{content:"\55"}.fa-square-root-alt:before,.fa-square-root-variable:before{content:"\f698"}.fa-clock-four:before,.fa-clock:before{content:"\f017"}.fa-backward-step:before,.fa-step-backward:before{content:"\f048"}.fa-pallet:before{content:"\f482"}.fa-faucet:before{content:"\e005"}.fa-baseball-bat-ball:before{content:"\f432"}.fa-s:before{content:"\53"}.fa-timeline:before{content:"\e29c"}.fa-keyboard:before{content:"\f11c"}.fa-caret-down:before{content:"\f0d7"}.fa-clinic-medical:before,.fa-house-chimney-medical:before{content:"\f7f2"}.fa-temperature-3:before,.fa-temperature-three-quarters:before,.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-mobile-android-alt:before,.fa-mobile-screen:before{content:"\f3cf"}.fa-plane-up:before{content:"\e22d"}.fa-piggy-bank:before{content:"\f4d3"}.fa-battery-3:before,.fa-battery-half:before{content:"\f242"}.fa-mountain-city:before{content:"\e52e"}.fa-coins:before{content:"\f51e"}.fa-khanda:before{content:"\f66d"}.fa-sliders-h:before,.fa-sliders:before{content:"\f1de"}.fa-folder-tree:before{content:"\f802"}.fa-network-wired:before{content:"\f6ff"}.fa-map-pin:before{content:"\f276"}.fa-hamsa:before{content:"\f665"}.fa-cent-sign:before{content:"\e3f5"}.fa-flask:before{content:"\f0c3"}.fa-person-pregnant:before{content:"\e31e"}.fa-wand-sparkles:before{content:"\f72b"}.fa-ellipsis-v:before,.fa-ellipsis-vertical:before{content:"\f142"}.fa-ticket:before{content:"\f145"}.fa-power-off:before{content:"\f011"}.fa-long-arrow-alt-right:before,.fa-right-long:before{content:"\f30b"}.fa-flag-usa:before{content:"\f74d"}.fa-laptop-file:before{content:"\e51d"}.fa-teletype:before,.fa-tty:before{content:"\f1e4"}.fa-diagram-next:before{content:"\e476"}.fa-person-rifle:before{content:"\e54e"}.fa-house-medical-circle-exclamation:before{content:"\e512"}.fa-closed-captioning:before{content:"\f20a"}.fa-hiking:before,.fa-person-hiking:before{content:"\f6ec"}.fa-venus-double:before{content:"\f226"}.fa-images:before{content:"\f302"}.fa-calculator:before{content:"\f1ec"}.fa-people-pulling:before{content:"\e535"}.fa-n:before{content:"\4e"}.fa-cable-car:before,.fa-tram:before{content:"\f7da"}.fa-cloud-rain:before{content:"\f73d"}.fa-building-circle-xmark:before{content:"\e4d4"}.fa-ship:before{content:"\f21a"}.fa-arrows-down-to-line:before{content:"\e4b8"}.fa-download:before{content:"\f019"}.fa-face-grin:before,.fa-grin:before{content:"\f580"}.fa-backspace:before,.fa-delete-left:before{content:"\f55a"}.fa-eye-dropper-empty:before,.fa-eye-dropper:before,.fa-eyedropper:before{content:"\f1fb"}.fa-file-circle-check:before{content:"\e5a0"}.fa-forward:before{content:"\f04e"}.fa-mobile-android:before,.fa-mobile-phone:before,.fa-mobile:before{content:"\f3ce"}.fa-face-meh:before,.fa-meh:before{content:"\f11a"}.fa-align-center:before{content:"\f037"}.fa-book-dead:before,.fa-book-skull:before{content:"\f6b7"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-heart-circle-exclamation:before{content:"\e4fe"}.fa-home-alt:before,.fa-home-lg-alt:before,.fa-home:before,.fa-house:before{content:"\f015"}.fa-calendar-week:before{content:"\f784"}.fa-laptop-medical:before{content:"\f812"}.fa-b:before{content:"\42"}.fa-file-medical:before{content:"\f477"}.fa-dice-one:before{content:"\f525"}.fa-kiwi-bird:before{content:"\f535"}.fa-arrow-right-arrow-left:before,.fa-exchange:before{content:"\f0ec"}.fa-redo-alt:before,.fa-rotate-forward:before,.fa-rotate-right:before{content:"\f2f9"}.fa-cutlery:before,.fa-utensils:before{content:"\f2e7"}.fa-arrow-up-wide-short:before,.fa-sort-amount-up:before{content:"\f161"}.fa-mill-sign:before{content:"\e1ed"}.fa-bowl-rice:before{content:"\e2eb"}.fa-skull:before{content:"\f54c"}.fa-broadcast-tower:before,.fa-tower-broadcast:before{content:"\f519"}.fa-truck-pickup:before{content:"\f63c"}.fa-long-arrow-alt-up:before,.fa-up-long:before{content:"\f30c"}.fa-stop:before{content:"\f04d"}.fa-code-merge:before{content:"\f387"}.fa-upload:before{content:"\f093"}.fa-hurricane:before{content:"\f751"}.fa-mound:before{content:"\e52d"}.fa-toilet-portable:before{content:"\e583"}.fa-compact-disc:before{content:"\f51f"}.fa-file-arrow-down:before,.fa-file-download:before{content:"\f56d"}.fa-caravan:before{content:"\f8ff"}.fa-shield-cat:before{content:"\e572"}.fa-bolt:before,.fa-zap:before{content:"\f0e7"}.fa-glass-water:before{content:"\e4f4"}.fa-oil-well:before{content:"\e532"}.fa-vault:before{content:"\e2c5"}.fa-mars:before{content:"\f222"}.fa-toilet:before{content:"\f7d8"}.fa-plane-circle-xmark:before{content:"\e557"}.fa-cny:before,.fa-jpy:before,.fa-rmb:before,.fa-yen-sign:before,.fa-yen:before{content:"\f157"}.fa-rouble:before,.fa-rub:before,.fa-ruble-sign:before,.fa-ruble:before{content:"\f158"}.fa-sun:before{content:"\f185"}.fa-guitar:before{content:"\f7a6"}.fa-face-laugh-wink:before,.fa-laugh-wink:before{content:"\f59c"}.fa-horse-head:before{content:"\f7ab"}.fa-bore-hole:before{content:"\e4c3"}.fa-industry:before{content:"\f275"}.fa-arrow-alt-circle-down:before,.fa-circle-down:before{content:"\f358"}.fa-arrows-turn-to-dots:before{content:"\e4c1"}.fa-florin-sign:before{content:"\e184"}.fa-arrow-down-short-wide:before,.fa-sort-amount-desc:before,.fa-sort-amount-down-alt:before{content:"\f884"}.fa-less-than:before{content:"\3c"}.fa-angle-down:before{content:"\f107"}.fa-car-tunnel:before{content:"\e4de"}.fa-head-side-cough:before{content:"\e061"}.fa-grip-lines:before{content:"\f7a4"}.fa-thumbs-down:before{content:"\f165"}.fa-user-lock:before{content:"\f502"}.fa-arrow-right-long:before,.fa-long-arrow-right:before{content:"\f178"}.fa-anchor-circle-xmark:before{content:"\e4ac"}.fa-ellipsis-h:before,.fa-ellipsis:before{content:"\f141"}.fa-chess-pawn:before{content:"\f443"}.fa-first-aid:before,.fa-kit-medical:before{content:"\f479"}.fa-person-through-window:before{content:"\e5a9"}.fa-toolbox:before{content:"\f552"}.fa-hands-holding-circle:before{content:"\e4fb"}.fa-bug:before{content:"\f188"}.fa-credit-card-alt:before,.fa-credit-card:before{content:"\f09d"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-hand-holding-hand:before{content:"\e4f7"}.fa-book-open-reader:before,.fa-book-reader:before{content:"\f5da"}.fa-mountain-sun:before{content:"\e52f"}.fa-arrows-left-right-to-line:before{content:"\e4ba"}.fa-dice-d20:before{content:"\f6cf"}.fa-truck-droplet:before{content:"\e58c"}.fa-file-circle-xmark:before{content:"\e5a1"}.fa-temperature-arrow-up:before,.fa-temperature-up:before{content:"\e040"}.fa-medal:before{content:"\f5a2"}.fa-bed:before{content:"\f236"}.fa-h-square:before,.fa-square-h:before{content:"\f0fd"}.fa-podcast:before{content:"\f2ce"}.fa-temperature-4:before,.fa-temperature-full:before,.fa-thermometer-4:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-bell:before{content:"\f0f3"}.fa-superscript:before{content:"\f12b"}.fa-plug-circle-xmark:before{content:"\e560"}.fa-star-of-life:before{content:"\f621"}.fa-phone-slash:before{content:"\f3dd"}.fa-paint-roller:before{content:"\f5aa"}.fa-hands-helping:before,.fa-handshake-angle:before{content:"\f4c4"}.fa-location-dot:before,.fa-map-marker-alt:before{content:"\f3c5"}.fa-file:before{content:"\f15b"}.fa-greater-than:before{content:"\3e"}.fa-person-swimming:before,.fa-swimmer:before{content:"\f5c4"}.fa-arrow-down:before{content:"\f063"}.fa-droplet:before,.fa-tint:before{content:"\f043"}.fa-eraser:before{content:"\f12d"}.fa-earth-america:before,.fa-earth-americas:before,.fa-earth:before,.fa-globe-americas:before{content:"\f57d"}.fa-person-burst:before{content:"\e53b"}.fa-dove:before{content:"\f4ba"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-socks:before{content:"\f696"}.fa-inbox:before{content:"\f01c"}.fa-section:before{content:"\e447"}.fa-gauge-high:before,.fa-tachometer-alt-fast:before,.fa-tachometer-alt:before{content:"\f625"}.fa-envelope-open-text:before{content:"\f658"}.fa-hospital-alt:before,.fa-hospital-wide:before,.fa-hospital:before{content:"\f0f8"}.fa-wine-bottle:before{content:"\f72f"}.fa-chess-rook:before{content:"\f447"}.fa-bars-staggered:before,.fa-reorder:before,.fa-stream:before{content:"\f550"}.fa-dharmachakra:before{content:"\f655"}.fa-hotdog:before{content:"\f80f"}.fa-blind:before,.fa-person-walking-with-cane:before{content:"\f29d"}.fa-drum:before{content:"\f569"}.fa-ice-cream:before{content:"\f810"}.fa-heart-circle-bolt:before{content:"\e4fc"}.fa-fax:before{content:"\f1ac"}.fa-paragraph:before{content:"\f1dd"}.fa-check-to-slot:before,.fa-vote-yea:before{content:"\f772"}.fa-star-half:before{content:"\f089"}.fa-boxes-alt:before,.fa-boxes-stacked:before,.fa-boxes:before{content:"\f468"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-assistive-listening-systems:before,.fa-ear-listen:before{content:"\f2a2"}.fa-tree-city:before{content:"\e587"}.fa-play:before{content:"\f04b"}.fa-font:before{content:"\f031"}.fa-rupiah-sign:before{content:"\e23d"}.fa-magnifying-glass:before,.fa-search:before{content:"\f002"}.fa-ping-pong-paddle-ball:before,.fa-table-tennis-paddle-ball:before,.fa-table-tennis:before{content:"\f45d"}.fa-diagnoses:before,.fa-person-dots-from-line:before{content:"\f470"}.fa-trash-can-arrow-up:before,.fa-trash-restore-alt:before{content:"\f82a"}.fa-naira-sign:before{content:"\e1f6"}.fa-cart-arrow-down:before{content:"\f218"}.fa-walkie-talkie:before{content:"\f8ef"}.fa-file-edit:before,.fa-file-pen:before{content:"\f31c"}.fa-receipt:before{content:"\f543"}.fa-pen-square:before,.fa-pencil-square:before,.fa-square-pen:before{content:"\f14b"}.fa-suitcase-rolling:before{content:"\f5c1"}.fa-person-circle-exclamation:before{content:"\e53f"}.fa-chevron-down:before{content:"\f078"}.fa-battery-5:before,.fa-battery-full:before,.fa-battery:before{content:"\f240"}.fa-skull-crossbones:before{content:"\f714"}.fa-code-compare:before{content:"\e13a"}.fa-list-dots:before,.fa-list-ul:before{content:"\f0ca"}.fa-school-lock:before{content:"\e56f"}.fa-tower-cell:before{content:"\e585"}.fa-down-long:before,.fa-long-arrow-alt-down:before{content:"\f309"}.fa-ranking-star:before{content:"\e561"}.fa-chess-king:before{content:"\f43f"}.fa-person-harassing:before{content:"\e549"}.fa-brazilian-real-sign:before{content:"\e46c"}.fa-landmark-alt:before,.fa-landmark-dome:before{content:"\f752"}.fa-arrow-up:before{content:"\f062"}.fa-television:before,.fa-tv-alt:before,.fa-tv:before{content:"\f26c"}.fa-shrimp:before{content:"\e448"}.fa-list-check:before,.fa-tasks:before{content:"\f0ae"}.fa-jug-detergent:before{content:"\e519"}.fa-circle-user:before,.fa-user-circle:before{content:"\f2bd"}.fa-user-shield:before{content:"\f505"}.fa-wind:before{content:"\f72e"}.fa-car-burst:before,.fa-car-crash:before{content:"\f5e1"}.fa-y:before{content:"\59"}.fa-person-snowboarding:before,.fa-snowboarding:before{content:"\f7ce"}.fa-shipping-fast:before,.fa-truck-fast:before{content:"\f48b"}.fa-fish:before{content:"\f578"}.fa-user-graduate:before{content:"\f501"}.fa-adjust:before,.fa-circle-half-stroke:before{content:"\f042"}.fa-clapperboard:before{content:"\e131"}.fa-circle-radiation:before,.fa-radiation-alt:before{content:"\f7ba"}.fa-baseball-ball:before,.fa-baseball:before{content:"\f433"}.fa-jet-fighter-up:before{content:"\e518"}.fa-diagram-project:before,.fa-project-diagram:before{content:"\f542"}.fa-copy:before{content:"\f0c5"}.fa-volume-mute:before,.fa-volume-times:before,.fa-volume-xmark:before{content:"\f6a9"}.fa-hand-sparkles:before{content:"\e05d"}.fa-grip-horizontal:before,.fa-grip:before{content:"\f58d"}.fa-share-from-square:before,.fa-share-square:before{content:"\f14d"}.fa-child-combatant:before,.fa-child-rifle:before{content:"\e4e0"}.fa-gun:before{content:"\e19b"}.fa-phone-square:before,.fa-square-phone:before{content:"\f098"}.fa-add:before,.fa-plus:before{content:"\2b"}.fa-expand:before{content:"\f065"}.fa-computer:before{content:"\e4e5"}.fa-close:before,.fa-multiply:before,.fa-remove:before,.fa-times:before,.fa-xmark:before{content:"\f00d"}.fa-arrows-up-down-left-right:before,.fa-arrows:before{content:"\f047"}.fa-chalkboard-teacher:before,.fa-chalkboard-user:before{content:"\f51c"}.fa-peso-sign:before{content:"\e222"}.fa-building-shield:before{content:"\e4d8"}.fa-baby:before{content:"\f77c"}.fa-users-line:before{content:"\e592"}.fa-quote-left-alt:before,.fa-quote-left:before{content:"\f10d"}.fa-tractor:before{content:"\f722"}.fa-trash-arrow-up:before,.fa-trash-restore:before{content:"\f829"}.fa-arrow-down-up-lock:before{content:"\e4b0"}.fa-lines-leaning:before{content:"\e51e"}.fa-ruler-combined:before{content:"\f546"}.fa-copyright:before{content:"\f1f9"}.fa-equals:before{content:"\3d"}.fa-blender:before{content:"\f517"}.fa-teeth:before{content:"\f62e"}.fa-ils:before,.fa-shekel-sign:before,.fa-shekel:before,.fa-sheqel-sign:before,.fa-sheqel:before{content:"\f20b"}.fa-map:before{content:"\f279"}.fa-rocket:before{content:"\f135"}.fa-photo-film:before,.fa-photo-video:before{content:"\f87c"}.fa-folder-minus:before{content:"\f65d"}.fa-store:before{content:"\f54e"}.fa-arrow-trend-up:before{content:"\e098"}.fa-plug-circle-minus:before{content:"\e55e"}.fa-sign-hanging:before,.fa-sign:before{content:"\f4d9"}.fa-bezier-curve:before{content:"\f55b"}.fa-bell-slash:before{content:"\f1f6"}.fa-tablet-android:before,.fa-tablet:before{content:"\f3fb"}.fa-school-flag:before{content:"\e56e"}.fa-fill:before{content:"\f575"}.fa-angle-up:before{content:"\f106"}.fa-drumstick-bite:before{content:"\f6d7"}.fa-holly-berry:before{content:"\f7aa"}.fa-chevron-left:before{content:"\f053"}.fa-bacteria:before{content:"\e059"}.fa-hand-lizard:before{content:"\f258"}.fa-notdef:before{content:"\e1fe"}.fa-disease:before{content:"\f7fa"}.fa-briefcase-medical:before{content:"\f469"}.fa-genderless:before{content:"\f22d"}.fa-chevron-right:before{content:"\f054"}.fa-retweet:before{content:"\f079"}.fa-car-alt:before,.fa-car-rear:before{content:"\f5de"}.fa-pump-soap:before{content:"\e06b"}.fa-video-slash:before{content:"\f4e2"}.fa-battery-2:before,.fa-battery-quarter:before{content:"\f243"}.fa-radio:before{content:"\f8d7"}.fa-baby-carriage:before,.fa-carriage-baby:before{content:"\f77d"}.fa-traffic-light:before{content:"\f637"}.fa-thermometer:before{content:"\f491"}.fa-vr-cardboard:before{content:"\f729"}.fa-hand-middle-finger:before{content:"\f806"}.fa-percent:before,.fa-percentage:before{content:"\25"}.fa-truck-moving:before{content:"\f4df"}.fa-glass-water-droplet:before{content:"\e4f5"}.fa-display:before{content:"\e163"}.fa-face-smile:before,.fa-smile:before{content:"\f118"}.fa-thumb-tack:before,.fa-thumbtack:before{content:"\f08d"}.fa-trophy:before{content:"\f091"}.fa-person-praying:before,.fa-pray:before{content:"\f683"}.fa-hammer:before{content:"\f6e3"}.fa-hand-peace:before{content:"\f25b"}.fa-rotate:before,.fa-sync-alt:before{content:"\f2f1"}.fa-spinner:before{content:"\f110"}.fa-robot:before{content:"\f544"}.fa-peace:before{content:"\f67c"}.fa-cogs:before,.fa-gears:before{content:"\f085"}.fa-warehouse:before{content:"\f494"}.fa-arrow-up-right-dots:before{content:"\e4b7"}.fa-splotch:before{content:"\f5bc"}.fa-face-grin-hearts:before,.fa-grin-hearts:before{content:"\f584"}.fa-dice-four:before{content:"\f524"}.fa-sim-card:before{content:"\f7c4"}.fa-transgender-alt:before,.fa-transgender:before{content:"\f225"}.fa-mercury:before{content:"\f223"}.fa-arrow-turn-down:before,.fa-level-down:before{content:"\f149"}.fa-person-falling-burst:before{content:"\e547"}.fa-award:before{content:"\f559"}.fa-ticket-alt:before,.fa-ticket-simple:before{content:"\f3ff"}.fa-building:before{content:"\f1ad"}.fa-angle-double-left:before,.fa-angles-left:before{content:"\f100"}.fa-qrcode:before{content:"\f029"}.fa-clock-rotate-left:before,.fa-history:before{content:"\f1da"}.fa-face-grin-beam-sweat:before,.fa-grin-beam-sweat:before{content:"\f583"}.fa-arrow-right-from-file:before,.fa-file-export:before{content:"\f56e"}.fa-shield-blank:before,.fa-shield:before{content:"\f132"}.fa-arrow-up-short-wide:before,.fa-sort-amount-up-alt:before{content:"\f885"}.fa-house-medical:before{content:"\e3b2"}.fa-golf-ball-tee:before,.fa-golf-ball:before{content:"\f450"}.fa-chevron-circle-left:before,.fa-circle-chevron-left:before{content:"\f137"}.fa-house-chimney-window:before{content:"\e00d"}.fa-pen-nib:before{content:"\f5ad"}.fa-tent-arrow-turn-left:before{content:"\e580"}.fa-tents:before{content:"\e582"}.fa-magic:before,.fa-wand-magic:before{content:"\f0d0"}.fa-dog:before{content:"\f6d3"}.fa-carrot:before{content:"\f787"}.fa-moon:before{content:"\f186"}.fa-wine-glass-alt:before,.fa-wine-glass-empty:before{content:"\f5ce"}.fa-cheese:before{content:"\f7ef"}.fa-yin-yang:before{content:"\f6ad"}.fa-music:before{content:"\f001"}.fa-code-commit:before{content:"\f386"}.fa-temperature-low:before{content:"\f76b"}.fa-biking:before,.fa-person-biking:before{content:"\f84a"}.fa-broom:before{content:"\f51a"}.fa-shield-heart:before{content:"\e574"}.fa-gopuram:before{content:"\f664"}.fa-earth-oceania:before,.fa-globe-oceania:before{content:"\e47b"}.fa-square-xmark:before,.fa-times-square:before,.fa-xmark-square:before{content:"\f2d3"}.fa-hashtag:before{content:"\23"}.fa-expand-alt:before,.fa-up-right-and-down-left-from-center:before{content:"\f424"}.fa-oil-can:before{content:"\f613"}.fa-t:before{content:"\54"}.fa-hippo:before{content:"\f6ed"}.fa-chart-column:before{content:"\e0e3"}.fa-infinity:before{content:"\f534"}.fa-vial-circle-check:before{content:"\e596"}.fa-person-arrow-down-to-line:before{content:"\e538"}.fa-voicemail:before{content:"\f897"}.fa-fan:before{content:"\f863"}.fa-person-walking-luggage:before{content:"\e554"}.fa-arrows-alt-v:before,.fa-up-down:before{content:"\f338"}.fa-cloud-moon-rain:before{content:"\f73c"}.fa-calendar:before{content:"\f133"}.fa-trailer:before{content:"\e041"}.fa-bahai:before,.fa-haykal:before{content:"\f666"}.fa-sd-card:before{content:"\f7c2"}.fa-dragon:before{content:"\f6d5"}.fa-shoe-prints:before{content:"\f54b"}.fa-circle-plus:before,.fa-plus-circle:before{content:"\f055"}.fa-face-grin-tongue-wink:before,.fa-grin-tongue-wink:before{content:"\f58b"}.fa-hand-holding:before{content:"\f4bd"}.fa-plug-circle-exclamation:before{content:"\e55d"}.fa-chain-broken:before,.fa-chain-slash:before,.fa-link-slash:before,.fa-unlink:before{content:"\f127"}.fa-clone:before{content:"\f24d"}.fa-person-walking-arrow-loop-left:before{content:"\e551"}.fa-arrow-up-z-a:before,.fa-sort-alpha-up-alt:before{content:"\f882"}.fa-fire-alt:before,.fa-fire-flame-curved:before{content:"\f7e4"}.fa-tornado:before{content:"\f76f"}.fa-file-circle-plus:before{content:"\e494"}.fa-book-quran:before,.fa-quran:before{content:"\f687"}.fa-anchor:before{content:"\f13d"}.fa-border-all:before{content:"\f84c"}.fa-angry:before,.fa-face-angry:before{content:"\f556"}.fa-cookie-bite:before{content:"\f564"}.fa-arrow-trend-down:before{content:"\e097"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-draw-polygon:before{content:"\f5ee"}.fa-balance-scale:before,.fa-scale-balanced:before{content:"\f24e"}.fa-gauge-simple-high:before,.fa-tachometer-fast:before,.fa-tachometer:before{content:"\f62a"}.fa-shower:before{content:"\f2cc"}.fa-desktop-alt:before,.fa-desktop:before{content:"\f390"}.fa-m:before{content:"\4d"}.fa-table-list:before,.fa-th-list:before{content:"\f00b"}.fa-comment-sms:before,.fa-sms:before{content:"\f7cd"}.fa-book:before{content:"\f02d"}.fa-user-plus:before{content:"\f234"}.fa-check:before{content:"\f00c"}.fa-battery-4:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-house-circle-check:before{content:"\e509"}.fa-angle-left:before{content:"\f104"}.fa-diagram-successor:before{content:"\e47a"}.fa-truck-arrow-right:before{content:"\e58b"}.fa-arrows-split-up-and-left:before{content:"\e4bc"}.fa-fist-raised:before,.fa-hand-fist:before{content:"\f6de"}.fa-cloud-moon:before{content:"\f6c3"}.fa-briefcase:before{content:"\f0b1"}.fa-person-falling:before{content:"\e546"}.fa-image-portrait:before,.fa-portrait:before{content:"\f3e0"}.fa-user-tag:before{content:"\f507"}.fa-rug:before{content:"\e569"}.fa-earth-europe:before,.fa-globe-europe:before{content:"\f7a2"}.fa-cart-flatbed-suitcase:before,.fa-luggage-cart:before{content:"\f59d"}.fa-rectangle-times:before,.fa-rectangle-xmark:before,.fa-times-rectangle:before,.fa-window-close:before{content:"\f410"}.fa-baht-sign:before{content:"\e0ac"}.fa-book-open:before{content:"\f518"}.fa-book-journal-whills:before,.fa-journal-whills:before{content:"\f66a"}.fa-handcuffs:before{content:"\e4f8"}.fa-exclamation-triangle:before,.fa-triangle-exclamation:before,.fa-warning:before{content:"\f071"}.fa-database:before{content:"\f1c0"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-bottle-droplet:before{content:"\e4c4"}.fa-mask-face:before{content:"\e1d7"}.fa-hill-rockslide:before{content:"\e508"}.fa-exchange-alt:before,.fa-right-left:before{content:"\f362"}.fa-paper-plane:before{content:"\f1d8"}.fa-road-circle-exclamation:before{content:"\e565"}.fa-dungeon:before{content:"\f6d9"}.fa-align-right:before{content:"\f038"}.fa-money-bill-1-wave:before,.fa-money-bill-wave-alt:before{content:"\f53b"}.fa-life-ring:before{content:"\f1cd"}.fa-hands:before,.fa-sign-language:before,.fa-signing:before{content:"\f2a7"}.fa-calendar-day:before{content:"\f783"}.fa-ladder-water:before,.fa-swimming-pool:before,.fa-water-ladder:before{content:"\f5c5"}.fa-arrows-up-down:before,.fa-arrows-v:before{content:"\f07d"}.fa-face-grimace:before,.fa-grimace:before{content:"\f57f"}.fa-wheelchair-alt:before,.fa-wheelchair-move:before{content:"\e2ce"}.fa-level-down-alt:before,.fa-turn-down:before{content:"\f3be"}.fa-person-walking-arrow-right:before{content:"\e552"}.fa-envelope-square:before,.fa-square-envelope:before{content:"\f199"}.fa-dice:before{content:"\f522"}.fa-bowling-ball:before{content:"\f436"}.fa-brain:before{content:"\f5dc"}.fa-band-aid:before,.fa-bandage:before{content:"\f462"}.fa-calendar-minus:before{content:"\f272"}.fa-circle-xmark:before,.fa-times-circle:before,.fa-xmark-circle:before{content:"\f057"}.fa-gifts:before{content:"\f79c"}.fa-hotel:before{content:"\f594"}.fa-earth-asia:before,.fa-globe-asia:before{content:"\f57e"}.fa-id-card-alt:before,.fa-id-card-clip:before{content:"\f47f"}.fa-magnifying-glass-plus:before,.fa-search-plus:before{content:"\f00e"}.fa-thumbs-up:before{content:"\f164"}.fa-user-clock:before{content:"\f4fd"}.fa-allergies:before,.fa-hand-dots:before{content:"\f461"}.fa-file-invoice:before{content:"\f570"}.fa-window-minimize:before{content:"\f2d1"}.fa-coffee:before,.fa-mug-saucer:before{content:"\f0f4"}.fa-brush:before{content:"\f55d"}.fa-mask:before{content:"\f6fa"}.fa-magnifying-glass-minus:before,.fa-search-minus:before{content:"\f010"}.fa-ruler-vertical:before{content:"\f548"}.fa-user-alt:before,.fa-user-large:before{content:"\f406"}.fa-train-tram:before{content:"\e5b4"}.fa-user-nurse:before{content:"\f82f"}.fa-syringe:before{content:"\f48e"}.fa-cloud-sun:before{content:"\f6c4"}.fa-stopwatch-20:before{content:"\e06f"}.fa-square-full:before{content:"\f45c"}.fa-magnet:before{content:"\f076"}.fa-jar:before{content:"\e516"}.fa-note-sticky:before,.fa-sticky-note:before{content:"\f249"}.fa-bug-slash:before{content:"\e490"}.fa-arrow-up-from-water-pump:before{content:"\e4b6"}.fa-bone:before{content:"\f5d7"}.fa-user-injured:before{content:"\f728"}.fa-face-sad-tear:before,.fa-sad-tear:before{content:"\f5b4"}.fa-plane:before{content:"\f072"}.fa-tent-arrows-down:before{content:"\e581"}.fa-exclamation:before{content:"\21"}.fa-arrows-spin:before{content:"\e4bb"}.fa-print:before{content:"\f02f"}.fa-try:before,.fa-turkish-lira-sign:before,.fa-turkish-lira:before{content:"\e2bb"}.fa-dollar-sign:before,.fa-dollar:before,.fa-usd:before{content:"\24"}.fa-x:before{content:"\58"}.fa-magnifying-glass-dollar:before,.fa-search-dollar:before{content:"\f688"}.fa-users-cog:before,.fa-users-gear:before{content:"\f509"}.fa-person-military-pointing:before{content:"\e54a"}.fa-bank:before,.fa-building-columns:before,.fa-institution:before,.fa-museum:before,.fa-university:before{content:"\f19c"}.fa-umbrella:before{content:"\f0e9"}.fa-trowel:before{content:"\e589"}.fa-d:before{content:"\44"}.fa-stapler:before{content:"\e5af"}.fa-masks-theater:before,.fa-theater-masks:before{content:"\f630"}.fa-kip-sign:before{content:"\e1c4"}.fa-hand-point-left:before{content:"\f0a5"}.fa-handshake-alt:before,.fa-handshake-simple:before{content:"\f4c6"}.fa-fighter-jet:before,.fa-jet-fighter:before{content:"\f0fb"}.fa-share-alt-square:before,.fa-square-share-nodes:before{content:"\f1e1"}.fa-barcode:before{content:"\f02a"}.fa-plus-minus:before{content:"\e43c"}.fa-video-camera:before,.fa-video:before{content:"\f03d"}.fa-graduation-cap:before,.fa-mortar-board:before{content:"\f19d"}.fa-hand-holding-medical:before{content:"\e05c"}.fa-person-circle-check:before{content:"\e53e"}.fa-level-up-alt:before,.fa-turn-up:before{content:"\f3bf"} +.fa-sr-only,.fa-sr-only-focusable:not(:focus),.sr-only,.sr-only-focusable:not(:focus){position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}:host,:root{--fa-style-family-brands:"Font Awesome 6 Brands";--fa-font-brands:normal 400 1em/1 "Font Awesome 6 Brands"}@font-face{font-family:"Font Awesome 6 Brands";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.ttf) format("truetype")}.fa-brands,.fab{font-weight:400}.fa-monero:before{content:"\f3d0"}.fa-hooli:before{content:"\f427"}.fa-yelp:before{content:"\f1e9"}.fa-cc-visa:before{content:"\f1f0"}.fa-lastfm:before{content:"\f202"}.fa-shopware:before{content:"\f5b5"}.fa-creative-commons-nc:before{content:"\f4e8"}.fa-aws:before{content:"\f375"}.fa-redhat:before{content:"\f7bc"}.fa-yoast:before{content:"\f2b1"}.fa-cloudflare:before{content:"\e07d"}.fa-ups:before{content:"\f7e0"}.fa-pixiv:before{content:"\e640"}.fa-wpexplorer:before{content:"\f2de"}.fa-dyalog:before{content:"\f399"}.fa-bity:before{content:"\f37a"}.fa-stackpath:before{content:"\f842"}.fa-buysellads:before{content:"\f20d"}.fa-first-order:before{content:"\f2b0"}.fa-modx:before{content:"\f285"}.fa-guilded:before{content:"\e07e"}.fa-vnv:before{content:"\f40b"}.fa-js-square:before,.fa-square-js:before{content:"\f3b9"}.fa-microsoft:before{content:"\f3ca"}.fa-qq:before{content:"\f1d6"}.fa-orcid:before{content:"\f8d2"}.fa-java:before{content:"\f4e4"}.fa-invision:before{content:"\f7b0"}.fa-creative-commons-pd-alt:before{content:"\f4ed"}.fa-centercode:before{content:"\f380"}.fa-glide-g:before{content:"\f2a6"}.fa-drupal:before{content:"\f1a9"}.fa-hire-a-helper:before{content:"\f3b0"}.fa-creative-commons-by:before{content:"\f4e7"}.fa-unity:before{content:"\e049"}.fa-whmcs:before{content:"\f40d"}.fa-rocketchat:before{content:"\f3e8"}.fa-vk:before{content:"\f189"}.fa-untappd:before{content:"\f405"}.fa-mailchimp:before{content:"\f59e"}.fa-css3-alt:before{content:"\f38b"}.fa-reddit-square:before,.fa-square-reddit:before{content:"\f1a2"}.fa-vimeo-v:before{content:"\f27d"}.fa-contao:before{content:"\f26d"}.fa-square-font-awesome:before{content:"\e5ad"}.fa-deskpro:before{content:"\f38f"}.fa-brave:before{content:"\e63c"}.fa-sistrix:before{content:"\f3ee"}.fa-instagram-square:before,.fa-square-instagram:before{content:"\e055"}.fa-battle-net:before{content:"\f835"}.fa-the-red-yeti:before{content:"\f69d"}.fa-hacker-news-square:before,.fa-square-hacker-news:before{content:"\f3af"}.fa-edge:before{content:"\f282"}.fa-threads:before{content:"\e618"}.fa-napster:before{content:"\f3d2"}.fa-snapchat-square:before,.fa-square-snapchat:before{content:"\f2ad"}.fa-google-plus-g:before{content:"\f0d5"}.fa-artstation:before{content:"\f77a"}.fa-markdown:before{content:"\f60f"}.fa-sourcetree:before{content:"\f7d3"}.fa-google-plus:before{content:"\f2b3"}.fa-diaspora:before{content:"\f791"}.fa-foursquare:before{content:"\f180"}.fa-stack-overflow:before{content:"\f16c"}.fa-github-alt:before{content:"\f113"}.fa-phoenix-squadron:before{content:"\f511"}.fa-pagelines:before{content:"\f18c"}.fa-algolia:before{content:"\f36c"}.fa-red-river:before{content:"\f3e3"}.fa-creative-commons-sa:before{content:"\f4ef"}.fa-safari:before{content:"\f267"}.fa-google:before{content:"\f1a0"}.fa-font-awesome-alt:before,.fa-square-font-awesome-stroke:before{content:"\f35c"}.fa-atlassian:before{content:"\f77b"}.fa-linkedin-in:before{content:"\f0e1"}.fa-digital-ocean:before{content:"\f391"}.fa-nimblr:before{content:"\f5a8"}.fa-chromecast:before{content:"\f838"}.fa-evernote:before{content:"\f839"}.fa-hacker-news:before{content:"\f1d4"}.fa-creative-commons-sampling:before{content:"\f4f0"}.fa-adversal:before{content:"\f36a"}.fa-creative-commons:before{content:"\f25e"}.fa-watchman-monitoring:before{content:"\e087"}.fa-fonticons:before{content:"\f280"}.fa-weixin:before{content:"\f1d7"}.fa-shirtsinbulk:before{content:"\f214"}.fa-codepen:before{content:"\f1cb"}.fa-git-alt:before{content:"\f841"}.fa-lyft:before{content:"\f3c3"}.fa-rev:before{content:"\f5b2"}.fa-windows:before{content:"\f17a"}.fa-wizards-of-the-coast:before{content:"\f730"}.fa-square-viadeo:before,.fa-viadeo-square:before{content:"\f2aa"}.fa-meetup:before{content:"\f2e0"}.fa-centos:before{content:"\f789"}.fa-adn:before{content:"\f170"}.fa-cloudsmith:before{content:"\f384"}.fa-opensuse:before{content:"\e62b"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-dribbble-square:before,.fa-square-dribbble:before{content:"\f397"}.fa-codiepie:before{content:"\f284"}.fa-node:before{content:"\f419"}.fa-mix:before{content:"\f3cb"}.fa-steam:before{content:"\f1b6"}.fa-cc-apple-pay:before{content:"\f416"}.fa-scribd:before{content:"\f28a"}.fa-debian:before{content:"\e60b"}.fa-openid:before{content:"\f19b"}.fa-instalod:before{content:"\e081"}.fa-expeditedssl:before{content:"\f23e"}.fa-sellcast:before{content:"\f2da"}.fa-square-twitter:before,.fa-twitter-square:before{content:"\f081"}.fa-r-project:before{content:"\f4f7"}.fa-delicious:before{content:"\f1a5"}.fa-freebsd:before{content:"\f3a4"}.fa-vuejs:before{content:"\f41f"}.fa-accusoft:before{content:"\f369"}.fa-ioxhost:before{content:"\f208"}.fa-fonticons-fi:before{content:"\f3a2"}.fa-app-store:before{content:"\f36f"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-itunes-note:before{content:"\f3b5"}.fa-golang:before{content:"\e40f"}.fa-kickstarter:before{content:"\f3bb"}.fa-grav:before{content:"\f2d6"}.fa-weibo:before{content:"\f18a"}.fa-uncharted:before{content:"\e084"}.fa-firstdraft:before{content:"\f3a1"}.fa-square-youtube:before,.fa-youtube-square:before{content:"\f431"}.fa-wikipedia-w:before{content:"\f266"}.fa-rendact:before,.fa-wpressr:before{content:"\f3e4"}.fa-angellist:before{content:"\f209"}.fa-galactic-republic:before{content:"\f50c"}.fa-nfc-directional:before{content:"\e530"}.fa-skype:before{content:"\f17e"}.fa-joget:before{content:"\f3b7"}.fa-fedora:before{content:"\f798"}.fa-stripe-s:before{content:"\f42a"}.fa-meta:before{content:"\e49b"}.fa-laravel:before{content:"\f3bd"}.fa-hotjar:before{content:"\f3b1"}.fa-bluetooth-b:before{content:"\f294"}.fa-square-letterboxd:before{content:"\e62e"}.fa-sticker-mule:before{content:"\f3f7"}.fa-creative-commons-zero:before{content:"\f4f3"}.fa-hips:before{content:"\f452"}.fa-behance:before{content:"\f1b4"}.fa-reddit:before{content:"\f1a1"}.fa-discord:before{content:"\f392"}.fa-chrome:before{content:"\f268"}.fa-app-store-ios:before{content:"\f370"}.fa-cc-discover:before{content:"\f1f2"}.fa-wpbeginner:before{content:"\f297"}.fa-confluence:before{content:"\f78d"}.fa-shoelace:before{content:"\e60c"}.fa-mdb:before{content:"\f8ca"}.fa-dochub:before{content:"\f394"}.fa-accessible-icon:before{content:"\f368"}.fa-ebay:before{content:"\f4f4"}.fa-amazon:before{content:"\f270"}.fa-unsplash:before{content:"\e07c"}.fa-yarn:before{content:"\f7e3"}.fa-square-steam:before,.fa-steam-square:before{content:"\f1b7"}.fa-500px:before{content:"\f26e"}.fa-square-vimeo:before,.fa-vimeo-square:before{content:"\f194"}.fa-asymmetrik:before{content:"\f372"}.fa-font-awesome-flag:before,.fa-font-awesome-logo-full:before,.fa-font-awesome:before{content:"\f2b4"}.fa-gratipay:before{content:"\f184"}.fa-apple:before{content:"\f179"}.fa-hive:before{content:"\e07f"}.fa-gitkraken:before{content:"\f3a6"}.fa-keybase:before{content:"\f4f5"}.fa-apple-pay:before{content:"\f415"}.fa-padlet:before{content:"\e4a0"}.fa-amazon-pay:before{content:"\f42c"}.fa-github-square:before,.fa-square-github:before{content:"\f092"}.fa-stumbleupon:before{content:"\f1a4"}.fa-fedex:before{content:"\f797"}.fa-phoenix-framework:before{content:"\f3dc"}.fa-shopify:before{content:"\e057"}.fa-neos:before{content:"\f612"}.fa-square-threads:before{content:"\e619"}.fa-hackerrank:before{content:"\f5f7"}.fa-researchgate:before{content:"\f4f8"}.fa-swift:before{content:"\f8e1"}.fa-angular:before{content:"\f420"}.fa-speakap:before{content:"\f3f3"}.fa-angrycreative:before{content:"\f36e"}.fa-y-combinator:before{content:"\f23b"}.fa-empire:before{content:"\f1d1"}.fa-envira:before{content:"\f299"}.fa-google-scholar:before{content:"\e63b"}.fa-gitlab-square:before,.fa-square-gitlab:before{content:"\e5ae"}.fa-studiovinari:before{content:"\f3f8"}.fa-pied-piper:before{content:"\f2ae"}.fa-wordpress:before{content:"\f19a"}.fa-product-hunt:before{content:"\f288"}.fa-firefox:before{content:"\f269"}.fa-linode:before{content:"\f2b8"}.fa-goodreads:before{content:"\f3a8"}.fa-odnoklassniki-square:before,.fa-square-odnoklassniki:before{content:"\f264"}.fa-jsfiddle:before{content:"\f1cc"}.fa-sith:before{content:"\f512"}.fa-themeisle:before{content:"\f2b2"}.fa-page4:before{content:"\f3d7"}.fa-hashnode:before{content:"\e499"}.fa-react:before{content:"\f41b"}.fa-cc-paypal:before{content:"\f1f4"}.fa-squarespace:before{content:"\f5be"}.fa-cc-stripe:before{content:"\f1f5"}.fa-creative-commons-share:before{content:"\f4f2"}.fa-bitcoin:before{content:"\f379"}.fa-keycdn:before{content:"\f3ba"}.fa-opera:before{content:"\f26a"}.fa-itch-io:before{content:"\f83a"}.fa-umbraco:before{content:"\f8e8"}.fa-galactic-senate:before{content:"\f50d"}.fa-ubuntu:before{content:"\f7df"}.fa-draft2digital:before{content:"\f396"}.fa-stripe:before{content:"\f429"}.fa-houzz:before{content:"\f27c"}.fa-gg:before{content:"\f260"}.fa-dhl:before{content:"\f790"}.fa-pinterest-square:before,.fa-square-pinterest:before{content:"\f0d3"}.fa-xing:before{content:"\f168"}.fa-blackberry:before{content:"\f37b"}.fa-creative-commons-pd:before{content:"\f4ec"}.fa-playstation:before{content:"\f3df"}.fa-quinscape:before{content:"\f459"}.fa-less:before{content:"\f41d"}.fa-blogger-b:before{content:"\f37d"}.fa-opencart:before{content:"\f23d"}.fa-vine:before{content:"\f1ca"}.fa-signal-messenger:before{content:"\e663"}.fa-paypal:before{content:"\f1ed"}.fa-gitlab:before{content:"\f296"}.fa-typo3:before{content:"\f42b"}.fa-reddit-alien:before{content:"\f281"}.fa-yahoo:before{content:"\f19e"}.fa-dailymotion:before{content:"\e052"}.fa-affiliatetheme:before{content:"\f36b"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-bootstrap:before{content:"\f836"}.fa-odnoklassniki:before{content:"\f263"}.fa-nfc-symbol:before{content:"\e531"}.fa-mintbit:before{content:"\e62f"}.fa-ethereum:before{content:"\f42e"}.fa-speaker-deck:before{content:"\f83c"}.fa-creative-commons-nc-eu:before{content:"\f4e9"}.fa-patreon:before{content:"\f3d9"}.fa-avianex:before{content:"\f374"}.fa-ello:before{content:"\f5f1"}.fa-gofore:before{content:"\f3a7"}.fa-bimobject:before{content:"\f378"}.fa-brave-reverse:before{content:"\e63d"}.fa-facebook-f:before{content:"\f39e"}.fa-google-plus-square:before,.fa-square-google-plus:before{content:"\f0d4"}.fa-mandalorian:before{content:"\f50f"}.fa-first-order-alt:before{content:"\f50a"}.fa-osi:before{content:"\f41a"}.fa-google-wallet:before{content:"\f1ee"}.fa-d-and-d-beyond:before{content:"\f6ca"}.fa-periscope:before{content:"\f3da"}.fa-fulcrum:before{content:"\f50b"}.fa-cloudscale:before{content:"\f383"}.fa-forumbee:before{content:"\f211"}.fa-mizuni:before{content:"\f3cc"}.fa-schlix:before{content:"\f3ea"}.fa-square-xing:before,.fa-xing-square:before{content:"\f169"}.fa-bandcamp:before{content:"\f2d5"}.fa-wpforms:before{content:"\f298"}.fa-cloudversify:before{content:"\f385"}.fa-usps:before{content:"\f7e1"}.fa-megaport:before{content:"\f5a3"}.fa-magento:before{content:"\f3c4"}.fa-spotify:before{content:"\f1bc"}.fa-optin-monster:before{content:"\f23c"}.fa-fly:before{content:"\f417"}.fa-aviato:before{content:"\f421"}.fa-itunes:before{content:"\f3b4"}.fa-cuttlefish:before{content:"\f38c"}.fa-blogger:before{content:"\f37c"}.fa-flickr:before{content:"\f16e"}.fa-viber:before{content:"\f409"}.fa-soundcloud:before{content:"\f1be"}.fa-digg:before{content:"\f1a6"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-letterboxd:before{content:"\e62d"}.fa-symfony:before{content:"\f83d"}.fa-maxcdn:before{content:"\f136"}.fa-etsy:before{content:"\f2d7"}.fa-facebook-messenger:before{content:"\f39f"}.fa-audible:before{content:"\f373"}.fa-think-peaks:before{content:"\f731"}.fa-bilibili:before{content:"\e3d9"}.fa-erlang:before{content:"\f39d"}.fa-x-twitter:before{content:"\e61b"}.fa-cotton-bureau:before{content:"\f89e"}.fa-dashcube:before{content:"\f210"}.fa-42-group:before,.fa-innosoft:before{content:"\e080"}.fa-stack-exchange:before{content:"\f18d"}.fa-elementor:before{content:"\f430"}.fa-pied-piper-square:before,.fa-square-pied-piper:before{content:"\e01e"}.fa-creative-commons-nd:before{content:"\f4eb"}.fa-palfed:before{content:"\f3d8"}.fa-superpowers:before{content:"\f2dd"}.fa-resolving:before{content:"\f3e7"}.fa-xbox:before{content:"\f412"}.fa-searchengin:before{content:"\f3eb"}.fa-tiktok:before{content:"\e07b"}.fa-facebook-square:before,.fa-square-facebook:before{content:"\f082"}.fa-renren:before{content:"\f18b"}.fa-linux:before{content:"\f17c"}.fa-glide:before{content:"\f2a5"}.fa-linkedin:before{content:"\f08c"}.fa-hubspot:before{content:"\f3b2"}.fa-deploydog:before{content:"\f38e"}.fa-twitch:before{content:"\f1e8"}.fa-ravelry:before{content:"\f2d9"}.fa-mixer:before{content:"\e056"}.fa-lastfm-square:before,.fa-square-lastfm:before{content:"\f203"}.fa-vimeo:before{content:"\f40a"}.fa-mendeley:before{content:"\f7b3"}.fa-uniregistry:before{content:"\f404"}.fa-figma:before{content:"\f799"}.fa-creative-commons-remix:before{content:"\f4ee"}.fa-cc-amazon-pay:before{content:"\f42d"}.fa-dropbox:before{content:"\f16b"}.fa-instagram:before{content:"\f16d"}.fa-cmplid:before{content:"\e360"}.fa-upwork:before{content:"\e641"}.fa-facebook:before{content:"\f09a"}.fa-gripfire:before{content:"\f3ac"}.fa-jedi-order:before{content:"\f50e"}.fa-uikit:before{content:"\f403"}.fa-fort-awesome-alt:before{content:"\f3a3"}.fa-phabricator:before{content:"\f3db"}.fa-ussunnah:before{content:"\f407"}.fa-earlybirds:before{content:"\f39a"}.fa-trade-federation:before{content:"\f513"}.fa-autoprefixer:before{content:"\f41c"}.fa-whatsapp:before{content:"\f232"}.fa-slideshare:before{content:"\f1e7"}.fa-google-play:before{content:"\f3ab"}.fa-viadeo:before{content:"\f2a9"}.fa-line:before{content:"\f3c0"}.fa-google-drive:before{content:"\f3aa"}.fa-servicestack:before{content:"\f3ec"}.fa-simplybuilt:before{content:"\f215"}.fa-bitbucket:before{content:"\f171"}.fa-imdb:before{content:"\f2d8"}.fa-deezer:before{content:"\e077"}.fa-raspberry-pi:before{content:"\f7bb"}.fa-jira:before{content:"\f7b1"}.fa-docker:before{content:"\f395"}.fa-screenpal:before{content:"\e570"}.fa-bluetooth:before{content:"\f293"}.fa-gitter:before{content:"\f426"}.fa-d-and-d:before{content:"\f38d"}.fa-microblog:before{content:"\e01a"}.fa-cc-diners-club:before{content:"\f24c"}.fa-gg-circle:before{content:"\f261"}.fa-pied-piper-hat:before{content:"\f4e5"}.fa-kickstarter-k:before{content:"\f3bc"}.fa-yandex:before{content:"\f413"}.fa-readme:before{content:"\f4d5"}.fa-html5:before{content:"\f13b"}.fa-sellsy:before{content:"\f213"}.fa-sass:before{content:"\f41e"}.fa-wirsindhandwerk:before,.fa-wsh:before{content:"\e2d0"}.fa-buromobelexperte:before{content:"\f37f"}.fa-salesforce:before{content:"\f83b"}.fa-octopus-deploy:before{content:"\e082"}.fa-medapps:before{content:"\f3c6"}.fa-ns8:before{content:"\f3d5"}.fa-pinterest-p:before{content:"\f231"}.fa-apper:before{content:"\f371"}.fa-fort-awesome:before{content:"\f286"}.fa-waze:before{content:"\f83f"}.fa-cc-jcb:before{content:"\f24b"}.fa-snapchat-ghost:before,.fa-snapchat:before{content:"\f2ab"}.fa-fantasy-flight-games:before{content:"\f6dc"}.fa-rust:before{content:"\e07a"}.fa-wix:before{content:"\f5cf"}.fa-behance-square:before,.fa-square-behance:before{content:"\f1b5"}.fa-supple:before{content:"\f3f9"}.fa-webflow:before{content:"\e65c"}.fa-rebel:before{content:"\f1d0"}.fa-css3:before{content:"\f13c"}.fa-staylinked:before{content:"\f3f5"}.fa-kaggle:before{content:"\f5fa"}.fa-space-awesome:before{content:"\e5ac"}.fa-deviantart:before{content:"\f1bd"}.fa-cpanel:before{content:"\f388"}.fa-goodreads-g:before{content:"\f3a9"}.fa-git-square:before,.fa-square-git:before{content:"\f1d2"}.fa-square-tumblr:before,.fa-tumblr-square:before{content:"\f174"}.fa-trello:before{content:"\f181"}.fa-creative-commons-nc-jp:before{content:"\f4ea"}.fa-get-pocket:before{content:"\f265"}.fa-perbyte:before{content:"\e083"}.fa-grunt:before{content:"\f3ad"}.fa-weebly:before{content:"\f5cc"}.fa-connectdevelop:before{content:"\f20e"}.fa-leanpub:before{content:"\f212"}.fa-black-tie:before{content:"\f27e"}.fa-themeco:before{content:"\f5c6"}.fa-python:before{content:"\f3e2"}.fa-android:before{content:"\f17b"}.fa-bots:before{content:"\e340"}.fa-free-code-camp:before{content:"\f2c5"}.fa-hornbill:before{content:"\f592"}.fa-js:before{content:"\f3b8"}.fa-ideal:before{content:"\e013"}.fa-git:before{content:"\f1d3"}.fa-dev:before{content:"\f6cc"}.fa-sketch:before{content:"\f7c6"}.fa-yandex-international:before{content:"\f414"}.fa-cc-amex:before{content:"\f1f3"}.fa-uber:before{content:"\f402"}.fa-github:before{content:"\f09b"}.fa-php:before{content:"\f457"}.fa-alipay:before{content:"\f642"}.fa-youtube:before{content:"\f167"}.fa-skyatlas:before{content:"\f216"}.fa-firefox-browser:before{content:"\e007"}.fa-replyd:before{content:"\f3e6"}.fa-suse:before{content:"\f7d6"}.fa-jenkins:before{content:"\f3b6"}.fa-twitter:before{content:"\f099"}.fa-rockrms:before{content:"\f3e9"}.fa-pinterest:before{content:"\f0d2"}.fa-buffer:before{content:"\f837"}.fa-npm:before{content:"\f3d4"}.fa-yammer:before{content:"\f840"}.fa-btc:before{content:"\f15a"}.fa-dribbble:before{content:"\f17d"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-internet-explorer:before{content:"\f26b"}.fa-stubber:before{content:"\e5c7"}.fa-telegram-plane:before,.fa-telegram:before{content:"\f2c6"}.fa-old-republic:before{content:"\f510"}.fa-odysee:before{content:"\e5c6"}.fa-square-whatsapp:before,.fa-whatsapp-square:before{content:"\f40c"}.fa-node-js:before{content:"\f3d3"}.fa-edge-legacy:before{content:"\e078"}.fa-slack-hash:before,.fa-slack:before{content:"\f198"}.fa-medrt:before{content:"\f3c8"}.fa-usb:before{content:"\f287"}.fa-tumblr:before{content:"\f173"}.fa-vaadin:before{content:"\f408"}.fa-quora:before{content:"\f2c4"}.fa-square-x-twitter:before{content:"\e61a"}.fa-reacteurope:before{content:"\f75d"}.fa-medium-m:before,.fa-medium:before{content:"\f23a"}.fa-amilia:before{content:"\f36d"}.fa-mixcloud:before{content:"\f289"}.fa-flipboard:before{content:"\f44d"}.fa-viacoin:before{content:"\f237"}.fa-critical-role:before{content:"\f6c9"}.fa-sitrox:before{content:"\e44a"}.fa-discourse:before{content:"\f393"}.fa-joomla:before{content:"\f1aa"}.fa-mastodon:before{content:"\f4f6"}.fa-airbnb:before{content:"\f834"}.fa-wolf-pack-battalion:before{content:"\f514"}.fa-buy-n-large:before{content:"\f8a6"}.fa-gulp:before{content:"\f3ae"}.fa-creative-commons-sampling-plus:before{content:"\f4f1"}.fa-strava:before{content:"\f428"}.fa-ember:before{content:"\f423"}.fa-canadian-maple-leaf:before{content:"\f785"}.fa-teamspeak:before{content:"\f4f9"}.fa-pushed:before{content:"\f3e1"}.fa-wordpress-simple:before{content:"\f411"}.fa-nutritionix:before{content:"\f3d6"}.fa-wodu:before{content:"\e088"}.fa-google-pay:before{content:"\e079"}.fa-intercom:before{content:"\f7af"}.fa-zhihu:before{content:"\f63f"}.fa-korvue:before{content:"\f42f"}.fa-pix:before{content:"\e43a"}.fa-steam-symbol:before{content:"\f3f6"}:host,:root{--fa-font-regular:normal 400 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.ttf) format("truetype")}.fa-regular,.far{font-weight:400}:host,:root{--fa-style-family-classic:"Font Awesome 6 Free";--fa-font-solid:normal 900 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:900;font-display:block;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}.fa-solid,.fas{font-weight:900}@font-face{font-family:"Font Awesome 5 Brands";font-display:block;font-weight:400;src:url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.ttf) format("truetype")}@font-face{font-family:"Font Awesome 5 Free";font-display:block;font-weight:900;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}@font-face{font-family:"Font Awesome 5 Free";font-display:block;font-weight:400;src:url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.ttf) format("truetype")}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.ttf) format("truetype")}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.ttf) format("truetype");unicode-range:u+f003,u+f006,u+f014,u+f016-f017,u+f01a-f01b,u+f01d,u+f022,u+f03e,u+f044,u+f046,u+f05c-f05d,u+f06e,u+f070,u+f087-f088,u+f08a,u+f094,u+f096-f097,u+f09d,u+f0a0,u+f0a2,u+f0a4-f0a7,u+f0c5,u+f0c7,u+f0e5-f0e6,u+f0eb,u+f0f6-f0f8,u+f10c,u+f114-f115,u+f118-f11a,u+f11c-f11d,u+f133,u+f147,u+f14e,u+f150-f152,u+f185-f186,u+f18e,u+f190-f192,u+f196,u+f1c1-f1c9,u+f1d9,u+f1db,u+f1e3,u+f1ea,u+f1f7,u+f1f9,u+f20a,u+f247-f248,u+f24a,u+f24d,u+f255-f25b,u+f25d,u+f271-f274,u+f278,u+f27b,u+f28c,u+f28e,u+f29c,u+f2b5,u+f2b7,u+f2ba,u+f2bc,u+f2be,u+f2c0-f2c1,u+f2c3,u+f2d0,u+f2d2,u+f2d4,u+f2dc}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-v4compatibility.woff2) format("woff2"),url(../webfonts/fa-v4compatibility.ttf) format("truetype");unicode-range:u+f041,u+f047,u+f065-f066,u+f07d-f07e,u+f080,u+f08b,u+f08e,u+f090,u+f09a,u+f0ac,u+f0ae,u+f0b2,u+f0d0,u+f0d6,u+f0e4,u+f0ec,u+f10a-f10b,u+f123,u+f13e,u+f148-f149,u+f14c,u+f156,u+f15e,u+f160-f161,u+f163,u+f175-f178,u+f195,u+f1f8,u+f219,u+f27a} \ No newline at end of file diff --git a/dejacode/static/fontawesome-free-6.5.1/webfonts/fa-brands-400.ttf b/dejacode/static/fontawesome-free-6.5.1/webfonts/fa-brands-400.ttf new file mode 100644 index 00000000..5efb1d4f Binary files /dev/null and b/dejacode/static/fontawesome-free-6.5.1/webfonts/fa-brands-400.ttf differ diff --git a/dejacode/static/fontawesome-free-6.5.1/webfonts/fa-brands-400.woff2 b/dejacode/static/fontawesome-free-6.5.1/webfonts/fa-brands-400.woff2 new file mode 100644 index 00000000..36fbda7d Binary files /dev/null and b/dejacode/static/fontawesome-free-6.5.1/webfonts/fa-brands-400.woff2 differ diff --git a/dejacode/static/fontawesome-free-6.5.1/webfonts/fa-regular-400.ttf b/dejacode/static/fontawesome-free-6.5.1/webfonts/fa-regular-400.ttf new file mode 100644 index 00000000..838b4e2c Binary files /dev/null and b/dejacode/static/fontawesome-free-6.5.1/webfonts/fa-regular-400.ttf differ diff --git a/dejacode/static/fontawesome-free-6.5.1/webfonts/fa-regular-400.woff2 b/dejacode/static/fontawesome-free-6.5.1/webfonts/fa-regular-400.woff2 new file mode 100644 index 00000000..b6cabbac Binary files /dev/null and b/dejacode/static/fontawesome-free-6.5.1/webfonts/fa-regular-400.woff2 differ diff --git a/dejacode/static/fontawesome-free-6.5.1/webfonts/fa-solid-900.ttf b/dejacode/static/fontawesome-free-6.5.1/webfonts/fa-solid-900.ttf new file mode 100644 index 00000000..ec24749d Binary files /dev/null and b/dejacode/static/fontawesome-free-6.5.1/webfonts/fa-solid-900.ttf differ diff --git a/dejacode/static/fontawesome-free-6.5.1/webfonts/fa-solid-900.woff2 b/dejacode/static/fontawesome-free-6.5.1/webfonts/fa-solid-900.woff2 new file mode 100644 index 00000000..824d518e Binary files /dev/null and b/dejacode/static/fontawesome-free-6.5.1/webfonts/fa-solid-900.woff2 differ diff --git a/dejacode/static/fontawesome-free-6.5.1/webfonts/fa-v4compatibility.ttf b/dejacode/static/fontawesome-free-6.5.1/webfonts/fa-v4compatibility.ttf new file mode 100644 index 00000000..b175aa8e Binary files /dev/null and b/dejacode/static/fontawesome-free-6.5.1/webfonts/fa-v4compatibility.ttf differ diff --git a/dejacode/static/fontawesome-free-6.5.1/webfonts/fa-v4compatibility.woff2 b/dejacode/static/fontawesome-free-6.5.1/webfonts/fa-v4compatibility.woff2 new file mode 100644 index 00000000..e09b5a55 Binary files /dev/null and b/dejacode/static/fontawesome-free-6.5.1/webfonts/fa-v4compatibility.woff2 differ diff --git a/dejacode/static/img/dejacode_logo_small.png b/dejacode/static/img/dejacode_logo_small.png new file mode 100644 index 00000000..084cc979 Binary files /dev/null and b/dejacode/static/img/dejacode_logo_small.png differ diff --git a/dejacode/static/img/favicon.ico b/dejacode/static/img/favicon.ico new file mode 100644 index 00000000..8206ca78 Binary files /dev/null and b/dejacode/static/img/favicon.ico differ diff --git a/dejacode/static/img/favicon_v2.ico b/dejacode/static/img/favicon_v2.ico new file mode 100644 index 00000000..85b13248 Binary files /dev/null and b/dejacode/static/img/favicon_v2.ico differ diff --git a/dejacode/static/img/icon-chevron-down.png b/dejacode/static/img/icon-chevron-down.png new file mode 100644 index 00000000..36963cf0 Binary files /dev/null and b/dejacode/static/img/icon-chevron-down.png differ diff --git a/dejacode/static/img/icon-chevron-up.png b/dejacode/static/img/icon-chevron-up.png new file mode 100644 index 00000000..a150ef56 Binary files /dev/null and b/dejacode/static/img/icon-chevron-up.png differ diff --git a/dejacode/static/img/icon-hierarchy-hover.png b/dejacode/static/img/icon-hierarchy-hover.png new file mode 100644 index 00000000..017b94c6 Binary files /dev/null and b/dejacode/static/img/icon-hierarchy-hover.png differ diff --git a/dejacode/static/img/icon-hierarchy.png b/dejacode/static/img/icon-hierarchy.png new file mode 100644 index 00000000..159be99e Binary files /dev/null and b/dejacode/static/img/icon-hierarchy.png differ diff --git a/dejacode/static/img/icon-no-gray.png b/dejacode/static/img/icon-no-gray.png new file mode 100644 index 00000000..390f2372 Binary files /dev/null and b/dejacode/static/img/icon-no-gray.png differ diff --git a/dejacode/static/img/icon-no.png b/dejacode/static/img/icon-no.png new file mode 100644 index 00000000..390f2372 Binary files /dev/null and b/dejacode/static/img/icon-no.png differ diff --git a/dejacode/static/img/icon-related-lookup-m2m-hover.png b/dejacode/static/img/icon-related-lookup-m2m-hover.png new file mode 100644 index 00000000..6f816a4f Binary files /dev/null and b/dejacode/static/img/icon-related-lookup-m2m-hover.png differ diff --git a/dejacode/static/img/icon-related-lookup-m2m.png b/dejacode/static/img/icon-related-lookup-m2m.png new file mode 100644 index 00000000..1062a58a Binary files /dev/null and b/dejacode/static/img/icon-related-lookup-m2m.png differ diff --git a/dejacode/static/img/icon-suitcase.png b/dejacode/static/img/icon-suitcase.png new file mode 100644 index 00000000..81560d77 Binary files /dev/null and b/dejacode/static/img/icon-suitcase.png differ diff --git a/dejacode/static/img/icon-trash-list-toggle-handler-hover.png b/dejacode/static/img/icon-trash-list-toggle-handler-hover.png new file mode 100644 index 00000000..22057833 Binary files /dev/null and b/dejacode/static/img/icon-trash-list-toggle-handler-hover.png differ diff --git a/dejacode/static/img/icon-trash-list-toggle-handler.png b/dejacode/static/img/icon-trash-list-toggle-handler.png new file mode 100644 index 00000000..81593993 Binary files /dev/null and b/dejacode/static/img/icon-trash-list-toggle-handler.png differ diff --git a/dejacode/static/img/icon-unknown.png b/dejacode/static/img/icon-unknown.png new file mode 100644 index 00000000..0b7d1809 Binary files /dev/null and b/dejacode/static/img/icon-unknown.png differ diff --git a/dejacode/static/img/icon-yes.png b/dejacode/static/img/icon-yes.png new file mode 100644 index 00000000..2f140794 Binary files /dev/null and b/dejacode/static/img/icon-yes.png differ diff --git a/dejacode/static/img/logo_django_13110.png b/dejacode/static/img/logo_django_13110.png new file mode 100644 index 00000000..f55ea4f8 Binary files /dev/null and b/dejacode/static/img/logo_django_13110.png differ diff --git a/dejacode/static/img/logo_postgres.png b/dejacode/static/img/logo_postgres.png new file mode 100644 index 00000000..2a26bce4 Binary files /dev/null and b/dejacode/static/img/logo_postgres.png differ diff --git a/dejacode/static/img/logo_python.png b/dejacode/static/img/logo_python.png new file mode 100644 index 00000000..b156c381 Binary files /dev/null and b/dejacode/static/img/logo_python.png differ diff --git a/dejacode/static/img/request_icon.png b/dejacode/static/img/request_icon.png new file mode 100644 index 00000000..615dd361 Binary files /dev/null and b/dejacode/static/img/request_icon.png differ diff --git a/dejacode/static/js/annotator/README.txt b/dejacode/static/js/annotator/README.txt new file mode 100644 index 00000000..504fee83 --- /dev/null +++ b/dejacode/static/js/annotator/README.txt @@ -0,0 +1,14 @@ +The following includes were commented for security reason, as it's loading +external resources. +The only purpose of those is for compatibility with some old IE version (<=8) +Our Annotator usage was tested fine without this in: +Chrome, Firefox, Opera, Safari, IE9+. +Note that we could consider to serve those external libraries ourself but we'll +have to consider the lgpl 3.0 license for xpath.min.js +See https://github.com/okfn/annotator/issues/85 + +Removed parts: + +``` +if(((_ref1=g.document)!=null?_ref1.evaluate:void 0)==null){$.getScript("http://assets.annotateit.org/vendor/xpath.min.js")}if(g.getSelection==null){$.getScript("http://assets.annotateit.org/vendor/ierange.min.js")}if(g.JSON==null){$.getScript("http://assets.annotateit.org/vendor/json2.min.js")} +``` \ No newline at end of file diff --git a/dejacode/static/js/annotator/annotator-full.1.2.10.min.js b/dejacode/static/js/annotator/annotator-full.1.2.10.min.js new file mode 100644 index 00000000..8c6e18ce --- /dev/null +++ b/dejacode/static/js/annotator/annotator-full.1.2.10.min.js @@ -0,0 +1,20 @@ + +/* +** Annotator v1.2.10 +** https://github.com/okfn/annotator/ +** +** Copyright 2015, the Annotator project contributors. +** Dual licensed under the MIT and GPLv3 licenses. +** https://github.com/okfn/annotator/blob/master/LICENSE +** +** Built at: 2015-02-26 03:26:47Z + */ + + +// + +!function(){var $,Annotator,Delegator,LinkParser,Range,Util,base64Decode,base64UrlDecode,createDateFromISO8601,findChild,fn,functions,g,getNodeName,getNodePosition,gettext,parseToken,simpleXPathJQuery,simpleXPathPure,_Annotator,_gettext,_i,_j,_len,_len1,_ref,_ref1,_ref2,_ref3,_ref4,_t,__slice=[].slice,__hasProp={}.hasOwnProperty,__extends=function(child,parent){for(var key in parent){if(__hasProp.call(parent,key))child[key]=parent[key]}function ctor(){this.constructor=child}ctor.prototype=parent.prototype;child.prototype=new ctor;child.__super__=parent.prototype;return child},__bind=function(fn,me){return function(){return fn.apply(me,arguments)}},__indexOf=[].indexOf||function(item){for(var i=0,l=this.length;i/g,">").replace(/"/g,""")};Util.uuid=function(){var counter;counter=0;return function(){return counter++}}();Util.getGlobal=function(){return function(){return this}()};Util.maxZIndex=function($elements){var all,el;all=function(){var _i,_len,_results;_results=[];for(_i=0,_len=$elements.length;_i<_len;_i++){el=$elements[_i];if($(el).css("position")==="static"){_results.push(-1)}else{_results.push(parseFloat($(el).css("z-index"))||-1)}}return _results}();return Math.max.apply(Math,all)};Util.mousePosition=function(e,offsetEl){var offset,_ref1;if((_ref1=$(offsetEl).css("position"))!=="absolute"&&_ref1!=="fixed"&&_ref1!=="relative"){offsetEl=$(offsetEl).offsetParent()[0]}offset=$(offsetEl).offset();return{top:e.pageY-offset.top,left:e.pageX-offset.left}};Util.preventEventDefault=function(event){return event!=null?typeof event.preventDefault==="function"?event.preventDefault():void 0:void 0};functions=["log","debug","info","warn","exception","assert","dir","dirxml","trace","group","groupEnd","groupCollapsed","time","timeEnd","profile","profileEnd","count","clear","table","error","notifyFirebug","firebug","userObjects"];if(typeof console!=="undefined"&&console!==null){if(console.group==null){console.group=function(name){return console.log("GROUP: ",name)}}if(console.groupCollapsed==null){console.groupCollapsed=console.group}for(_i=0,_len=functions.length;_i<_len;_i++){fn=functions[_i];if(console[fn]==null){console[fn]=function(){return console.log(_t("Not implemented:")+(" console."+name))}}}}else{this.console={};for(_j=0,_len1=functions.length;_j<_len1;_j++){fn=functions[_j];this.console[fn]=function(){}}this.console["error"]=function(){var args;args=1<=arguments.length?__slice.call(arguments,0):[];return alert("ERROR: "+args.join(", "))};this.console["warn"]=function(){var args;args=1<=arguments.length?__slice.call(arguments,0):[];return alert("WARNING: "+args.join(", "))}}Delegator=function(){Delegator.prototype.events={};Delegator.prototype.options={};Delegator.prototype.element=null;function Delegator(element,options){this.options=$.extend(true,{},this.options,options);this.element=$(element);this._closures={};this.on=this.subscribe;this.addEvents()}Delegator.prototype.destroy=function(){return this.removeEvents()};Delegator.prototype.addEvents=function(){var event,_k,_len2,_ref1,_results;_ref1=Delegator._parseEvents(this.events);_results=[];for(_k=0,_len2=_ref1.length;_k<_len2;_k++){event=_ref1[_k];_results.push(this._addEvent(event.selector,event.event,event.functionName))}return _results};Delegator.prototype.removeEvents=function(){var event,_k,_len2,_ref1,_results;_ref1=Delegator._parseEvents(this.events);_results=[];for(_k=0,_len2=_ref1.length;_k<_len2;_k++){event=_ref1[_k];_results.push(this._removeEvent(event.selector,event.event,event.functionName))}return _results};Delegator.prototype._addEvent=function(selector,event,functionName){var closure,_this=this;closure=function(){return _this[functionName].apply(_this,arguments)};if(selector===""&&Delegator._isCustomEvent(event)){this.subscribe(event,closure)}else{this.element.delegate(selector,event,closure)}this._closures[""+selector+"/"+event+"/"+functionName]=closure;return this};Delegator.prototype._removeEvent=function(selector,event,functionName){var closure;closure=this._closures[""+selector+"/"+event+"/"+functionName];if(selector===""&&Delegator._isCustomEvent(event)){this.unsubscribe(event,closure)}else{this.element.undelegate(selector,event,closure)}delete this._closures[""+selector+"/"+event+"/"+functionName];return this};Delegator.prototype.publish=function(){this.element.triggerHandler.apply(this.element,arguments);return this};Delegator.prototype.subscribe=function(event,callback){var closure;closure=function(){return callback.apply(this,[].slice.call(arguments,1))};closure.guid=callback.guid=$.guid+=1;this.element.bind(event,closure);return this};Delegator.prototype.unsubscribe=function(){this.element.unbind.apply(this.element,arguments);return this};return Delegator}();Delegator._parseEvents=function(eventsObj){var event,events,functionName,sel,selector,_k,_ref1;events=[];for(sel in eventsObj){functionName=eventsObj[sel];_ref1=sel.split(" "),selector=2<=_ref1.length?__slice.call(_ref1,0,_k=_ref1.length-1):(_k=0,[]),event=_ref1[_k++];events.push({selector:selector.join(" "),event:event,functionName:functionName})}return events};Delegator.natives=function(){var key,specials,val;specials=function(){var _ref1,_results;_ref1=jQuery.event.special;_results=[];for(key in _ref1){if(!__hasProp.call(_ref1,key))continue;val=_ref1[key];_results.push(key)}return _results}();return"blur focus focusin focusout load resize scroll unload click dblclick\nmousedown mouseup mousemove mouseover mouseout mouseenter mouseleave\nchange select submit keydown keypress keyup error".split(/[^a-z]+/).concat(specials)}();Delegator._isCustomEvent=function(event){event=event.split(".")[0];return $.inArray(event,Delegator.natives)===-1};Range={};Range.sniff=function(r){if(r.commonAncestorContainer!=null){return new Range.BrowserRange(r)}else if(typeof r.start==="string"){return new Range.SerializedRange(r)}else if(r.start&&typeof r.start==="object"){return new Range.NormalizedRange(r)}else{console.error(_t("Could not sniff range type"));return false}};Range.nodeFromXPath=function(xpath,root){var customResolver,evaluateXPath,namespace,node,segment;if(root==null){root=document}evaluateXPath=function(xp,nsResolver){var exception;if(nsResolver==null){nsResolver=null}try{return document.evaluate("."+xp,root,nsResolver,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue}catch(_error){exception=_error;console.log("XPath evaluation failed.");console.log("Trying fallback...");return Util.nodeFromXPath(xp,root)}};if(!$.isXMLDoc(document.documentElement)){return evaluateXPath(xpath)}else{customResolver=document.createNSResolver(document.ownerDocument===null?document.documentElement:document.ownerDocument.documentElement);node=evaluateXPath(xpath,customResolver);if(!node){xpath=function(){var _k,_len2,_ref1,_results;_ref1=xpath.split("/");_results=[];for(_k=0,_len2=_ref1.length;_k<_len2;_k++){segment=_ref1[_k];if(segment&&segment.indexOf(":")===-1){_results.push(segment.replace(/^([a-z]+)/,"xhtml:$1"))}else{_results.push(segment)}}return _results}().join("/");namespace=document.lookupNamespaceURI(null);customResolver=function(ns){if(ns==="xhtml"){return namespace}else{return document.documentElement.getAttribute("xmlns:"+ns)}};node=evaluateXPath(xpath,customResolver)}return node}};Range.RangeError=function(_super){__extends(RangeError,_super);function RangeError(type,message,parent){this.type=type;this.message=message;this.parent=parent!=null?parent:null;RangeError.__super__.constructor.call(this,this.message)}return RangeError}(Error);Range.BrowserRange=function(){function BrowserRange(obj){this.commonAncestorContainer=obj.commonAncestorContainer;this.startContainer=obj.startContainer;this.startOffset=obj.startOffset;this.endContainer=obj.endContainer;this.endOffset=obj.endOffset}BrowserRange.prototype.normalize=function(root){var n,node,nr,r;if(this.tainted){console.error(_t("You may only call normalize() once on a BrowserRange!"));return false}else{this.tainted=true}r={};if(this.startContainer.nodeType===Node.ELEMENT_NODE){r.start=Util.getFirstTextNodeNotBefore(this.startContainer.childNodes[this.startOffset]);r.startOffset=0}else{r.start=this.startContainer;r.startOffset=this.startOffset}if(this.endContainer.nodeType===Node.ELEMENT_NODE){node=this.endContainer.childNodes[this.endOffset];if(node!=null){n=node;while(n!=null&&n.nodeType!==Node.TEXT_NODE){n=n.firstChild}if(n!=null){r.end=n;r.endOffset=0}}if(r.end==null){node=this.endContainer.childNodes[this.endOffset-1];r.end=Util.getLastTextNodeUpTo(node);r.endOffset=r.end.nodeValue.length}}else{r.end=this.endContainer;r.endOffset=this.endOffset}nr={};if(r.startOffset>0){if(r.start.nodeValue.length>r.startOffset){nr.start=r.start.splitText(r.startOffset)}else{nr.start=r.start.nextSibling}}else{nr.start=r.start}if(r.start===r.end){if(nr.start.nodeValue.length>r.endOffset-r.startOffset){nr.start.splitText(r.endOffset-r.startOffset)}nr.end=nr.start}else{if(r.end.nodeValue.length>r.endOffset){r.end.splitText(r.endOffset)}nr.end=r.end}nr.commonAncestor=this.commonAncestorContainer;while(nr.commonAncestor.nodeType!==Node.ELEMENT_NODE){nr.commonAncestor=nr.commonAncestor.parentNode}return new Range.NormalizedRange(nr)};BrowserRange.prototype.serialize=function(root,ignoreSelector){return this.normalize(root).serialize(root,ignoreSelector)};return BrowserRange}();Range.NormalizedRange=function(){function NormalizedRange(obj){this.commonAncestor=obj.commonAncestor;this.start=obj.start;this.end=obj.end}NormalizedRange.prototype.normalize=function(root){return this};NormalizedRange.prototype.limit=function(bounds){var nodes,parent,startParents,_k,_len2,_ref1;nodes=$.grep(this.textNodes(),function(node){return node.parentNode===bounds||$.contains(bounds,node.parentNode)});if(!nodes.length){return null}this.start=nodes[0];this.end=nodes[nodes.length-1];startParents=$(this.start).parents();_ref1=$(this.end).parents();for(_k=0,_len2=_ref1.length;_k<_len2;_k++){parent=_ref1[_k];if(startParents.index(parent)!==-1){this.commonAncestor=parent;break}}return this};NormalizedRange.prototype.serialize=function(root,ignoreSelector){var end,serialization,start;serialization=function(node,isEnd){var n,nodes,offset,origParent,textNodes,xpath,_k,_len2;if(ignoreSelector){origParent=$(node).parents(":not("+ignoreSelector+")").eq(0)}else{origParent=$(node).parent()}xpath=Util.xpathFromNode(origParent,root)[0];textNodes=Util.getTextNodes(origParent);nodes=textNodes.slice(0,textNodes.index(node));offset=0;for(_k=0,_len2=nodes.length;_k<_len2;_k++){n=nodes[_k];offset+=n.nodeValue.length}if(isEnd){return[xpath,offset+node.nodeValue.length]}else{return[xpath,offset]}};start=serialization(this.start);end=serialization(this.end,true);return new Range.SerializedRange({start:start[0],end:end[0],startOffset:start[1],endOffset:end[1]})};NormalizedRange.prototype.text=function(){var node;return function(){var _k,_len2,_ref1,_results;_ref1=this.textNodes();_results=[];for(_k=0,_len2=_ref1.length;_k<_len2;_k++){node=_ref1[_k];_results.push(node.nodeValue)}return _results}.call(this).join("")};NormalizedRange.prototype.textNodes=function(){var end,start,textNodes,_ref1;textNodes=Util.getTextNodes($(this.commonAncestor));_ref1=[textNodes.index(this.start),textNodes.index(this.end)],start=_ref1[0],end=_ref1[1];return $.makeArray(textNodes.slice(start,+end+1||9e9))};NormalizedRange.prototype.toRange=function(){var range;range=document.createRange();range.setStartBefore(this.start);range.setEndAfter(this.end);return range};return NormalizedRange}();Range.SerializedRange=function(){function SerializedRange(obj){this.start=obj.start;this.startOffset=obj.startOffset;this.end=obj.end;this.endOffset=obj.endOffset}SerializedRange.prototype.normalize=function(root){var contains,e,length,node,p,range,targetOffset,tn,_k,_l,_len2,_len3,_ref1,_ref2;range={};_ref1=["start","end"];for(_k=0,_len2=_ref1.length;_k<_len2;_k++){p=_ref1[_k];try{node=Range.nodeFromXPath(this[p],root)}catch(_error){e=_error;throw new Range.RangeError(p,"Error while finding "+p+" node: "+this[p]+": "+e,e)}if(!node){throw new Range.RangeError(p,"Couldn't find "+p+" node: "+this[p])}length=0;targetOffset=this[p+"Offset"];if(p==="end"){targetOffset--}_ref2=Util.getTextNodes($(node));for(_l=0,_len3=_ref2.length;_l<_len3;_l++){tn=_ref2[_l];if(length+tn.nodeValue.length>targetOffset){range[p+"Container"]=tn;range[p+"Offset"]=this[p+"Offset"]-length;break}else{length+=tn.nodeValue.length}}if(range[p+"Offset"]==null){throw new Range.RangeError(""+p+"offset","Couldn't find offset "+this[p+"Offset"]+" in element "+this[p])}}contains=document.compareDocumentPosition==null?function(a,b){return a.contains(b)}:function(a,b){return a.compareDocumentPosition(b)&16};$(range.startContainer).parents().each(function(){if(contains(this,range.endContainer)){range.commonAncestorContainer=this;return false}});return new Range.BrowserRange(range).normalize(root)};SerializedRange.prototype.serialize=function(root,ignoreSelector){return this.normalize(root).serialize(root,ignoreSelector)};SerializedRange.prototype.toObject=function(){return{start:this.start,startOffset:this.startOffset,end:this.end,endOffset:this.endOffset}};return SerializedRange}();_Annotator=this.Annotator;Annotator=function(_super){__extends(Annotator,_super);Annotator.prototype.events={".annotator-adder button click":"onAdderClick",".annotator-adder button mousedown":"onAdderMousedown",".annotator-hl mouseover":"onHighlightMouseover",".annotator-hl mouseout":"startViewerHideTimer"};Annotator.prototype.html={adder:'
        ",wrapper:'
        '};Annotator.prototype.options={readOnly:false};Annotator.prototype.plugins={};Annotator.prototype.editor=null;Annotator.prototype.viewer=null;Annotator.prototype.selectedRanges=null;Annotator.prototype.mouseIsDown=false;Annotator.prototype.ignoreMouseup=false;Annotator.prototype.viewerHideTimer=null;function Annotator(element,options){this.onDeleteAnnotation=__bind(this.onDeleteAnnotation,this);this.onEditAnnotation=__bind(this.onEditAnnotation,this);this.onAdderClick=__bind(this.onAdderClick,this);this.onAdderMousedown=__bind(this.onAdderMousedown,this);this.onHighlightMouseover=__bind(this.onHighlightMouseover,this);this.checkForEndSelection=__bind(this.checkForEndSelection,this);this.checkForStartSelection=__bind(this.checkForStartSelection,this);this.clearViewerHideTimer=__bind(this.clearViewerHideTimer,this);this.startViewerHideTimer=__bind(this.startViewerHideTimer,this);this.showViewer=__bind(this.showViewer,this);this.onEditorSubmit=__bind(this.onEditorSubmit,this);this.onEditorHide=__bind(this.onEditorHide,this);this.showEditor=__bind(this.showEditor,this);Annotator.__super__.constructor.apply(this,arguments);this.plugins={};if(!Annotator.supported()){return this}if(!this.options.readOnly){this._setupDocumentEvents()}this._setupWrapper()._setupViewer()._setupEditor();this._setupDynamicStyle();this.adder=$(this.html.adder).appendTo(this.wrapper).hide();Annotator._instances.push(this)}Annotator.prototype._setupWrapper=function(){this.wrapper=$(this.html.wrapper);this.element.find("script").remove();this.element.wrapInner(this.wrapper);this.wrapper=this.element.find(".annotator-wrapper");return this};Annotator.prototype._setupViewer=function(){var _this=this;this.viewer=new Annotator.Viewer({readOnly:this.options.readOnly});this.viewer.hide().on("edit",this.onEditAnnotation).on("delete",this.onDeleteAnnotation).addField({load:function(field,annotation){if(annotation.text){$(field).html(Util.escape(annotation.text))}else{$(field).html(""+_t("No Comment")+"")}return _this.publish("annotationViewerTextField",[field,annotation])}}).element.appendTo(this.wrapper).bind({mouseover:this.clearViewerHideTimer,mouseout:this.startViewerHideTimer});return this};Annotator.prototype._setupEditor=function(){this.editor=new Annotator.Editor;this.editor.hide().on("hide",this.onEditorHide).on("save",this.onEditorSubmit).addField({type:"textarea",label:_t("Comments")+"…",load:function(field,annotation){return $(field).find("textarea").val(annotation.text||"")},submit:function(field,annotation){return annotation.text=$(field).find("textarea").val()}});this.editor.element.appendTo(this.wrapper);return this};Annotator.prototype._setupDocumentEvents=function(){$(document).bind({mouseup:this.checkForEndSelection,mousedown:this.checkForStartSelection});return this};Annotator.prototype._setupDynamicStyle=function(){var max,sel,style,x;style=$("#annotator-dynamic-style");if(!style.length){style=$('').appendTo(document.head)}sel="*"+function(){var _k,_len2,_ref1,_results;_ref1=["adder","outer","notice","filter"];_results=[];for(_k=0,_len2=_ref1.length;_k<_len2;_k++){x=_ref1[_k];_results.push(":not(.annotator-"+x+")")}return _results}().join("");max=Util.maxZIndex($(document.body).find(sel));max=Math.max(max,1e3);style.text([".annotator-adder, .annotator-outer, .annotator-notice {"," z-index: "+(max+20)+";","}",".annotator-filter {"," z-index: "+(max+10)+";","}"].join("\n"));return this};Annotator.prototype.destroy=function(){var idx,name,plugin,_base,_ref1;Annotator.__super__.destroy.apply(this,arguments);$(document).unbind({mouseup:this.checkForEndSelection,mousedown:this.checkForStartSelection});$("#annotator-dynamic-style").remove();this.adder.remove();this.viewer.destroy();this.editor.destroy();this.wrapper.find(".annotator-hl").each(function(){$(this).contents().insertBefore(this);return $(this).remove()});this.wrapper.contents().insertBefore(this.wrapper);this.wrapper.remove();this.element.data("annotator",null);_ref1=this.plugins;for(name in _ref1){plugin=_ref1[name];if(typeof(_base=this.plugins[name]).destroy==="function"){_base.destroy()}}idx=Annotator._instances.indexOf(this);if(idx!==-1){return Annotator._instances.splice(idx,1)}};Annotator.prototype.getSelectedRanges=function(){var browserRange,i,normedRange,r,ranges,rangesToIgnore,selection,_k,_len2;selection=Util.getGlobal().getSelection();ranges=[];rangesToIgnore=[];if(!selection.isCollapsed){ranges=function(){var _k,_ref1,_results;_results=[];for(i=_k=0,_ref1=selection.rangeCount;0<=_ref1?_k<_ref1:_k>_ref1;i=0<=_ref1?++_k:--_k){r=selection.getRangeAt(i);browserRange=new Range.BrowserRange(r);normedRange=browserRange.normalize().limit(this.wrapper[0]);if(normedRange===null){rangesToIgnore.push(r)}_results.push(normedRange)}return _results}.call(this);selection.removeAllRanges()}for(_k=0,_len2=rangesToIgnore.length;_k<_len2;_k++){r=rangesToIgnore[_k];selection.addRange(r)}return $.grep(ranges,function(range){if(range){selection.addRange(range.toRange())}return range})};Annotator.prototype.createAnnotation=function(){var annotation;annotation={};this.publish("beforeAnnotationCreated",[annotation]);return annotation};Annotator.prototype.setupAnnotation=function(annotation){var e,normed,normedRanges,r,root,_k,_l,_len2,_len3,_ref1;root=this.wrapper[0];annotation.ranges||(annotation.ranges=this.selectedRanges);normedRanges=[];_ref1=annotation.ranges;for(_k=0,_len2=_ref1.length;_k<_len2;_k++){r=_ref1[_k];try{normedRanges.push(Range.sniff(r).normalize(root))}catch(_error){e=_error;if(e instanceof Range.RangeError){this.publish("rangeNormalizeFail",[annotation,r,e])}else{throw e}}}annotation.quote=[];annotation.ranges=[];annotation.highlights=[];for(_l=0,_len3=normedRanges.length;_l<_len3;_l++){normed=normedRanges[_l];annotation.quote.push($.trim(normed.text()));annotation.ranges.push(normed.serialize(this.wrapper[0],".annotator-hl"));$.merge(annotation.highlights,this.highlightRange(normed))}annotation.quote=annotation.quote.join(" / ");$(annotation.highlights).data("annotation",annotation);$(annotation.highlights).attr("data-annotation-id",annotation.id);return annotation};Annotator.prototype.updateAnnotation=function(annotation){this.publish("beforeAnnotationUpdated",[annotation]);$(annotation.highlights).attr("data-annotation-id",annotation.id);this.publish("annotationUpdated",[annotation]);return annotation};Annotator.prototype.deleteAnnotation=function(annotation){var child,h,_k,_len2,_ref1;if(annotation.highlights!=null){_ref1=annotation.highlights;for(_k=0,_len2=_ref1.length;_k<_len2;_k++){h=_ref1[_k];if(!(h.parentNode!=null)){continue}child=h.childNodes[0];$(h).replaceWith(h.childNodes)}}this.publish("annotationDeleted",[annotation]);return annotation};Annotator.prototype.loadAnnotations=function(annotations){var clone,loader,_this=this;if(annotations==null){annotations=[]}loader=function(annList){var n,now,_k,_len2;if(annList==null){annList=[]}now=annList.splice(0,10);for(_k=0,_len2=now.length;_k<_len2;_k++){n=now[_k];_this.setupAnnotation(n)}if(annList.length>0){return setTimeout(function(){return loader(annList)},10)}else{return _this.publish("annotationsLoaded",[clone])}};clone=annotations.slice();loader(annotations);return this};Annotator.prototype.dumpAnnotations=function(){if(this.plugins["Store"]){return this.plugins["Store"].dumpAnnotations()}else{console.warn(_t("Can't dump annotations without Store plugin."));return false}};Annotator.prototype.highlightRange=function(normedRange,cssClass){var hl,node,white,_k,_len2,_ref1,_results;if(cssClass==null){cssClass="annotator-hl"}white=/^\s*$/;hl=$("");_ref1=normedRange.textNodes();_results=[];for(_k=0,_len2=_ref1.length;_k<_len2;_k++){node=_ref1[_k];if(!white.test(node.nodeValue)){_results.push($(node).wrapAll(hl).parent().show()[0])}}return _results};Annotator.prototype.highlightRanges=function(normedRanges,cssClass){var highlights,r,_k,_len2;if(cssClass==null){cssClass="annotator-hl"}highlights=[];for(_k=0,_len2=normedRanges.length;_k<_len2;_k++){r=normedRanges[_k];$.merge(highlights,this.highlightRange(r,cssClass))}return highlights};Annotator.prototype.addPlugin=function(name,options){var klass,_base;if(this.plugins[name]){console.error(_t("You cannot have more than one instance of any plugin."))}else{klass=Annotator.Plugin[name];if(typeof klass==="function"){this.plugins[name]=new klass(this.element[0],options);this.plugins[name].annotator=this;if(typeof(_base=this.plugins[name]).pluginInit==="function"){_base.pluginInit()}}else{console.error(_t("Could not load ")+name+_t(" plugin. Have you included the appropriate ",""===t.firstChild.innerHTML}(o),c=o&&function(e){var t=e.createElement("div");return t.innerHTML="Test: Value","Test:"===t.childNodes[0].nodeValue&&" Value"===t.childNodes[2].nodeValue}(o),h=o&&function(e){var t,r,n=e.createElement("table");try{n.innerHTML=""}catch(i){}finally{r=0===n.childNodes.length}r&&(t={colgroup:["table"],table:[],tbody:["table"],tfoot:["table"],thead:["table"],tr:["table","tbody"]});var a=e.createElement("select");return a.innerHTML="",a.childNodes[0]||(t=t||{},t.select=[]),t}(o);s=l?function(e,r,i){return e=n(e,r),r=i.cloneNode(r,!1),t(r,e),r.childNodes}:function(e,t,r){return e=n(e,t),t=r.cloneNode(t,!1),t.innerHTML=e,t.childNodes};var m;m=h||c?function(e,t,n){var i=[],a=[];"string"==typeof e&&(e=e.replace(/(\s*)()(\s*)/g,function(e,t,r){return a.push(r),t}));var o;o=h[t.tagName.toLowerCase()]?r(e,t):s(e,t,n);var u,l,c,m,d=[];for(u=0;u0&&(p=n.document.createTextNode(v),f.parentNode.insertBefore(p,f)),g=a[u],g&&g.length>0&&(p=n.document.createTextNode(g),f.parentNode.insertBefore(p,f.nextSibling));return o}:s;var d;d=u?function(e,t,r){return i[t.tagName]?m(e,document.createElement("div"),r):m(e,t,r)}:m,e.buildHTMLDOM=d}),e("dom-helper/classes",["exports"],function(e){"use strict";function t(e){var t=e.getAttribute("class")||"";return""!==t&&" "!==t?t.split(" "):[]}function r(e,t){for(var r=0,n=e.length,i=0,a=t.length,s=new Array(a);n>r;r++)for(i=0;a>i;i++)if(t[i]===e[r]){s[i]=r;break}return s}function n(e,n){for(var i=t(e),a=r(i,n),s=!1,o=0,u=n.length;u>o;o++)void 0===a[o]&&(s=!0,i.push(n[o]));s&&e.setAttribute("class",i.length>0?i.join(" "):"")}function i(e,n){for(var i=t(e),a=r(n,i),s=!1,o=[],u=0,l=i.length;l>u;u++)void 0===a[u]?o.push(i[u]):s=!0;s&&e.setAttribute("class",o.length>0?o.join(" "):"")}var a,s,o="undefined"==typeof document?!1:document,u=o&&function(){var e=document.createElement("div");return e.classList?(e.classList.add("boo"),e.classList.add("boo","baz"),"boo baz"===e.className):!1}();u?(a=function(e,t){e.classList?1===t.length?e.classList.add(t[0]):2===t.length?e.classList.add(t[0],t[1]):e.classList.add.apply(e.classList,t):n(e,t)},s=function(e,t){e.classList?1===t.length?e.classList.remove(t[0]):2===t.length?e.classList.remove(t[0],t[1]):e.classList.remove.apply(e.classList,t):i(e,t)}):(a=n,s=i),e.addClasses=a,e.removeClasses=s}),e("dom-helper/prop",["exports"],function(e){"use strict";function t(e){return null===e||void 0===e}function r(e,t){var r,i=e.tagName,a=n[i];if(!a){a={};for(r in e)a[r.toLowerCase()]=r;n[i]=a}return a[t]}e.isAttrRemovalValue=t;var n={};e.propertyCaches=n,e.normalizeProperty=r}),e("ember-application",["ember-metal/core","ember-runtime/system/lazy_load","ember-application/system/resolver","ember-application/system/application","ember-application/ext/controller"],function(e,t,r,n){"use strict";e["default"].Application=n["default"],e["default"].Resolver=r.Resolver,e["default"].DefaultResolver=r["default"],t.runLoadHooks("Ember.Application",n["default"])}),e("ember-application/ext/controller",["exports","ember-metal/core","ember-metal/property_get","ember-metal/error","ember-metal/utils","ember-metal/computed","ember-runtime/mixins/controller","ember-routing/system/controller_for"],function(e,t,r,n,i,a,s,o){"use strict";function u(e,t,r){var a,s,o,u=[];for(s=0,o=r.length;o>s;s++)a=r[s],-1===a.indexOf(":")&&(a="controller:"+a),t._registry.has(a)||u.push(a);if(u.length)throw new n["default"](i.inspect(e)+" needs [ "+u.join(", ")+" ] but "+(u.length>1?"they":"it")+" could not be found")}var l=a.computed(function(){var e=this;return{needs:r.get(e,"needs"),container:r.get(e,"container"),unknownProperty:function(t){var r,n,a,s=this.needs;for(n=0,a=s.length;a>n;n++)if(r=s[n],r===t)return this.container.lookup("controller:"+t);var o=i.inspect(e)+"#needs does not include `"+t+"`. To access the "+t+" controller from "+i.inspect(e)+", "+i.inspect(e)+" should have a `needs` property that is an array of the controllers it has access to.";throw new ReferenceError(o)},setUnknownProperty:function(t,r){throw new Error("You cannot overwrite the value of `controllers."+t+"` of "+i.inspect(e))}}});s["default"].reopen({concatenatedProperties:["needs"],needs:[],init:function(){var e=r.get(this,"needs"),t=r.get(e,"length");t>0&&(this.container&&u(this,this.container,e),r.get(this,"controllers")),this._super.apply(this,arguments)},controllerFor:function(e){return o["default"](r.get(this,"container"),e)},controllers:l}),e["default"]=s["default"]}),e("ember-application/system/application-instance",["exports","ember-metal/property_get","ember-metal/property_set","ember-runtime/system/object","ember-metal/run_loop","ember-metal/computed","container/registry"],function(e,t,r,n,i,a,s){ +"use strict";e["default"]=n["default"].extend({container:null,applicationRegistry:null,registry:null,customEvents:null,rootElement:null,init:function(){this._super.apply(this,arguments),this.registry=new s["default"]({fallback:this.applicationRegistry,resolver:this.applicationRegistry.resolver}),this.registry.normalizeFullName=this.applicationRegistry.normalizeFullName,this.registry.makeToString=this.applicationRegistry.makeToString,this.container=this.registry.container(),this.registry.register("-application-instance:main",this,{instantiate:!1})},router:a.computed(function(){return this.container.lookup("router:main")}).readOnly(),overrideRouterLocation:function(e){var n=e&&e.location,i=t.get(this,"router");n&&r.set(i,"location",n)},didCreateRootView:function(e){e.appendTo(this.rootElement)},startRouting:function(){var e=t.get(this,"router"),r=!!this.registry.resolver.moduleBasedResolver;e.startRouting(r),this._didSetupRouter=!0},setupRouter:function(){if(!this._didSetupRouter){this._didSetupRouter=!0;var e=t.get(this,"router"),r=!!this.registry.resolver.moduleBasedResolver;e.setupRouter(r)}},handleURL:function(e){var r=t.get(this,"router");return this.setupRouter(),r.handleURL(e)},setupEventDispatcher:function(){var e=this.container.lookup("event_dispatcher:main");return e.setup(this.customEvents,this.rootElement),e},willDestroy:function(){this._super.apply(this,arguments),i["default"](this.container,"destroy")}})}),e("ember-application/system/application",["exports","dag-map","container/registry","ember-metal","ember-metal/property_get","ember-metal/property_set","ember-runtime/system/lazy_load","ember-runtime/system/namespace","ember-runtime/mixins/deferred","ember-application/system/resolver","ember-metal/platform/create","ember-metal/run_loop","ember-metal/utils","ember-runtime/controllers/controller","ember-metal/enumerable_utils","ember-runtime/controllers/object_controller","ember-runtime/controllers/array_controller","ember-views/system/renderer","dom-helper","ember-views/views/select","ember-routing-views/views/outlet","ember-views/views/view","ember-views/views/metamorph_view","ember-views/system/event_dispatcher","ember-views/system/jquery","ember-routing/system/route","ember-routing/system/router","ember-routing/location/hash_location","ember-routing/location/history_location","ember-routing/location/auto_location","ember-routing/location/none_location","ember-routing/system/cache","ember-application/system/application-instance","ember-extension-support/container_debug_adapter","ember-metal/environment"],function(e,t,r,n,i,a,s,o,u,l,c,h,m,d,f,p,v,g,b,y,_,w,x,C,E,O,P,S,A,N,T,k,M,I,V){"use strict";function R(e){var t=[];for(var r in e)t.push(r);return t}function j(e){function t(e){return n.resolve(e)}var r=e.get("resolver")||e.get("Resolver")||l["default"],n=r.create({namespace:e});return t.describe=function(e){return n.lookupDescription(e)},t.makeToString=function(e,t){return n.makeToString(e,t)},t.normalize=function(e){return n.normalize?n.normalize(e):e},t.__resolver__=n,t}function D(){B||(B=!0,V["default"].hasDOM&&n["default"].libraries.registerCoreLibrary("jQuery",E["default"]().jquery))}function F(){if(n["default"].LOG_VERSION){n["default"].LOG_VERSION=!1;for(var e=n["default"].libraries._registry,t=f["default"].map(e,function(e){return i.get(e,"name.length")}),r=Math.max.apply(this,t),a=0,s=e.length;s>a;a++){var o=e[a];new Array(r-o.name.length+1).join(" ")}}}function L(e,t){return function(t){if(void 0!==this.superclass[e]&&this.superclass[e]===this[e]){var r={};r[e]=c["default"](this[e]),this.reopenClass(r)}this[e][t.name]=t}}var B=!1,H=o["default"].extend(u["default"],{_suppressDeferredDeprecation:!0,rootElement:"body",eventDispatcher:null,customEvents:null,autoboot:!0,init:function(){this._super.apply(this,arguments),this.$||(this.$=E["default"]),this.buildRegistry(),D(),F(),this._readinessDeferrals=1,this.Router=(this.Router||P["default"]).extend(),this.buildDefaultInstance(),this.waitForDOMReady()},buildRegistry:function(){var e=this.registry=H.buildRegistry(this);return e},buildInstance:function(){return M["default"].create({customEvents:i.get(this,"customEvents"),rootElement:i.get(this,"rootElement"),applicationRegistry:this.registry})},buildDefaultInstance:function(){var e=this.buildInstance();return w["default"].views=e.container.lookup("-view-registry:main"),this.__deprecatedInstance__=e,this.__container__=e.container,e},waitForDOMReady:function(){!this.$||this.$.isReady?h["default"].schedule("actions",this,"domReady"):this.$().ready(h["default"].bind(this,"domReady"))},deferReadiness:function(){this._readinessDeferrals++},advanceReadiness:function(){this._readinessDeferrals--,0===this._readinessDeferrals&&h["default"].once(this,this.didBecomeReady)},register:function(){var e;(e=this.registry).register.apply(e,arguments)},inject:function(){var e;(e=this.registry).injection.apply(e,arguments)},initialize:function(){},domReady:function(){return this.isDestroyed?void 0:(this.boot(),this)},boot:function(){if(this._bootPromise)return this._bootPromise;var e=new n["default"].RSVP.defer;return this._bootPromise=e.promise,this._bootResolver=e,this.runInitializers(this.registry),s.runLoadHooks("application",this),this.advanceReadiness(),this._bootPromise},reset:function(){function e(){h["default"](t,"destroy"),h["default"].schedule("actions",this,"domReady",this.buildDefaultInstance())}var t=this.__deprecatedInstance__;this._readinessDeferrals=1,this._bootPromise=null,this._bootResolver=null,h["default"].join(this,e)},runInitializers:function(e){var t=this;this._runInitializer("initializers",function(r,n){n.initialize(e,t)})},runInstanceInitializers:function(e){this._runInitializer("instanceInitializers",function(t,r){r.initialize(e)})},_runInitializer:function(e,r){for(var n,a=i.get(this.constructor,e),s=R(a),o=new t["default"],u=0;u-1&&(i=i.replace(/\.(.)/g,function(e){return e.charAt(1).toUpperCase()})),n.indexOf("_")>-1&&(i=i.replace(/_(.)/g,function(e){return e.charAt(1).toUpperCase()})),r+":"+i}return e},resolve:function(e){var t,r=this.parseName(e),n=r.resolveMethodName;return this[n]&&(t=this[n](r)),t=t||this.resolveOther(r),r.root&&r.root.LOG_RESOLVER&&this._logLookup(t,r),t},parseName:function(e){return this._parseNameCache[e]||(this._parseNameCache[e]=this._parseName(e))},_parseName:function(e){var t=e.split(":"),n=t[0],a=t[1],o=a,u=r.get(this,"namespace"),l=u;if("template"!==n&&-1!==o.indexOf("/")){var c=o.split("/");o=c[c.length-1];var h=i.capitalize(c.slice(0,-1).join("."));l=s["default"].byName(h)}var m="main"===a?"Main":i.classify(n);if(!o||!n)throw new TypeError("Invalid fullName: `"+e+"`, must be of the form `type:name` ");return{fullName:e,type:n,fullNameWithoutType:a,name:o,root:l,resolveMethodName:"resolve"+m}},lookupDescription:function(e){var t,r=this.parseName(e);return"template"===r.type?"template at "+r.fullNameWithoutType.replace(/\./g,"/"):(t=r.root+"."+i.classify(r.name).replace(/\./g,""),"model"!==r.type&&(t+=i.classify(r.type)),t)},makeToString:function(e,t){return e.toString()},useRouterNaming:function(e){e.name=e.name.replace(/\./g,"_"),"basic"===e.name&&(e.name="")},resolveTemplate:function(e){var r=e.fullNameWithoutType.replace(/\./g,"/");return t["default"].TEMPLATES[r]?t["default"].TEMPLATES[r]:(r=i.decamelize(r),t["default"].TEMPLATES[r]?t["default"].TEMPLATES[r]:void 0)},resolveView:function(e){return this.useRouterNaming(e),this.resolveOther(e)},resolveController:function(e){return this.useRouterNaming(e),this.resolveOther(e)},resolveRoute:function(e){return this.useRouterNaming(e),this.resolveOther(e)},resolveModel:function(e){var t=i.classify(e.name),n=r.get(e.root,t);return n?n:void 0},resolveHelper:function(e){return this.resolveOther(e)||o["default"][e.fullNameWithoutType]},resolveOther:function(e){var t=i.classify(e.name)+i.classify(e.type),n=r.get(e.root,t);return n?n:void 0},resolveMain:function(e){var t=i.classify(e.type);return r.get(e.root,t)},_logLookup:function(e,t){var r,i;r=e?"[✓]":"[ ]",i=t.fullName.length>60?".":new Array(60-t.fullName.length).join("."),n["default"].info(r,t.fullName,i,this.lookupDescription(t.fullName))}}),e.Resolver=l}),e("ember-extension-support",["ember-metal/core","ember-extension-support/data_adapter","ember-extension-support/container_debug_adapter"],function(e,t,r){"use strict";e["default"].DataAdapter=t["default"],e["default"].ContainerDebugAdapter=r["default"]}),e("ember-extension-support/container_debug_adapter",["exports","ember-metal/core","ember-runtime/system/native_array","ember-metal/utils","ember-runtime/system/string","ember-runtime/system/namespace","ember-runtime/system/object"],function(e,t,r,n,i,a,s){"use strict";e["default"]=s["default"].extend({container:null,resolver:null,canCatalogEntriesByType:function(e){return"model"===e||"template"===e?!1:!0},catalogEntriesByType:function(e){var s=r.A(a["default"].NAMESPACES),o=r.A(),u=new RegExp(i.classify(e)+"$");return s.forEach(function(e){if(e!==t["default"])for(var r in e)if(e.hasOwnProperty(r)&&u.test(r)){var a=e[r];"class"===n.typeOf(a)&&o.push(i.dasherize(r.replace(u,"")))}}),o}})}),e("ember-extension-support/data_adapter",["exports","ember-metal/property_get","ember-metal/run_loop","ember-runtime/system/string","ember-runtime/system/namespace","ember-runtime/system/object","ember-runtime/system/native_array","ember-application/system/application"],function(e,t,r,n,i,a,s,o){"use strict";e["default"]=a["default"].extend({init:function(){this._super.apply(this,arguments),this.releaseMethods=s.A()},container:null,containerDebugAdapter:void 0,attributeLimit:3,releaseMethods:s.A(),getFilters:function(){return s.A()},watchModelTypes:function(e,t){var r,n=this.getModelTypes(),i=this,a=s.A();r=n.map(function(e){var r=e.klass,n=i.wrapModelType(r,e.name);return a.push(i.observeModelType(r,t)),n}),e(r);var o=function(){a.forEach(function(e){e()}),i.releaseMethods.removeObject(o)};return this.releaseMethods.pushObject(o),o},_nameToClass:function(e){return"string"==typeof e&&(e=this.container.lookupFactory("model:"+e)),e},watchRecords:function(e,t,r,n){var i,a=this,o=s.A(),u=this.getRecords(e),l=function(e){r([e])},c=u.map(function(e){return o.push(a.observeRecord(e,l)),a.wrapRecord(e)}),h=function(e,r,i,s){for(var u=r;r+s>u;u++){var c=e.objectAt(u),h=a.wrapRecord(c);o.push(a.observeRecord(c,l)),t([h])}i&&n(r,i)},m={didChange:h,willChange:function(){return this}};return u.addArrayObserver(a,m),i=function(){o.forEach(function(e){e()}),u.removeArrayObserver(a,m),a.releaseMethods.removeObject(i)},t(c),this.releaseMethods.pushObject(i),i},willDestroy:function(){this._super.apply(this,arguments),this.releaseMethods.forEach(function(e){e()})},detect:function(e){return!1},columnsForType:function(e){return s.A()},observeModelType:function(e,t){var n=this,i=this.getRecords(e),a=function(){t([n.wrapModelType(e)])},s={didChange:function(){r["default"].scheduleOnce("actions",this,a)},willChange:function(){return this}};i.addArrayObserver(this,s);var o=function(){i.removeArrayObserver(n,s)};return o},wrapModelType:function(e,r){var n,i=this.getRecords(e);return n={name:r||e.toString(),count:t.get(i,"length"),columns:this.columnsForType(e),object:e}},getModelTypes:function(){var e,t=this,r=this.get("containerDebugAdapter");return e=r.canCatalogEntriesByType("model")?r.catalogEntriesByType("model"):this._getObjectsOnNamespaces(),e=s.A(e).map(function(e){return{klass:t._nameToClass(e),name:e}}),e=s.A(e).filter(function(e){return t.detect(e.klass)}),s.A(e)},_getObjectsOnNamespaces:function(){var e=s.A(i["default"].NAMESPACES),t=s.A(),r=this;return e.forEach(function(e){for(var i in e)if(e.hasOwnProperty(i)&&r.detect(e[i])){var a=n.dasherize(i);e instanceof o["default"]||!e.toString()||(a=e+"/"+a),t.push(a)}}),t},getRecords:function(e){return s.A()},wrapRecord:function(e){var t={object:e};return t.columnValues=this.getRecordColumnValues(e),t.searchKeywords=this.getRecordKeywords(e),t.filterValues=this.getRecordFilterValues(e),t.color=this.getRecordColor(e),t},getRecordColumnValues:function(e){return{}},getRecordKeywords:function(e){return s.A()},getRecordFilterValues:function(e){return{}},getRecordColor:function(e){return null},observeRecord:function(e,t){return function(){}}})}),e("ember-htmlbars",["ember-metal/core","ember-template-compiler","ember-htmlbars/system/make-view-helper","ember-htmlbars/system/make_bound_helper","ember-htmlbars/helpers","ember-htmlbars/helpers/view","ember-htmlbars/helpers/component","ember-htmlbars/helpers/yield","ember-htmlbars/helpers/with","ember-htmlbars/helpers/log","ember-htmlbars/helpers/debugger","ember-htmlbars/helpers/bind-attr","ember-htmlbars/helpers/if_unless","ember-htmlbars/helpers/loc","ember-htmlbars/helpers/partial","ember-htmlbars/helpers/template","ember-htmlbars/helpers/input","ember-htmlbars/helpers/text_area","ember-htmlbars/helpers/collection","ember-htmlbars/helpers/each","ember-htmlbars/helpers/unbound","ember-htmlbars/system/bootstrap","ember-htmlbars/compat"],function(e,t,r,n,i,a,s,o,u,l,c,h,m,d,f,p,v,g,b,y,_){"use strict";i.registerHelper("view",a.viewHelper),i.registerHelper("component",s.componentHelper),i.registerHelper("yield",o.yieldHelper),i.registerHelper("with",u.withHelper),i.registerHelper("if",m.ifHelper),i.registerHelper("unless",m.unlessHelper),i.registerHelper("log",l.logHelper),i.registerHelper("debugger",c.debuggerHelper),i.registerHelper("loc",d.locHelper),i.registerHelper("partial",f.partialHelper),i.registerHelper("template",p.templateHelper),i.registerHelper("bind-attr",h.bindAttrHelper),i.registerHelper("bindAttr",h.bindAttrHelperDeprecated),i.registerHelper("input",v.inputHelper),i.registerHelper("textarea",g.textareaHelper),i.registerHelper("collection",b.collectionHelper),i.registerHelper("each",y.eachHelper),i.registerHelper("unbound",_.unboundHelper),e["default"].HTMLBars={_registerHelper:i.registerHelper,template:t.template,compile:t.compile,precompile:t.precompile,makeViewHelper:r["default"],makeBoundHelper:n["default"],registerPlugin:t.registerPlugin}}),e("ember-htmlbars/compat",["exports","ember-metal/core","ember-htmlbars/helpers","ember-htmlbars/compat/helper","ember-htmlbars/compat/handlebars-get","ember-htmlbars/compat/make-bound-helper","ember-htmlbars/compat/register-bound-helper","ember-htmlbars/system/make-view-helper","ember-htmlbars/utils/string"],function(e,t,r,n,i,a,s,o,u){"use strict";var l=t["default"].Handlebars=t["default"].Handlebars||{};l.helpers=r["default"],l.helper=n.handlebarsHelper,l.registerHelper=n.registerHandlebarsCompatibleHelper,l.registerBoundHelper=s["default"],l.makeBoundHelper=a["default"],l.get=i["default"],l.makeViewHelper=o["default"],l.SafeString=u.SafeString,l.Utils={escapeExpression:u.escapeExpression},e["default"]=l}),e("ember-htmlbars/compat/handlebars-get",["exports"],function(e){"use strict";function t(e,t,r){return r.data.view.getStream(t).value()}e["default"]=t}),e("ember-htmlbars/compat/helper",["exports","ember-metal/merge","ember-htmlbars/helpers","ember-views/views/view","ember-views/views/component","ember-htmlbars/system/make-view-helper","ember-htmlbars/compat/make-bound-helper","ember-metal/streams/utils"],function(e,t,r,n,i,a,s,o){"use strict";function u(e){if(o.isStream(e))return"ID";var t=typeof e;return t.toUpperCase()}function l(e){this.helperFunction=function(r,n,i,a){var s,l,c,h=a.data.view,m={hash:{},types:new Array(r.length),hashTypes:{}};t["default"](m,i),t["default"](m,a),m.hash={},i.isBlock&&(m.fn=function(){l=i.template.render(h,a,i.morph.contextualElement)},i.inverse&&(m.inverse=function(){l=i.inverse.render(h,a,i.morph.contextualElement)}));for(var d in n)s=n[d],m.hashTypes[d]=u(s),o.isStream(s)?m.hash[d]=s._label:m.hash[d]=s;for(var f=new Array(r.length),p=0,v=r.length;v>p;p++)s=r[p],m.types[p]=u(s),o.isStream(s)?f[p]=s._label:f[p]=s;return f.push(m),c=e.apply(this,f),i.isBlock?l:c},this.isHTMLBars=!0}function c(e,t){var n;n=t&&t.isHTMLBars?t:new l(t),r["default"][e]=n}function h(e,t){if(n["default"].detect(t))r["default"][e]=a["default"](t);else{var i=m.call(arguments,1),o=s["default"].apply(this,i);r["default"][e]=o}}e.registerHandlebarsCompatibleHelper=c,e.handlebarsHelper=h;var m=[].slice;l.prototype={preprocessArguments:function(){}},e["default"]=l}),e("ember-htmlbars/compat/make-bound-helper",["exports","ember-metal/core","ember-metal/mixin","ember-htmlbars/system/helper","ember-metal/streams/stream","ember-metal/streams/utils"],function(e,t,r,n,i,a){"use strict";function s(e,t){function s(t,n,s,l){function c(){for(var r=a.readArray(t),i=new Array(t.length),s=0,o=t.length;o>s;s++)h=t[s],a.isStream(h)?i[s]=h._label:i[s]=h;return r.push({hash:a.readHash(n),data:{properties:i}}),e.apply(m,r)}var h,m=l.data.view,d=t.length;for(var f in n)r.IS_BINDING.test(f)&&(n[f.slice(0,-7)]=m.getStream(n[f]),delete n[f]);var p=a.scanArray(t)||a.scanHash(n);if(p){var v=new i["default"](c);for(u=0;d>u;u++)h=t[u],a.isStream(h)&&h.subscribe(v.notify,v);for(f in n)h=n[f],a.isStream(h)&&h.subscribe(v.notify,v);if(d>0){var g=t[0];if(a.isStream(g)){var b=function(e){e.value(),v.notify()};for(u=0;ub;b++)d=g[b],"class"!==d&&(f=t[d],p=u.isStream(f)?f:l.getStream(f),v=new i["default"](d,p),v._morph=s.dom.createAttrMorph(o,d),l.appendChild(v))}function h(e,t){var r=e.split(" "),n=o.map(r,function(e){return l.streamifyClassNameBinding(t,e)}),i=u.concat(n," ");return i}function m(){return s["default"]["bind-attr"].helperFunction.apply(this,arguments)}e.bindAttrHelper=c,e.bindAttrHelperDeprecated=m,e["default"]=c}),e("ember-htmlbars/helpers/collection",["exports","ember-metal/core","ember-metal/mixin","ember-runtime/system/string","ember-metal/property_get","ember-views/views/collection_view","ember-views/streams/utils","ember-metal/enumerable_utils","ember-views/streams/class_name_binding","ember-htmlbars/system/merge-view-bindings"],function(e,t,r,n,i,a,s,o,u,l){"use strict";function c(e,t,n,c){var h,m=e[0],d=c.data,f=n.template,p=n.inverse,v=d.view,g=i.get(v,"controller"),b=g&&g.container?g.container:v.container;h=m?s.readViewFactory(m,b):a["default"];var y,_,w={},x=h.proto();_=t.itemView?s.readViewFactory(t.itemView,b):t.itemViewClass?s.readViewFactory(t.itemViewClass,b):x.itemViewClass,"string"==typeof _&&(_=b.lookupFactory("view:"+_)),delete t.itemViewClass,delete t.itemView;for(var C in t)if("itemController"!==C&&"itemClassBinding"!==C&&t.hasOwnProperty(C)&&(y=C.match(/^item(.)(.*)$/))){var E=y[1].toLowerCase()+y[2];r.IS_BINDING.test(C)?w[E]=v._getBindingForStream(t[C]):w[E]=t[C],delete t[C]}f&&(w.template=f,delete n.template);var O;p?(O=i.get(x,"emptyViewClass"),O=O.extend({template:p,tagName:w.tagName})):t.emptyViewClass&&(O=s.readViewFactory(t.emptyViewClass,b)),O&&(t.emptyView=O);var P=l["default"](v,{},w);if(t.itemClassBinding){var S=t.itemClassBinding.split(" ");P.classNameBindings=o.map(S,function(e){return u.streamifyClassNameBinding(v,e)})}return t.itemViewClass=_,t._itemViewProps=P,n.helperName=n.helperName||"collection",c.helpers.view.helperFunction.call(this,[h],t,n,c)}e.collectionHelper=c}),e("ember-htmlbars/helpers/component",["exports","ember-metal/core","ember-metal/streams/utils","ember-views/streams/utils","ember-metal/error","ember-views/views/bound_component_view","ember-htmlbars/system/merge-view-bindings","ember-htmlbars/system/append-templated-view"],function(e,t,r,n,i,a,s,o){"use strict";function u(e,u,l,c){var h=c.data.view,m=e[0],d=h.container||r.read(h._keywords.view).container,f={helperName:l.helperName||"component"};l.template&&(f.template=l.template);var p;if(r.isStream(m))p=a["default"],f={_boundComponentOptions:t["default"].merge(u,f)},f._boundComponentOptions.componentNameStream=m;else{if(p=n.readComponentFactory(m,d),!p)throw new i["default"]('HTMLBars error: Could not find component named "'+m+'".');s["default"](h,f,u)}o["default"](h,l.morph,p,f)}e.componentHelper=u}),e("ember-htmlbars/helpers/debugger",["exports","ember-metal/logger"],function(e,t){"use strict";function r(e,r,n,i){var a=i.data.view;a.get("context");t["default"].info("Use `view`, `context`, and `get()` to debug this template.")}e.debuggerHelper=r}),e("ember-htmlbars/helpers/each",["exports","ember-metal/core","ember-views/views/each"],function(e,t,r){"use strict";function n(e,t,n,i){var a=i.data.view,s="each",o=e[0]||a.getStream(""),u=n.template&&n.template.blockParams;return u&&(t.keyword=!0,t.blockParams=u),t.dataSource=o,n.helperName=n.helperName||s,i.helpers.collection.helperFunction.call(this,[r["default"]],t,n,i)}e.eachHelper=n,e.EachView=r["default"]}),e("ember-htmlbars/helpers/if_unless",["exports","ember-metal/core","ember-metal/streams/conditional","ember-views/streams/should_display","ember-metal/property_get","ember-metal/streams/utils","ember-views/views/bound_if_view","ember-htmlbars/templates/empty"],function(e,t,r,n,i,a,s,o){"use strict";function u(e,t,r,n){var i=r.helperName||"if";return c(!1,i,e,t,r,n)}function l(e,t,r,n){var i=r.helperName||"unless";return c(!0,i,e,t,r,n)}function c(e,t,r,n,i,a){var s=a.data.view;return i.isBlock?h(s,e,t,r,n,i,a):m(s,e,t,r,n,i,a)}function h(e,t,r,u,l,c,h){var m=n["default"](u[0]),d=(t?c.inverse:c.template)||o["default"],f=(t?c.template:c.inverse)||o["default"];if(a.isStream(m))e.appendChild(s["default"],{_morph:c.morph,_context:i.get(e,"context"),conditionStream:m,truthyTemplate:d,falsyTemplate:f,helperName:r});else{var p=m?d:f;if(p)return p.render(e,h,c.morph.contextualElement)}}function m(e,t,i,a){return r["default"](n["default"](a[0]),t?a[2]:a[1],t?a[1]:a[2])}e.ifHelper=u,e.unlessHelper=l}),e("ember-htmlbars/helpers/input",["exports","ember-views/views/checkbox","ember-views/views/text_field","ember-metal/streams/utils","ember-metal/core"],function(e,t,r,n,i){"use strict";function a(e,i,a,s){var o,u=i.on;o=n.read(i.type),"checkbox"===o?(delete i.type,s.helpers.view.helperFunction.call(this,[t["default"]],i,a,s)):(delete i.on,i.onEvent=u||"enter",s.helpers.view.helperFunction.call(this,[r["default"]],i,a,s))}e.inputHelper=a}),e("ember-htmlbars/helpers/loc",["exports","ember-metal/core","ember-runtime/system/string","ember-metal/streams/utils"],function(e,t,r,n){"use strict";function i(e,t,n,i){return r.loc.apply(i.data.view,e)}e.locHelper=i}),e("ember-htmlbars/helpers/log",["exports","ember-metal/logger","ember-metal/streams/utils"],function(e,t,r){"use strict";function n(e,n,i,a){for(var s=t["default"].log,o=[],u=0;ui;i++)r[i-1]=n.read(e[i]);return r}function o(e,t){var i={};for(var a in e)if(r.IS_BINDING.test(a)){var s=e[a];"string"==typeof s&&(s=t.getStream(s)),i[a.slice(0,-7)]=n.read(s)}else i[a]=n.read(e[a]);return i}e.unboundHelper=a}),e("ember-htmlbars/helpers/view",["exports","ember-metal/core","ember-metal/streams/utils","ember-views/streams/utils","ember-views/views/view","ember-htmlbars/system/merge-view-bindings","ember-htmlbars/system/append-templated-view"],function(e,t,r,n,i,a,s){"use strict";function o(e,t,o,u){var l,c=u.data.view,h=c.container||r.read(c._keywords.view).container;l=0===e.length?h?h.lookupFactory("view:toplevel"):i["default"]:n.readViewFactory(e[0],h);var m={helperName:o.helperName||"view"};o.template&&(m.template=o.template),a["default"](c,m,t),s["default"](c,o.morph,l,m)}e.viewHelper=o}),e("ember-htmlbars/helpers/with",["exports","ember-metal/core","ember-views/views/with_view"],function(e,t,r){"use strict";function n(e,t,n,i){var a,s=i.data.view;a=n.template.blockParams?!0:!1,s.appendChild(r["default"],{_morph:n.morph,withValue:e[0],preserveContext:a,previousContext:s.get("context"),controllerName:t.controller,mainTemplate:n.template,inverseTemplate:n.inverse,helperName:n.helperName||"with"})}e.withHelper=n}),e("ember-htmlbars/helpers/yield",["exports","ember-metal/core","ember-metal/property_get"],function(e,t,r){"use strict";function n(e,t,n,i){for(var a=i.data.view,s=a;s&&!r.get(s,"layout");)s=s._contextView?s._contextView:s._parentView;return s._yield(a,i,n.morph,e)}e.yieldHelper=n}),e("ember-htmlbars/hooks/attribute",["exports","ember-views/attr_nodes/attr_node","ember-metal/error","ember-metal/streams/utils","morph-attr/sanitize-attribute-value"],function(e,t,r,n,i){"use strict";function a(e,a,o,u,l){if(s){var c=new t["default"](u,l);c._morph=a,e.data.view.appendChild(c)}else{if(n.isStream(l))throw new r["default"]("Bound attributes are not yet supported in Ember.js");var h=i["default"](e.dom,o,u,l);e.dom.setProperty(o,u,h)}}e["default"]=a;var s=!1;s=!0}),e("ember-htmlbars/hooks/block",["exports","ember-views/views/simple_bound_view","ember-metal/streams/utils","ember-htmlbars/system/lookup-helper"],function(e,t,r,n){ +"use strict";function i(e,i,a,s,o,u,l,c){var h=n["default"](s,a,e),m={morph:i,template:l,inverse:c,isBlock:!0},d=h.helperFunction.call(void 0,o,u,m,e);r.isStream(d)?t.appendSimpleBoundView(a,i,d):i.setContent(d)}e["default"]=i}),e("ember-htmlbars/hooks/component",["exports","ember-metal/core","ember-htmlbars/system/lookup-helper"],function(e,t,r){"use strict";function n(e,t,n,i,a,s){var o=r["default"](i,n,e);return o.helperFunction.call(void 0,[],a,{morph:t,template:s},e)}e["default"]=n}),e("ember-htmlbars/hooks/concat",["exports","ember-metal/streams/utils"],function(e,t){"use strict";function r(e,r){return t.concat(r,"")}e["default"]=r}),e("ember-htmlbars/hooks/content",["exports","ember-views/views/simple_bound_view","ember-metal/streams/utils","ember-htmlbars/system/lookup-helper"],function(e,t,r,n){"use strict";function i(e,i,a,s){var o,u=n["default"](s,a,e);if(u){var l={morph:i,isInline:!0};o=u.helperFunction.call(void 0,[],{},l,e)}else o=a.getStream(s);r.isStream(o)?t.appendSimpleBoundView(a,i,o):i.setContent(o)}e["default"]=i}),e("ember-htmlbars/hooks/element",["exports","ember-metal/core","ember-metal/streams/utils","ember-htmlbars/system/lookup-helper"],function(e,t,r,n){"use strict";function i(e,t,r){s||(s=document.createElement("div")),s.innerHTML="<"+t.tagName+" "+r+">";for(var n=s.firstChild.attributes,i=0,a=n.length;a>i;i++){var o=n[i];o.specified&&e.setAttribute(t,o.name,o.value)}}function a(e,t,a,s,o,u){var l,c=n["default"](s,a,e);if(c){var h={element:t};l=c.helperFunction.call(void 0,o,u,h,e)}else l=a.getStream(s);var m=r.read(l);m&&i(e.dom,t,m)}e["default"]=a;var s}),e("ember-htmlbars/hooks/get",["exports"],function(e){"use strict";function t(e,t,r){return t.getStream(r)}e["default"]=t}),e("ember-htmlbars/hooks/inline",["exports","ember-views/views/simple_bound_view","ember-metal/streams/utils","ember-htmlbars/system/lookup-helper"],function(e,t,r,n){"use strict";function i(e,i,a,s,o,u){var l=n["default"](s,a,e),c=l.helperFunction.call(void 0,o,u,{morph:i},e);r.isStream(c)?t.appendSimpleBoundView(a,i,c):i.setContent(c)}e["default"]=i}),e("ember-htmlbars/hooks/set",["exports"],function(e){"use strict";function t(e,t,r,n){t._keywords[r]=n}e["default"]=t}),e("ember-htmlbars/hooks/subexpr",["exports","ember-htmlbars/system/lookup-helper"],function(e,t){"use strict";function r(e,r,n,i,a){var s=t["default"](n,r,e),o={isInline:!0};return s.helperFunction.call(void 0,i,a,o,e)}e["default"]=r}),e("ember-htmlbars/system/append-templated-view",["exports","ember-metal/core","ember-metal/property_get","ember-views/views/view"],function(e,t,r,n){"use strict";function i(e,t,i,a){var s;s=n["default"].detectInstance(i)?i:i.proto();var o=!s.controller;return s.controller&&s.controller.isDescriptor&&(o=!0),!o||s.controllerBinding||a.controller||a.controllerBinding||(a._context=r.get(e,"context")),a._morph=t,e.appendChild(i,a)}e["default"]=i}),e("ember-htmlbars/system/bootstrap",["exports","ember-metal/core","ember-views/component_lookup","ember-views/system/jquery","ember-metal/error","ember-runtime/system/lazy_load","ember-template-compiler/system/compile","ember-metal/environment"],function(e,t,r,n,i,a,s,o){"use strict";function u(e){var r='script[type="text/x-handlebars"], script[type="text/x-raw-handlebars"]';n["default"](r,e).each(function(){var e=n["default"](this),r="text/x-raw-handlebars"===e.attr("type")?n["default"].proxy(Handlebars.compile,Handlebars):s["default"],a=e.attr("data-template-name")||e.attr("id")||"application",o=r(e.html());if(void 0!==t["default"].TEMPLATES[a])throw new i["default"]('Template named "'+a+'" already exists.');t["default"].TEMPLATES[a]=o,e.remove()})}function l(){u(n["default"](document))}function c(e){e.register("component-lookup:main",r["default"])}a.onLoad("Ember.Application",function(e){e.initializer({name:"domTemplates",initialize:o["default"].hasDOM?l:function(){}}),e.initializer({name:"registerComponentLookup",after:"domTemplates",initialize:c})}),e["default"]=u}),e("ember-htmlbars/system/helper",["exports"],function(e){"use strict";function t(e){this.helperFunction=e,this.isHelper=!0,this.isHTMLBars=!0}e["default"]=t}),e("ember-htmlbars/system/lookup-helper",["exports","ember-metal/core","ember-metal/cache","ember-htmlbars/system/make-view-helper","ember-htmlbars/compat/helper"],function(e,t,r,n,i){"use strict";function a(e,t,r){var a=r.helpers[e];if(a)return a;var o=t.container;if(o&&!s.get(e)){var u="helper:"+e;if(a=o.lookup(u),!a){var l=o.lookup("component-lookup:main"),c=l.lookupFactory(e,o);c&&(a=n["default"](c),o._registry.register(u,a))}return a&&!a.isHTMLBars&&(a=new i["default"](a),o._registry.unregister(u),o._registry.register(u,a)),a}}e["default"]=a;var s=new r["default"](1e3,function(e){return-1===e.indexOf("-")});e.ISNT_HELPER_CACHE=s}),e("ember-htmlbars/system/make-view-helper",["exports","ember-metal/core","ember-htmlbars/system/helper"],function(e,t,r){"use strict";function n(e){function t(t,r,n,i){return i.helpers.view.helperFunction.call(this,[e],r,n,i)}return new r["default"](t)}e["default"]=n}),e("ember-htmlbars/system/make_bound_helper",["exports","ember-metal/core","ember-htmlbars/system/helper","ember-metal/streams/stream","ember-metal/streams/utils"],function(e,t,r,n,i){"use strict";function a(e){function t(t,r,a,s){function o(){return e.call(c,i.readArray(t),i.readHash(r),a,s)}var u,l,c=s.data.view,h=t.length,m=i.scanArray(t)||i.scanHash(r);if(m){for(var d=new n["default"](o),f=0;h>f;f++)u=t[f],i.subscribe(u,d.notify,d);for(l in r)u=r[l],i.subscribe(u,d.notify,d);return d}return o()}return new r["default"](t)}e["default"]=a}),e("ember-htmlbars/system/merge-view-bindings",["exports","ember-metal/core","ember-metal/mixin","ember-metal/streams/simple","ember-metal/streams/utils","ember-views/streams/class_name_binding"],function(e,t,r,n,i,a){"use strict";function s(e,t,r){return o(e,t,r),u(e,t,r),t}function o(e,t,n){for(var a in n)if("id"!==a&&"tag"!==a&&"class"!==a&&"classBinding"!==a&&"classNameBindings"!==a&&"attributeBindings"!==a){var s=n[a];r.IS_BINDING.test(a)?"string"==typeof s?t[a]=e._getBindingForStream(s):i.isStream(s)?t[a]=e._getBindingForStream(s):t[a]=s:i.isStream(s)?t[a+"Binding"]=e._getBindingForStream(s):t[a]=s}}function u(e,t,r){r.id&&(t.id=t.elementId=i.read(r.id)),r.tag&&(t.tagName=i.read(r.tag));var s=[];if(r["class"]&&("string"==typeof r["class"]?t.classNames=r["class"].split(" "):r["class"]._label?s.push(r["class"]._label):s.push(r["class"])),r.classBinding&&l.apply(s,r.classBinding.split(" ")),r.classNameBindings&&l.apply(s,r.classNameBindings.split(" ")),s.length>0){t.classNameBindings=s;for(var o=0;o=0;h--)m=c[h],i=a++,n[i]=m,l[d++]=i,w=m;for(i=l[--d],w=n[i];f===i;){if(_--,w._elementCreated=!0,this.didCreateElement(w),u&&this.willInsertElement(w),0===_){d--;break}f=p[_],v=-1===f?t:n[f],this.insertElement(w,v,b,null),i=l[--d],w=n[i],b=g[_],g[_]=null}}for(this.insertElement(w,t,b,r),h=a-1;h>=0;h--)u&&(n[h]._elementInserted=!0,this.didInsertElement(n[h])),n[h]=null;return b}function a(e,t,r){var n=this.uuid(e);if(this._inserts[n]&&(this.cancelRender(this._inserts[n]),this._inserts[n]=void 0),e._elementCreated){var i,a,s,o,u,l,c,h=[],m=[],d=e._morph;for(h.push(e),i=0;il;l++)o.push(u[l]);for(i=0;il;l++)m.push(u[l]);for(d&&!r&&d.destroy(),i=0,a=h.length;a>i;i++)this.afterRemove(h[i],!1);for(i=0,a=m.length;a>i;i++)this.afterRemove(m[i],!0);r&&(e._morph=d)}}function s(e,t,r,n){null!==r&&void 0!==r&&(e._morph?e._morph.setContent(r):t&&(e._morph=t._childViewsMorph.insertContentBeforeMorph(r,n)))}function o(e){e._elementCreated&&this.willDestroyElement(e),e._elementInserted&&this.willRemoveElement(e)}function u(e,t){e._elementInserted=!1,e._morph=null,e._childViewsMorph=null,e._elementCreated&&(e._elementCreated=!1,this.didDestroyElement(e)),t&&this.destroyView(e)}var l=r["default"].hasDOM?new t["default"]:null;n.prototype.uuid=function(e){return void 0===e._uuid&&(e._uuid=++this._uuid,e._renderer=this),e._uuid},n.prototype.scheduleInsert=function(e,t){if(e._morph||e._elementCreated)throw new Error("You cannot insert a View that has already been rendered");e._morph=t;var r=this.uuid(e);this._inserts[r]=this.scheduleRender(this,function(){this._inserts[r]=null,this.renderTree(e)})},n.prototype.appendTo=function(e,t){var r=this._dom.appendMorph(t);this.scheduleInsert(e,r)},n.prototype.appendAttrTo=function(e,t,r){var n=this._dom.createAttrMorph(t,r);this.scheduleInsert(e,n)},n.prototype.replaceIn=function(e,t){var r;r=t.firstChild?this._dom.createMorph(t,t.firstChild,t.lastChild):this._dom.appendMorph(t),this.scheduleInsert(e,r)},n.prototype.remove=a,n.prototype.removeAndDestroy=function(e){this.remove(e,!0)},n.prototype.renderTree=i,n.prototype.insertElement=s,n.prototype.beforeRemove=o,n.prototype.afterRemove=u;var c=function(){};n.prototype.willCreateElement=c,n.prototype.createElement=c,n.prototype.didCreateElement=c,n.prototype.willInsertElement=c,n.prototype.didInsertElement=c,n.prototype.willRemoveElement=c,n.prototype.willDestroyElement=c,n.prototype.didDestroyElement=c,n.prototype.destroyView=c,n.prototype.childViews=c,e["default"]=n}),e("ember-metal",["exports","ember-metal/core","ember-metal/merge","ember-metal/instrumentation","ember-metal/utils","ember-metal/error","ember-metal/enumerable_utils","ember-metal/cache","ember-metal/platform/define_property","ember-metal/platform/create","ember-metal/array","ember-metal/logger","ember-metal/property_get","ember-metal/events","ember-metal/observer_set","ember-metal/property_events","ember-metal/properties","ember-metal/property_set","ember-metal/map","ember-metal/get_properties","ember-metal/set_properties","ember-metal/watch_key","ember-metal/chains","ember-metal/watch_path","ember-metal/watching","ember-metal/expand_properties","ember-metal/computed","ember-metal/alias","ember-metal/computed_macros","ember-metal/observer","ember-metal/mixin","ember-metal/binding","ember-metal/run_loop","ember-metal/libraries","ember-metal/is_none","ember-metal/is_empty","ember-metal/is_blank","ember-metal/is_present","ember-metal/keys","backburner","ember-metal/streams/utils","ember-metal/streams/stream"],function(e,r,n,i,a,s,o,u,l,c,h,m,d,f,p,v,g,b,y,_,w,x,C,E,O,P,S,A,N,T,k,M,I,V,R,j,D,F,L,B,H,z){"use strict";S.computed.empty=N.empty,S.computed.notEmpty=N.notEmpty,S.computed.none=N.none,S.computed.not=N.not,S.computed.bool=N.bool,S.computed.match=N.match,S.computed.equal=N.equal,S.computed.gt=N.gt,S.computed.gte=N.gte,S.computed.lt=N.lt,S.computed.lte=N.lte,S.computed.alias=A["default"],S.computed.oneWay=N.oneWay,S.computed.reads=N.oneWay,S.computed.readOnly=N.readOnly,S.computed.defaultTo=N.defaultTo,S.computed.deprecatingAlias=N.deprecatingAlias,S.computed.and=N.and,S.computed.or=N.or,S.computed.any=N.any,S.computed.collect=N.collect;var q=r["default"].Instrumentation={};q.instrument=i.instrument,q.subscribe=i.subscribe,q.unsubscribe=i.unsubscribe,q.reset=i.reset,r["default"].instrument=i.instrument,r["default"].subscribe=i.subscribe,r["default"]._Cache=u["default"],r["default"].generateGuid=a.generateGuid,r["default"].GUID_KEY=a.GUID_KEY,r["default"].create=c["default"],r["default"].keys=L["default"],r["default"].platform={defineProperty:g.defineProperty,hasPropertyAccessors:l.hasPropertyAccessors};var U=r["default"].ArrayPolyfills={};U.map=h.map,U.forEach=h.forEach,U.filter=h.filter,U.indexOf=h.indexOf,r["default"].Error=s["default"],r["default"].guidFor=a.guidFor,r["default"].META_DESC=a.META_DESC,r["default"].EMPTY_META=a.EMPTY_META,r["default"].meta=a.meta,r["default"].getMeta=a.getMeta,r["default"].setMeta=a.setMeta,r["default"].metaPath=a.metaPath,r["default"].inspect=a.inspect,r["default"].typeOf=a.typeOf,r["default"].tryCatchFinally=a.deprecatedTryCatchFinally,r["default"].isArray=a.isArray,r["default"].makeArray=a.makeArray,r["default"].canInvoke=a.canInvoke,r["default"].tryInvoke=a.tryInvoke,r["default"].tryFinally=a.deprecatedTryFinally,r["default"].wrap=a.wrap,r["default"].apply=a.apply,r["default"].applyStr=a.applyStr,r["default"].uuid=a.uuid,r["default"].Logger=m["default"],r["default"].get=d.get,r["default"].getWithDefault=d.getWithDefault,r["default"].normalizeTuple=d.normalizeTuple,r["default"]._getPath=d._getPath,r["default"].EnumerableUtils=o["default"],r["default"].on=f.on,r["default"].addListener=f.addListener,r["default"].removeListener=f.removeListener,r["default"]._suspendListener=f.suspendListener,r["default"]._suspendListeners=f.suspendListeners,r["default"].sendEvent=f.sendEvent,r["default"].hasListeners=f.hasListeners,r["default"].watchedEvents=f.watchedEvents,r["default"].listenersFor=f.listenersFor,r["default"].accumulateListeners=f.accumulateListeners,r["default"]._ObserverSet=p["default"],r["default"].propertyWillChange=v.propertyWillChange,r["default"].propertyDidChange=v.propertyDidChange,r["default"].overrideChains=v.overrideChains,r["default"].beginPropertyChanges=v.beginPropertyChanges,r["default"].endPropertyChanges=v.endPropertyChanges,r["default"].changeProperties=v.changeProperties,r["default"].defineProperty=g.defineProperty,r["default"].set=b.set,r["default"].trySet=b.trySet,r["default"].OrderedSet=y.OrderedSet,r["default"].Map=y.Map,r["default"].MapWithDefault=y.MapWithDefault,r["default"].getProperties=_["default"],r["default"].setProperties=w["default"],r["default"].watchKey=x.watchKey,r["default"].unwatchKey=x.unwatchKey,r["default"].flushPendingChains=C.flushPendingChains,r["default"].removeChainWatcher=C.removeChainWatcher,r["default"]._ChainNode=C.ChainNode,r["default"].finishChains=C.finishChains,r["default"].watchPath=E.watchPath,r["default"].unwatchPath=E.unwatchPath,r["default"].watch=O.watch,r["default"].isWatching=O.isWatching,r["default"].unwatch=O.unwatch,r["default"].rewatch=O.rewatch,r["default"].destroy=O.destroy,r["default"].expandProperties=P["default"],r["default"].ComputedProperty=S.ComputedProperty,r["default"].computed=S.computed,r["default"].cacheFor=S.cacheFor,r["default"].addObserver=T.addObserver,r["default"].observersFor=T.observersFor,r["default"].removeObserver=T.removeObserver,r["default"].addBeforeObserver=T.addBeforeObserver,r["default"]._suspendBeforeObserver=T._suspendBeforeObserver,r["default"]._suspendBeforeObservers=T._suspendBeforeObservers,r["default"]._suspendObserver=T._suspendObserver,r["default"]._suspendObservers=T._suspendObservers,r["default"].beforeObserversFor=T.beforeObserversFor,r["default"].removeBeforeObserver=T.removeBeforeObserver,r["default"].IS_BINDING=k.IS_BINDING,r["default"].required=k.required,r["default"].aliasMethod=k.aliasMethod,r["default"].observer=k.observer,r["default"].immediateObserver=k.immediateObserver,r["default"].beforeObserver=k.beforeObserver,r["default"].mixin=k.mixin,r["default"].Mixin=k.Mixin,r["default"].oneWay=M.oneWay,r["default"].bind=M.bind,r["default"].Binding=M.Binding,r["default"].isGlobalPath=M.isGlobalPath,r["default"].run=I["default"],r["default"].Backburner=B["default"],r["default"].libraries=new V["default"],r["default"].libraries.registerCoreLibrary("Ember",r["default"].VERSION),r["default"].isNone=R["default"],r["default"].isEmpty=j["default"],r["default"].isBlank=D["default"],r["default"].isPresent=F["default"],r["default"].merge=n["default"],r["default"].onerror=null,r["default"].__loader.registry["ember-debug"]&&t("ember-debug"),e["default"]=r["default"]}),e("ember-metal/alias",["exports","ember-metal/property_get","ember-metal/property_set","ember-metal/core","ember-metal/error","ember-metal/properties","ember-metal/computed","ember-metal/platform/create","ember-metal/utils","ember-metal/dependent_keys"],function(e,t,r,n,i,a,s,o,u,l){"use strict";function c(e){return new h(e)}function h(e){this.isDescriptor=!0,this.altKey=e,this._dependentKeys=[e]}function m(e,t,r){throw new i["default"]("Cannot set read-only property '"+t+"' on object: "+u.inspect(e))}function d(e,t,n){return a.defineProperty(e,t,null),r.set(e,t,n)}e.AliasedProperty=h,e["default"]=c,h.prototype=o["default"](a.Descriptor.prototype),h.prototype.get=function(e,r){return t.get(e,this.altKey)},h.prototype.set=function(e,t,n){return r.set(e,this.altKey,n)},h.prototype.willWatch=function(e,t){l.addDependentKeys(this,e,t,u.meta(e))},h.prototype.didUnwatch=function(e,t){l.removeDependentKeys(this,e,t,u.meta(e))},h.prototype.setup=function(e,t){var r=u.meta(e);r.watching[t]&&l.addDependentKeys(this,e,t,r)},h.prototype.teardown=function(e,t){var r=u.meta(e);r.watching[t]&&l.removeDependentKeys(this,e,t,r)},h.prototype.readOnly=function(){return this.set=m,this},h.prototype.oneWay=function(){return this.set=d,this},h.prototype._meta=void 0,h.prototype.meta=s.ComputedProperty.prototype.meta}),e("ember-metal/array",["exports"],function(e){"use strict";var t=Array.prototype,r=function(e){return e&&Function.prototype.toString.call(e).indexOf("[native code]")>-1},n=function(e,t){return r(e)?e:t},a=n(t.map,function(e){if(void 0===this||null===this||"function"!=typeof e)throw new TypeError;for(var t=Object(this),r=t.length>>>0,n=new Array(r),i=0;r>i;i++)i in t&&(n[i]=e.call(arguments[1],t[i],i,t));return n}),s=n(t.forEach,function(e){if(void 0===this||null===this||"function"!=typeof e)throw new TypeError;for(var t=Object(this),r=t.length>>>0,n=0;r>n;n++)n in t&&e.call(arguments[1],t[n],n,t)}),o=n(t.indexOf,function(e,t){null===t||void 0===t?t=0:0>t&&(t=Math.max(0,this.length+t));for(var r=t,n=this.length;n>r;r++)if(this[r]===e)return r;return-1}),u=n(t.lastIndexOf,function(e,t){var r,n=this.length;for(t=void 0===t?n-1:0>t?Math.ceil(t):Math.floor(t),0>t&&(t+=n),r=t;r>=0;r--)if(this[r]===e)return r;return-1}),l=n(t.filter,function(e,t){var r,n,i=[],a=this.length;for(r=0;a>r;r++)this.hasOwnProperty(r)&&(n=this[r],e.call(t,n,r,this)&&i.push(n));return i});i.SHIM_ES5&&(t.map=t.map||a,t.forEach=t.forEach||s,t.filter=t.filter||l,t.indexOf=t.indexOf||o,t.lastIndexOf=t.lastIndexOf||u),e.map=a,e.forEach=s,e.filter=l,e.indexOf=o,e.lastIndexOf=u}),e("ember-metal/binding",["exports","ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/utils","ember-metal/observer","ember-metal/run_loop","ember-metal/path_cache"],function(e,t,r,n,i,a,s,o){"use strict";function u(e,n){return r.get(o.isGlobal(n)?t["default"].lookup:e,n)}function l(e,t){this._direction=void 0,this._from=t,this._to=e,this._readyToSync=void 0,this._oneWay=void 0}function c(e,t){for(var r in t)t.hasOwnProperty(r)&&(e[r]=t[r])}function h(e,t,r){return new l(t,r).connect(e)}function m(e,t,r){return new l(t,r).oneWay().connect(e)}e.bind=h,e.oneWay=m,e.Binding=l,t["default"].LOG_BINDINGS=!1||!!t["default"].ENV.LOG_BINDINGS,l.prototype={copy:function(){var e=new l(this._to,this._from);return this._oneWay&&(e._oneWay=!0),e},from:function(e){return this._from=e,this},to:function(e){return this._to=e,this},oneWay:function(){return this._oneWay=!0,this},toString:function(){var e=this._oneWay?"[oneWay]":"";return"Ember.Binding<"+i.guidFor(this)+">("+this._from+" -> "+this._to+")"+e},connect:function(e){var t=this._from,r=this._to;return n.trySet(e,r,u(e,t)),a.addObserver(e,t,this,this.fromDidChange),this._oneWay||a.addObserver(e,r,this,this.toDidChange),this._readyToSync=!0,this},disconnect:function(e){var t=!this._oneWay;return a.removeObserver(e,this._from,this,this.fromDidChange),t&&a.removeObserver(e,this._to,this,this.toDidChange),this._readyToSync=!1,this},fromDidChange:function(e){this._scheduleSync(e,"fwd")},toDidChange:function(e){this._scheduleSync(e,"back")},_scheduleSync:function(e,t){var r=this._direction;void 0===r&&(s["default"].schedule("sync",this,this._sync,e),this._direction=t),"back"===r&&"fwd"===t&&(this._direction="fwd")},_sync:function(e){var i=t["default"].LOG_BINDINGS;if(!e.isDestroyed&&this._readyToSync){var s=this._direction,l=this._from,c=this._to;if(this._direction=void 0,"fwd"===s){var h=u(e,this._from);i&&t["default"].Logger.log(" ",this.toString(),"->",h,e),this._oneWay?n.trySet(e,c,h):a._suspendObserver(e,c,this,this.toDidChange,function(){n.trySet(e,c,h)})}else if("back"===s){var m=r.get(e,this._to);i&&t["default"].Logger.log(" ",this.toString(),"<-",m,e),a._suspendObserver(e,l,this,this.fromDidChange,function(){n.trySet(o.isGlobal(l)?t["default"].lookup:e,l,m)})}}}},c(l,{from:function(e){var t=this;return new t(void 0,e)},to:function(e){var t=this;return new t(e,void 0)},oneWay:function(e,t){var r=this;return new r(void 0,e).oneWay(t)}}),e.isGlobalPath=o.isGlobal}),e("ember-metal/cache",["exports","ember-metal/dictionary"],function(e,t){"use strict";function r(e,r){this.store=t["default"](null),this.size=0,this.misses=0,this.hits=0,this.limit=e,this.func=r}e["default"]=r;var n=function(){};r.prototype={set:function(e,t){return this.limit>this.size&&(this.size++,void 0===t?this.store[e]=n:this.store[e]=t),t},get:function(e){var t=this.store[e];return void 0===t?(this.misses++,t=this.set(e,this.func(e))):t===n?(this.hits++,t=void 0):this.hits++,t},purge:function(){this.store=t["default"](null),this.size=0,this.hits=0,this.misses=0}}}),e("ember-metal/chains",["exports","ember-metal/core","ember-metal/property_get","ember-metal/utils","ember-metal/array","ember-metal/watch_key"],function(e,t,r,n,i,a){"use strict";function s(e){return e.match(p)[0]}function o(e){return e&&"object"==typeof e}function u(){if(0!==v.length){var e=v;v=[],i.forEach.call(e,function(e){e[0].add(e[1])}),f("Watching an undefined global, Ember expects watched globals to be setup by the time the run loop is flushed, check for typos",0===v.length); +}}function l(e,t,r){if(o(e)){var i=n.meta(e),s=i.chainWatchers;i.hasOwnProperty("chainWatchers")||(s=i.chainWatchers={}),s[t]||(s[t]=[]),s[t].push(r),a.watchKey(e,t,i)}}function c(e,t,r){if(o(e)){var n=e.__ember_meta__;if(!n||n.hasOwnProperty("chainWatchers")){var i=n&&n.chainWatchers;if(i&&i[t]){i=i[t];for(var s=0,u=i.length;u>s;s++)if(i[s]===r){i.splice(s,1);break}}a.unwatchKey(e,t,n)}}}function h(e,t,r){this._parent=e,this._key=t,this._watching=void 0===r,this._value=r,this._paths={},this._watching&&(this._object=e.value(),this._object&&l(this._object,this._key,this)),this._parent&&"@each"===this._parent._key&&this.value()}function m(e,t){if(e){var n=e.__ember_meta__;if(!n||n.proto!==e){if("@each"===t)return r.get(e,t);var i=e[t],a=null!==i&&"object"==typeof i&&i.isDescriptor?i:void 0;return a&&a._cacheable?n.cache&&t in n.cache?n.cache[t]:void 0:r.get(e,t)}}}function d(e){var t,r,i,a=e.__ember_meta__;if(a){if(r=a.chainWatchers)for(var s in r)if(r.hasOwnProperty(s)&&(i=r[s]))for(var o=0,u=i.length;u>o;o++)i[o].didChange(null);t=a.chains,t&&t.value()!==e&&(n.meta(e).chains=t=t.copy(e))}}e.flushPendingChains=u,e.finishChains=d,e.removeChainWatcher=c,e.ChainNode=h;var f=t["default"].warn,p=/^([^\.]+)/,v=[];h.prototype={value:function(){if(void 0===this._value&&this._watching){var e=this._parent.value();this._value=m(e,this._key)}return this._value},destroy:function(){if(this._watching){var e=this._object;e&&c(e,this._key,this),this._watching=!1}},copy:function(e){var t,r=new h(null,null,e),n=this._paths;for(t in n)n[t]<=0||r.add(t);return r},add:function(e){var t,n,i,a,o;if(o=this._paths,o[e]=(o[e]||0)+1,t=this.value(),n=r.normalizeTuple(t,e),n[0]&&n[0]===t)e=n[1],i=s(e),e=e.slice(i.length+1);else{if(!n[0])return v.push([this,e]),void(n.length=0);a=n[0],i=e.slice(0,0-(n[1].length+1)),e=n[1]}n.length=0,this.chain(i,e,a)},remove:function(e){var t,n,i,a,o;o=this._paths,o[e]>0&&o[e]--,t=this.value(),n=r.normalizeTuple(t,e),n[0]===t?(e=n[1],i=s(e),e=e.slice(i.length+1)):(a=n[0],i=e.slice(0,0-(n[1].length+1)),e=n[1]),n.length=0,this.unchain(i,e)},count:0,chain:function(e,t,r){var n,i=this._chains;i||(i=this._chains={}),n=i[e],n||(n=i[e]=new h(this,e,r)),n.count++,t&&(e=s(t),t=t.slice(e.length+1),n.chain(e,t))},unchain:function(e,t){var r=this._chains,n=r[e];if(t&&t.length>1){var i=s(t),a=t.slice(i.length+1);n.unchain(i,a)}n.count--,n.count<=0&&(delete r[n._key],n.destroy())},willChange:function(e){var t=this._chains;if(t)for(var r in t)t.hasOwnProperty(r)&&t[r].willChange(e);this._parent&&this._parent.chainWillChange(this,this._key,1,e)},chainWillChange:function(e,t,r,n){this._key&&(t=this._key+"."+t),this._parent?this._parent.chainWillChange(this,t,r+1,n):(r>1&&n.push(this.value(),t),t="this."+t,this._paths[t]>0&&n.push(this.value(),t))},chainDidChange:function(e,t,r,n){this._key&&(t=this._key+"."+t),this._parent?this._parent.chainDidChange(this,t,r+1,n):(r>1&&n.push(this.value(),t),t="this."+t,this._paths[t]>0&&n.push(this.value(),t))},didChange:function(e){if(this._watching){var t=this._parent.value();t!==this._object&&(c(this._object,this._key,this),this._object=t,l(t,this._key,this)),this._value=void 0,this._parent&&"@each"===this._parent._key&&this.value()}var r=this._chains;if(r)for(var n in r)r.hasOwnProperty(n)&&r[n].didChange(e);null!==e&&this._parent&&this._parent.chainDidChange(this,this._key,1,e)}}}),e("ember-metal/computed",["exports","ember-metal/property_set","ember-metal/utils","ember-metal/expand_properties","ember-metal/error","ember-metal/properties","ember-metal/property_events","ember-metal/dependent_keys"],function(e,t,r,n,i,a,s,o){"use strict";function u(){}function l(e,t){this.isDescriptor=!0,"function"==typeof e?(e.__ember_arity=e.length,this._getter=e,e.__ember_arity>1&&(this._setter=e)):(this._getter=e.get,this._setter=e.set,this._setter&&void 0===this._setter.__ember_arity&&(this._setter.__ember_arity=this._setter.length)),this._dependentKeys=void 0,this._suspended=void 0,this._meta=void 0,this._cacheable=t&&void 0!==t.cacheable?t.cacheable:!0,this._dependentKeys=t&&t.dependentKeys,this._readOnly=t&&(void 0!==t.readOnly||!!t.readOnly)||!1}function c(e){for(var t=0,r=e.length;r>t;t++)e[t].didChange(null)}function h(e){var t;arguments.length>1&&(t=[].slice.call(arguments),e=t.pop());var r=new l(e);return t&&r.property.apply(r,t),r}function m(e,t){var r=e.__ember_meta__,n=r&&r.cache,i=n&&n[t];return i===u?void 0:i}e.ComputedProperty=l,e.computed=h,e.cacheFor=m;var d=r.meta;l.prototype=new a.Descriptor;var f=l.prototype;f.cacheable=function(e){return this._cacheable=e!==!1,this},f["volatile"]=function(){return this._cacheable=!1,this},f.readOnly=function(e){return this._readOnly=void 0===e||!!e,this},f.property=function(){var e,t=function(t){e.push(t)};e=[];for(var r=0,i=arguments.length;i>r;r++)n["default"](arguments[r],t);return this._dependentKeys=e,this},f.meta=function(e){return 0===arguments.length?this._meta||{}:(this._meta=e,this)},f.didChange=function(e,t){if(this._cacheable&&this._suspended!==e){var r=d(e);r.cache&&void 0!==r.cache[t]&&(r.cache[t]=void 0,o.removeDependentKeys(this,e,t,r))}},f.get=function(e,t){var r,n,i,a;if(this._cacheable){i=d(e),n=i.cache;var s=n&&n[t];if(s===u)return void 0;if(void 0!==s)return s;r=this._getter.call(e,t),n=i.cache,n||(n=i.cache={}),void 0===r?n[t]=u:n[t]=r,a=i.chainWatchers&&i.chainWatchers[t],a&&c(a),o.addDependentKeys(this,e,t,i)}else r=this._getter.call(e,t);return r},f.set=function(e,t,r){var n=this._suspended;this._suspended=e;try{this._set(e,t,r)}finally{this._suspended=n}},f._set=function(e,n,l){var c,h,m=this._cacheable,f=this._setter,p=d(e,m),v=p.cache,g=!1;if(this._readOnly)throw new i["default"]('Cannot set read-only property "'+n+'" on object: '+r.inspect(e));if(m&&v&&void 0!==v[n]&&(v[n]!==u&&(c=v[n]),g=!0),!f)return a.defineProperty(e,n,null,c),void t.set(e,n,l);if(h=2===f.__ember_arity?f.call(e,n,l):f.call(e,n,l,c),!g||c!==h){var b=p.watching[n];return b&&s.propertyWillChange(e,n),g&&(v[n]=void 0),m&&(g||o.addDependentKeys(this,e,n,p),v||(v=p.cache={}),void 0===h?v[n]=u:v[n]=h),b&&s.propertyDidChange(e,n),h}},f.teardown=function(e,t){var r=d(e);return r.cache&&(t in r.cache&&o.removeDependentKeys(this,e,t,r),this._cacheable&&delete r.cache[t]),null},m.set=function(e,t,r){void 0===r?e[t]=u:e[t]=r},m.get=function(e,t){var r=e[t];return r===u?void 0:r},m.remove=function(e,t){e[t]=void 0}}),e("ember-metal/computed_macros",["exports","ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/computed","ember-metal/is_empty","ember-metal/is_none","ember-metal/alias"],function(e,t,r,n,i,a,s,o){"use strict";function u(e,t){for(var n={},i=0;in;n++)r[n]=arguments[n];var a=i.computed(function(){return e.apply(this,[u(this,r)])});return a.property.apply(a,r)}}function c(e){return i.computed(e+".length",function(){return a["default"](r.get(this,e))})}function h(e){return i.computed(e+".length",function(){return!a["default"](r.get(this,e))})}function m(e){return i.computed(e,function(){return s["default"](r.get(this,e))})}function d(e){return i.computed(e,function(){return!r.get(this,e)})}function f(e){return i.computed(e,function(){return!!r.get(this,e)})}function p(e,t){return i.computed(e,function(){var n=r.get(this,e);return"string"==typeof n?t.test(n):!1})}function v(e,t){return i.computed(e,function(){return r.get(this,e)===t})}function g(e,t){return i.computed(e,function(){return r.get(this,e)>t})}function b(e,t){return i.computed(e,function(){return r.get(this,e)>=t})}function y(e,t){return i.computed(e,function(){return r.get(this,e)u;u++)c=m[u],h=n(o,c),h[a]=(h[a]||0)+1,r.watch(t,c,s)}function s(e,t,a,s){var o,u,l,c,h,m=e._dependentKeys;if(m)for(o=i(s),u=0,l=m.length;l>u;u++)c=m[u],h=n(o,c),h[a]=(h[a]||0)-1,r.unwatch(t,c,s)}e.addDependentKeys=a,e.removeDependentKeys=s}),e("ember-metal/deprecate_property",["exports","ember-metal/core","ember-metal/platform/define_property","ember-metal/properties","ember-metal/property_get","ember-metal/property_set"],function(e,t,r,n,i,a){"use strict";function s(e,t,s){function o(){}r.hasPropertyAccessors&&n.defineProperty(e,t,{configurable:!0,enumerable:!1,set:function(e){o(),a.set(this,s,e)},get:function(){return o(),i.get(this,s)}})}e.deprecateProperty=s}),e("ember-metal/dictionary",["exports","ember-metal/platform/create"],function(e,t){"use strict";function r(e){var r=t["default"](e);return r._dict=null,delete r._dict,r}e["default"]=r}),e("ember-metal/enumerable_utils",["exports","ember-metal/array"],function(e,t){"use strict";function r(e,r,n){return e.map?e.map(r,n):t.map.call(e,r,n)}function n(e,r,n){return e.forEach?e.forEach(r,n):t.forEach.call(e,r,n)}function i(e,r,n){return e.filter?e.filter(r,n):t.filter.call(e,r,n)}function a(e,r,n){return e.indexOf?e.indexOf(r,n):t.indexOf.call(e,r,n)}function s(e,t){return void 0===t?[]:r(t,function(t){return a(e,t)})}function o(e,t){var r=a(e,t);-1===r&&e.push(t)}function u(e,t){var r=a(e,t);-1!==r&&e.splice(r,1)}function l(e,t,r,n){for(var i,a,s=[].concat(n),o=[],u=6e4,l=t,c=r;s.length;)i=c>u?u:c,0>=i&&(i=0),a=s.splice(0,u),a=[l,i].concat(a),l+=u,c-=i,o=o.concat(m.apply(e,a));return o}function c(e,t,r,n){return e.replace?e.replace(t,r,n):l(e,t,r,n)}function h(e,t){var r=[];return n(e,function(e){a(t,e)>=0&&r.push(e)}),r}e.map=r,e.forEach=n,e.filter=i,e.indexOf=a,e.indexesOf=s,e.addObject=o,e.removeObject=u,e._replace=l,e.replace=c,e.intersection=h;var m=Array.prototype.splice;e["default"]={_replace:l,addObject:o,filter:i,forEach:n,indexOf:a,indexesOf:s,intersection:h,map:r,removeObject:u,replace:c}}),e("ember-metal/environment",["exports","ember-metal/core"],function(e,t){"use strict";var r,n="undefined"!=typeof window&&"undefined"!=typeof document&&"undefined"!=typeof document.createElement&&!t["default"].ENV.disableBrowserEnvironment;r=n?{hasDOM:!0,isChrome:!!window.chrome&&!window.opera,location:window.location,history:window.history,userAgent:window.navigator.userAgent,global:window}:{hasDOM:!1,isChrome:!1,location:null,history:null,userAgent:"Lynx (textmode)",global:null},e["default"]=r}),e("ember-metal/error",["exports","ember-metal/platform/create"],function(e,t){"use strict";function r(){var e=Error.apply(this,arguments);Error.captureStackTrace&&Error.captureStackTrace(this,i.Error);for(var t=0;t=0;i-=3)if(t===e[i]&&r===e[i+1]){n=i;break}return n}function a(e,t){var i,a=r.meta(e,!0),s=a.listeners;return s?s.__source__!==e&&(s=a.listeners=n["default"](s),s.__source__=e):(s=a.listeners=n["default"](null),s.__source__=e),i=s[t],i&&i.__source__!==e?(i=s[t]=s[t].slice(),i.__source__=e):i||(i=s[t]=[],i.__source__=e),i}function s(e,t,r){var n=e.__ember_meta__,a=n&&n.listeners&&n.listeners[t];if(a){for(var s=[],o=a.length-3;o>=0;o-=3){var u=a[o],l=a[o+1],c=a[o+2],h=i(r,u,l);-1===h&&(r.push(u,l,c),s.push(u,l,c))}return s}}function o(e,t,r,n,s){n||"function"!=typeof r||(n=r,r=null);var o=a(e,t),u=i(o,r,n),l=0;s&&(l|=v),-1===u&&(o.push(r,n,l),"function"==typeof e.didAddListener&&e.didAddListener(t,r,n))}function u(e,t,r,n){function s(r,n){var s=a(e,t),o=i(s,r,n);-1!==o&&(s.splice(o,3),"function"==typeof e.didRemoveListener&&e.didRemoveListener(t,r,n))}if(n||"function"!=typeof r||(n=r,r=null),n)s(r,n);else{var o=e.__ember_meta__,u=o&&o.listeners&&o.listeners[t];if(!u)return;for(var l=u.length-3;l>=0;l-=3)s(u[l],u[l+1])}}function l(e,t,n,s,o){function u(){return o.call(n)}function l(){-1!==h&&(c[h+2]&=~g)}s||"function"!=typeof n||(s=n,n=null);var c=a(e,t),h=i(c,n,s);return-1!==h&&(c[h+2]|=g),r.tryFinally(u,l)}function c(e,t,n,s,o){function u(){return o.call(n)}function l(){for(var e=0,t=f.length;t>e;e++){var r=f[e];p[e][r+2]&=~g}}s||"function"!=typeof n||(s=n,n=null);var c,h,m,d,f=[],p=[];for(m=0,d=t.length;d>m;m++){c=t[m],h=a(e,c);var v=i(h,n,s);-1!==v&&(h[v+2]|=g,f.push(v),p.push(h))}return r.tryFinally(u,l)}function h(e){var t=e.__ember_meta__.listeners,r=[];if(t)for(var n in t)"__source__"!==n&&t[n]&&r.push(n);return r}function m(e,n,i,a){if(e!==t["default"]&&"function"==typeof e.sendEvent&&e.sendEvent(n,i),!a){var s=e.__ember_meta__;a=s&&s.listeners&&s.listeners[n]}if(a){for(var o=a.length-3;o>=0;o-=3){var l=a[o],c=a[o+1],h=a[o+2];c&&(h&g||(h&v&&u(e,n,l,c),l||(l=e),"string"==typeof c?i?r.applyStr(l,c,i):l[c]():i?r.apply(l,c,i):c.call(l)))}return!0}}function d(e,t){var r=e.__ember_meta__,n=r&&r.listeners&&r.listeners[t];return!(!n||!n.length)}function f(e,t){var r=[],n=e.__ember_meta__,i=n&&n.listeners&&n.listeners[t];if(!i)return r;for(var a=0,s=i.length;s>a;a+=3){var o=i[a],u=i[a+1];r.push([o,u])}return r}function p(){for(var e=arguments.length,t=Array(e),r=0;e>r;r++)t[r]=arguments[r];var n=t.pop(),i=t;return n.__ember_listens__=i,n}e.accumulateListeners=s,e.addListener=o,e.suspendListener=l,e.suspendListeners=c,e.watchedEvents=h,e.sendEvent=m,e.hasListeners=d,e.listenersFor=f,e.on=p,e.removeListener=u;var v=1,g=2}),e("ember-metal/expand_properties",["exports","ember-metal/error","ember-metal/enumerable_utils","ember-metal/utils"],function(e,t,r,n){"use strict";function i(e,i){if(e.indexOf(" ")>-1)throw new t["default"]("Brace expanded properties cannot contain spaces, e.g. 'user.{firstName, lastName}' should be 'user.{firstName,lastName}'");if("string"===n.typeOf(e)){var o=e.split(s),u=[o];r.forEach(o,function(e,t){e.indexOf(",")>=0&&(u=a(u,e.split(","),t))}),r.forEach(u,function(e){i(e.join(""))})}else i(e)}function a(e,t,n){var i=[];return r.forEach(e,function(e){r.forEach(t,function(t){var r=e.slice(0);r[n]=t,i.push(r)})}),i}e["default"]=i;var s=/\{|\}/}),e("ember-metal/get_properties",["exports","ember-metal/property_get","ember-metal/utils"],function(e,t,r){"use strict";function n(e){var n={},i=arguments,a=1;2===arguments.length&&"array"===r.typeOf(arguments[1])&&(a=0,i=arguments[1]);for(var s=i.length;s>a;a++)n[i[a]]=t.get(e,i[a]);return n}e["default"]=n}),e("ember-metal/injected_property",["exports","ember-metal/core","ember-metal/computed","ember-metal/alias","ember-metal/properties","ember-metal/platform/create"],function(e,t,r,n,i,a){"use strict";function s(e,t){this.type=e,this.name=t,this._super$Constructor(o),c.oneWay.call(this)}function o(e){var t=this[e],r=null!==t&&"object"==typeof t&&t.isDescriptor?t:void 0;return this.container.lookup(r.type+":"+(r.name||e))}s.prototype=a["default"](i.Descriptor.prototype);var u=s.prototype,l=r.ComputedProperty.prototype,c=n.AliasedProperty.prototype;u._super$Constructor=r.ComputedProperty,u.get=l.get,u.readOnly=l.readOnly,u.teardown=l.teardown,e["default"]=s}),e("ember-metal/instrumentation",["exports","ember-metal/core","ember-metal/utils"],function(e,t,r){"use strict";function n(e,t,n,a){if(arguments.length<=3&&"function"==typeof t&&(a=n,n=t,t=void 0),0===u.length)return n.call(a);var s=t||{},o=i(e,function(){return s});if(o){var l=function(){return n.call(a)},c=function(e){s.exception=e};return r.tryCatchFinally(l,c,o)}return n.call(a)}function i(e,r){var n=l[e];if(n||(n=c(e)),0!==n.length){var i,a=r(),s=t["default"].STRUCTURED_PROFILE;s&&(i=e+": "+a.object,console.time(i));var o,u,m=n.length,d=new Array(m),f=h();for(o=0;m>o;o++)u=n[o],d[o]=u.before(e,f,a);return function(){var t,r,o,u=h();for(t=0,r=n.length;r>t;t++)o=n[t],o.after(e,u,a,d[t]);s&&console.timeEnd(i)}}}function a(e,t){for(var r,n=e.split("."),i=[],a=0,s=n.length;s>a;a++)r=n[a],"*"===r?i.push("[^\\.]*"):i.push(r);i=i.join("\\."),i+="(\\..*)?";var o={pattern:e,regex:new RegExp("^"+i+"$"),object:t};return u.push(o),l={},o}function s(e){for(var t,r=0,n=u.length;n>r;r++)u[r]===e&&(t=r);u.splice(t,1),l={}}function o(){u.length=0,l={}}e.instrument=n,e._instrumentStart=i,e.subscribe=a,e.unsubscribe=s,e.reset=o;var u=[],l={},c=function(e){for(var t,r=[],n=0,i=u.length;i>n;n++)t=u[n],t.regex.test(e)&&r.push(t.object);return l[e]=r,r},h=function(){var e="undefined"!=typeof window?window.performance||{}:{},t=e.now||e.mozNow||e.webkitNow||e.msNow||e.oNow;return t?t.bind(e):function(){return+new Date}}();e.subscribers=u}),e("ember-metal/is_blank",["exports","ember-metal/is_empty"],function(e,t){"use strict";function r(e){return t["default"](e)||"string"==typeof e&&null===e.match(/\S/)}e["default"]=r}),e("ember-metal/is_empty",["exports","ember-metal/property_get","ember-metal/is_none"],function(e,t,r){"use strict";function n(e){var n=r["default"](e);if(n)return n;if("number"==typeof e.size)return!e.size;var i=typeof e;if("object"===i){var a=t.get(e,"size");if("number"==typeof a)return!a}if("number"==typeof e.length&&"function"!==i)return!e.length;if("object"===i){var s=t.get(e,"length");if("number"==typeof s)return!s}return!1}e["default"]=n}),e("ember-metal/is_none",["exports"],function(e){"use strict";function t(e){return null===e||void 0===e}e["default"]=t}),e("ember-metal/is_present",["exports","ember-metal/is_blank"],function(e,t){"use strict";function r(e){return!t["default"](e)}e["default"]=r}),e("ember-metal/keys",["exports","ember-metal/platform/define_property"],function(e,t){"use strict";var r=Object.keys;r&&t.canDefineNonEnumerableProperties||(r=function(){var e=Object.prototype.hasOwnProperty,t=!{toString:null}.propertyIsEnumerable("toString"),r=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],n=r.length;return function(i){if("object"!=typeof i&&("function"!=typeof i||null===i))throw new TypeError("Object.keys called on non-object");var a,s,o=[];for(a in i)"_super"!==a&&0!==a.lastIndexOf("__",0)&&e.call(i,a)&&o.push(a);if(t)for(s=0;n>s;s++)e.call(i,r[s])&&o.push(r[s]);return o}}()),e["default"]=r}),e("ember-metal/libraries",["exports","ember-metal/core","ember-metal/enumerable_utils"],function(e,t,r){"use strict";function n(){this._registry=[],this._coreLibIndex=0}n.prototype={constructor:n,_getLibraryByName:function(e){for(var t=this._registry,r=t.length,n=0;r>n;n++)if(t[n].name===e)return t[n]},register:function(e,t,r){var n=this._registry.length;this._getLibraryByName(e)||(r&&(n=this._coreLibIndex++),this._registry.splice(n,0,{name:e,version:t}))},registerCoreLibrary:function(e,t){this.register(e,t,!0)},deRegister:function(e){var t,n=this._getLibraryByName(e);n&&(t=r.indexOf(this._registry,n),this._registry.splice(t,1))},each:function(e){r.forEach(this._registry,function(t){e(t.name,t.version)})}},e["default"]=n}),e("ember-metal/logger",["exports","ember-metal/core","ember-metal/error"],function(e,t,r){"use strict";function n(){return this}function i(e){var r,n;t["default"].imports.console?r=t["default"].imports.console:"undefined"!=typeof console&&(r=console);var i="object"==typeof r?r[e]:null;return i?"function"==typeof i.bind?(n=i.bind(r),n.displayName="console."+e,n):"function"==typeof i.apply?(n=function(){i.apply(r,arguments)},n.displayName="console."+e,n):function(){var e=Array.prototype.join.call(arguments,", ");i(e)}:void 0}function a(e,t){if(!e)try{throw new r["default"]("assertion failed: "+t)}catch(n){setTimeout(function(){throw n},0)}}e["default"]={log:i("log")||n,warn:i("warn")||n,error:i("error")||n,info:i("info")||n,debug:i("debug")||i("info")||n,assert:i("assert")||a}}),e("ember-metal/map",["exports","ember-metal/utils","ember-metal/array","ember-metal/platform/create","ember-metal/deprecate_property"],function(e,t,r,n,a){"use strict";function s(e){throw new TypeError(""+Object.prototype.toString.call(e)+" is not a function")}function o(e){throw new TypeError("Constructor "+e+" requires 'new'")}function u(e){var t=n["default"](null);for(var r in e)t[r]=e[r];return t}function l(e,t){var r=e._keys.copy(),n=u(e._values);return t._keys=r,t._values=n,t.size=e.size,t}function c(){this instanceof c?(this.clear(),this._silenceRemoveDeprecation=!1):o("OrderedSet")}function h(){this instanceof this.constructor?(this._keys=c.create(),this._keys._silenceRemoveDeprecation=!0,this._values=n["default"](null),this.size=0):o("OrderedSet")}function m(e){this._super$constructor(),this.defaultValue=e.defaultValue}e.OrderedSet=c,e.Map=h,e.MapWithDefault=m,c.create=function(){var e=this;return new e},c.prototype={constructor:c,clear:function(){this.presenceSet=n["default"](null),this.list=[],this.size=0},add:function(e,r){var n=r||t.guidFor(e),i=this.presenceSet,a=this.list;return i[n]!==!0&&(i[n]=!0,this.size=a.push(e)),this},remove:function(e,t){return this["delete"](e,t)},"delete":function(e,n){var i=n||t.guidFor(e),a=this.presenceSet,s=this.list;if(a[i]===!0){delete a[i];var o=r.indexOf.call(s,e);return o>-1&&s.splice(o,1),this.size=s.length,!0}return!1},isEmpty:function(){return 0===this.size},has:function(e){if(0===this.size)return!1;var r=t.guidFor(e),n=this.presenceSet;return n[r]===!0},forEach:function(e){if("function"!=typeof e&&s(e),0!==this.size){var t,r=this.list,n=arguments.length;if(2===n)for(t=0;ts;s++)n=i[s],e[n]=r[n];return e}e["default"]=r}),e("ember-metal/mixin",["exports","ember-metal/core","ember-metal/merge","ember-metal/array","ember-metal/platform/create","ember-metal/property_get","ember-metal/property_set","ember-metal/utils","ember-metal/expand_properties","ember-metal/properties","ember-metal/computed","ember-metal/binding","ember-metal/observer","ember-metal/events","ember-metal/streams/utils"],function(e,t,r,n,i,a,s,o,u,l,c,h,m,d,f){function p(){var e,t=this.__nextSuper;if(t){var r=arguments.length;return this.__nextSuper=null,e=0===r?t.call(this):1===r?t.call(this,arguments[0]):2===r?t.call(this,arguments[0],arguments[1]):t.apply(this,arguments),this.__nextSuper=t,e}}function v(e){var t=o.meta(e,!0),r=t.mixins;return r?t.hasOwnProperty("mixins")||(r=t.mixins=i["default"](r)):r=t.mixins={},r}function g(e){return"function"==typeof e&&e.isMethod!==!1&&e!==Boolean&&e!==Object&&e!==Number&&e!==Array&&e!==Date&&e!==String}function b(e,t){var r;return t instanceof R?(r=o.guidFor(t),e[r]?G:(e[r]=t,t.properties)):t}function y(e,t,r,n){var i;return i=r[e]||n[e],t[e]&&(i=i?i.concat(t[e]):t[e]),i}function _(e,t,r,n,a,s){var u;if(void 0===n[t]&&(u=a[t]),!u){var l=s[t],h=null!==l&&"object"==typeof l&&l.isDescriptor?l:void 0;u=h}return void 0!==u&&u instanceof c.ComputedProperty?(r=i["default"](r),r._getter=o.wrap(r._getter,u._getter),u._setter&&(r._setter?r._setter=o.wrap(r._setter,u._setter):r._setter=u._setter),r):r}function w(e,t,r,n,i){var a;if(void 0===i[t]&&(a=n[t]),a=a||e[t],void 0===a||"function"!=typeof a)return r;var s;return Q&&(s=r.__hasSuper,void 0===s&&(s=r.toString().indexOf("_super")>-1,r.__hasSuper=s)),Q===!1||s?o.wrap(r,a):r}function x(e,t,r,n){var i=n[t]||e[t];return i?"function"==typeof i.concat?null===r||void 0===r?i:i.concat(r):o.makeArray(i).concat(r):o.makeArray(r)}function C(e,t,n,i){var a=i[t]||e[t];if(!a)return n;var s=r["default"]({},a),o=!1;for(var u in n)if(n.hasOwnProperty(u)){var l=n[u];g(l)?(o=!0,s[u]=w(e,u,l,a,{})):s[u]=l}return o&&(s._super=p),s}function E(e,t,r,i,a,s,o,u){if(r instanceof l.Descriptor){if(r===U&&a[t])return G;r._getter&&(r=_(i,t,r,s,a,e)),a[t]=r,s[t]=void 0}else o&&n.indexOf.call(o,t)>=0||"concatenatedProperties"===t||"mergedProperties"===t?r=x(e,t,r,s):u&&n.indexOf.call(u,t)>=0?r=C(e,t,r,s):g(r)&&(r=w(e,t,r,s,a)),a[t]=void 0,s[t]=r}function O(e,t,r,i,a,s){function u(e){delete r[e],delete i[e]}for(var l,c,h,m,d,f,p=0,v=e.length;v>p;p++)if(l=e[p],c=b(t,l),c!==G)if(c){f=o.meta(a),a.willMergeMixin&&a.willMergeMixin(c),m=y("concatenatedProperties",c,i,a),d=y("mergedProperties",c,i,a);for(h in c)c.hasOwnProperty(h)&&(s.push(h),E(a,h,c[h],f,r,i,m,d));c.hasOwnProperty("toString")&&(a.toString=c.toString)}else l.mixins&&(O(l.mixins,t,r,i,a,s),l._without&&n.forEach.call(l._without,u))}function P(e,t,r,n){if(Y.test(t)){var a=n.bindings;a?n.hasOwnProperty("bindings")||(a=n.bindings=i["default"](n.bindings)):a=n.bindings={},a[t]=r}}function S(e,t,r){var n=function(r){m._suspendObserver(e,t,null,o,function(){s.trySet(e,t,r.value())})},o=function(){r.setValue(a.get(e,t),n)};s.set(e,t,r.value()),m.addObserver(e,t,null,o),r.subscribe(n),void 0===e._streamBindingSubscriptions&&(e._streamBindingSubscriptions=i["default"](null)),e._streamBindingSubscriptions[t]=n}function A(e,t){var r,n,i,a=t.bindings;if(a){for(r in a)if(n=a[r]){if(i=r.slice(0,-7),f.isStream(n)){S(e,i,n);continue}n instanceof h.Binding?(n=n.copy(),n.to(i)):n=new h.Binding(i,n),n.connect(e),e[r]=n}t.bindings={}}}function N(e,t){return A(e,t||o.meta(e)),e}function T(e,t,r,n,i){var a,s,o=t.methodName;return n[o]||i[o]?(a=i[o],t=n[o]):(s=e[o])&&null!==s&&"object"==typeof s&&s.isDescriptor?(t=s,a=void 0):(t=void 0,a=e[o]),{desc:t,value:a}}function k(e,t,r,n,i){var a=r[n];if(a)for(var s=0,o=a.length;o>s;s++)i(e,a[s],null,t)}function M(e,t,r){var n=e[t];"function"==typeof n&&(k(e,t,n,"__ember_observesBefore__",m.removeBeforeObserver),k(e,t,n,"__ember_observes__",m.removeObserver),k(e,t,n,"__ember_listens__",d.removeListener)),"function"==typeof r&&(k(e,t,r,"__ember_observesBefore__",m.addBeforeObserver),k(e,t,r,"__ember_observes__",m.addObserver),k(e,t,r,"__ember_listens__",d.addListener))}function I(e,t,r){var n,i,a,s={},u={},c=o.meta(e),h=[];e._super=p,O(t,v(e),s,u,e,h);for(var m=0,d=h.length;d>m;m++)if(n=h[m],"constructor"!==n&&u.hasOwnProperty(n)&&(a=s[n],i=u[n],a!==U)){for(;a&&a instanceof L;){var f=T(e,a,c,s,u);a=f.desc,i=f.value}(void 0!==a||void 0!==i)&&(M(e,n,i),P(e,n,i,c),l.defineProperty(e,n,a,i,c))}return r||N(e,c),e}function V(e){for(var t=arguments.length,r=Array(t>1?t-1:0),n=1;t>n;n++)r[n-1]=arguments[n];return I(e,r,!1),e}function R(e,t){this.properties=t;var r=e&&e.length;if(r>0){for(var n=new Array(r),i=0;r>i;i++){var a=e[i];a instanceof R?n[i]=a:n[i]=new R(void 0,a)}this.mixins=n}else this.mixins=void 0;this.ownerConstructor=void 0}function j(e,t,r){var n=o.guidFor(e);if(r[n])return!1;if(r[n]=!0,e===t)return!0;for(var i=e.mixins,a=i?i.length:0;--a>=0;)if(j(i[a],t,r))return!0;return!1}function D(e,t,r){if(!r[o.guidFor(t)])if(r[o.guidFor(t)]=!0,t.properties){var i=t.properties;for(var a in i)i.hasOwnProperty(a)&&(e[a]=!0)}else t.mixins&&n.forEach.call(t.mixins,function(t){D(e,t,r)})}function F(){return U}function L(e){this.isDescriptor=!0,this.methodName=e}function B(e){return new L(e)}function H(){for(var e=arguments.length,r=Array(e),n=0;e>n;n++)r[n]=arguments[n];var i,a=r.slice(-1)[0],s=function(e){i.push(e)},o=r.slice(0,-1);"function"!=typeof a&&(a=r[0],o=r.slice(1)),i=[];for(var l=0;le;e++){arguments[e]}return H.apply(this,arguments)}function q(){for(var e=arguments.length,r=Array(e),n=0;e>n;n++)r[n]=arguments[n];var i,a=r.slice(-1)[0],s=function(e){i.push(e)},o=r.slice(0,-1);"function"!=typeof a&&(a=r[0],o=r.slice(1)),i=[];for(var l=0;l-1,Y=/^.+Binding$/;e["default"]=R,R._apply=I,R.applyPartial=function(e){var t=W.call(arguments,1);return I(e,t,!0)},R.finishPartial=N,t["default"].anyUnprocessedMixins=!1,R.create=function(){for(var e=arguments.length,r=Array(e),n=0;e>n;n++)r[n]=arguments[n];t["default"].anyUnprocessedMixins=!0;var i=this;return new i(r,void 0)};var $=R.prototype;$.reopen=function(){var e;this.properties?(e=new R(void 0,this.properties),this.properties=void 0,this.mixins=[e]):this.mixins||(this.mixins=[]);var t,r=arguments.length,n=this.mixins;for(t=0;r>t;t++)e=arguments[t],e instanceof R?n.push(e):n.push(new R(void 0,e));return this},$.apply=function(e){return I(e,[this],!1)},$.applyPartial=function(e){return I(e,[this],!0)},$.detect=function(e){if(!e)return!1;if(e instanceof R)return j(e,this,{});var t=e.__ember_meta__,r=t&&t.mixins;return r?!!r[o.guidFor(this)]:!1},$.without=function(){for(var e=arguments.length,t=Array(e),r=0;e>r;r++)t[r]=arguments[r];var n=new R([this]);return n._without=t,n},$.keys=function(){var e={},t={},r=[];D(e,this,t);for(var n in e)e.hasOwnProperty(n)&&r.push(n);return r},R.mixins=function(e){var t=e.__ember_meta__,r=t&&t.mixins,n=[];if(!r)return n;for(var i in r){var a=r[i];a.properties||n.push(a)}return n},U=new l.Descriptor,U.toString=function(){return"(Required Property)"},L.prototype=new l.Descriptor,e.IS_BINDING=Y,e.REQUIRED=U}),e("ember-metal/observer",["exports","ember-metal/watching","ember-metal/array","ember-metal/events"],function(e,t,r,n){"use strict";function i(e){return e+v}function a(e){return e+g}function s(e,r,a,s){return n.addListener(e,i(r),a,s),t.watch(e,r),this}function o(e,t){return n.listenersFor(e,i(t))}function u(e,r,a,s){return t.unwatch(e,r),n.removeListener(e,i(r),a,s),this}function l(e,r,i,s){return n.addListener(e,a(r),i,s),t.watch(e,r),this}function c(e,t,r,i,s){return n.suspendListener(e,a(t),r,i,s)}function h(e,t,r,a,s){return n.suspendListener(e,i(t),r,a,s)}function m(e,t,i,s,o){var u=r.map.call(t,a);return n.suspendListeners(e,u,i,s,o)}function d(e,t,a,s,o){var u=r.map.call(t,i);return n.suspendListeners(e,u,a,s,o)}function f(e,t){return n.listenersFor(e,a(t))}function p(e,r,i,s){return t.unwatch(e,r),n.removeListener(e,a(r),i,s),this}e.addObserver=s,e.observersFor=o,e.removeObserver=u,e.addBeforeObserver=l,e._suspendBeforeObserver=c,e._suspendObserver=h,e._suspendBeforeObservers=m,e._suspendObservers=d,e.beforeObserversFor=f,e.removeBeforeObserver=p;var v=":change",g=":before"}),e("ember-metal/observer_set",["exports","ember-metal/utils","ember-metal/events"],function(e,t,r){"use strict";function n(){this.clear()}e["default"]=n,n.prototype.add=function(e,r,n){var i,a=this.observerSet,s=this.observers,o=t.guidFor(e),u=a[o];return u||(a[o]=u={}),i=u[r],void 0===i&&(i=s.push({sender:e,keyName:r,eventName:n,listeners:[]})-1,u[r]=i),s[i].listeners},n.prototype.flush=function(){var e,t,n,i,a=this.observers;for(this.clear(),e=0,t=a.length;t>e;++e)n=a[e],i=n.sender,i.isDestroying||i.isDestroyed||r.sendEvent(i,n.eventName,[i,n.keyName],n.listeners)},n.prototype.clear=function(){this.observerSet={},this.observers=[]}}),e("ember-metal/path_cache",["exports","ember-metal/cache"],function(e,t){"use strict";function r(e){return h.get(e)}function n(e){return m.get(e)}function i(e){return d.get(e)}function a(e){return-1!==f.get(e)}function s(e){return p.get(e)}function o(e){return v.get(e)}e.isGlobal=r,e.isGlobalPath=n,e.hasThis=i,e.isPath=a,e.getFirstKey=s,e.getTailPath=o;var u=/^[A-Z$]/,l=/^[A-Z$].*[\.]/,c="this.",h=new t["default"](1e3,function(e){return u.test(e)}),m=new t["default"](1e3,function(e){return l.test(e)}),d=new t["default"](1e3,function(e){return 0===e.lastIndexOf(c,0)}),f=new t["default"](1e3,function(e){return e.indexOf(".")}),p=new t["default"](1e3,function(e){var t=f.get(e);return-1===t?e:e.slice(0,t)}),v=new t["default"](1e3,function(e){var t=f.get(e);return-1!==t?e.slice(t+1):void 0}),g={isGlobalCache:h,isGlobalPathCache:m,hasThisCache:d,firstDotIndexCache:f,firstKeyCache:p,tailPathCache:v};e.caches=g}),e("ember-metal/platform/create",["exports","ember-metal/platform/define_properties"],function(e,t){"REMOVE_USE_STRICT: true";var r;if(!Object.create||Object.create(null).hasOwnProperty){var n,i=!({__proto__:null}instanceof Object);n=i||"undefined"==typeof document?function(){return{__proto__:null}}:function(){function e(){}var t=document.createElement("iframe"),r=document.body||document.documentElement;t.style.display="none",r.appendChild(t),t.src="javascript:";var i=t.contentWindow.Object.prototype;return r.removeChild(t),t=null,delete i.constructor,delete i.hasOwnProperty,delete i.propertyIsEnumerable,delete i.isPrototypeOf,delete i.toLocaleString,delete i.toString,delete i.valueOf,e.prototype=i,n=function(){return new e},new e},r=Object.create=function(e,r){function i(){}var a;if(null===e)a=n();else{if("object"!=typeof e&&"function"!=typeof e)throw new TypeError("Object prototype may only be an Object or null");i.prototype=e,a=new i}return void 0!==r&&t["default"](a,r),a}}else r=Object.create;e["default"]=r}),e("ember-metal/platform/define_properties",["exports","ember-metal/platform/define_property"],function(e,t){"use strict";var r=Object.defineProperties;r||(r=function(e,r){for(var n in r)r.hasOwnProperty(n)&&"__proto__"!==n&&t.defineProperty(e,n,r[n]);return e},Object.defineProperties=r),e["default"]=r}),e("ember-metal/platform/define_property",["exports"],function(e){"use strict";var t=function(e){if(e)try{var t=5,r={};if(e(r,"a",{configurable:!0,enumerable:!0,get:function(){return t},set:function(e){t=e}}),5!==r.a)return;if(r.a=10,10!==t)return;e(r,"a",{configurable:!0,enumerable:!1,writable:!0,value:!0});for(var n in r)if("a"===n)return;if(r.a!==!0)return;if(e(r,"a",{enumerable:!1}),r.a!==!0)return;return e}catch(i){return}}(Object.defineProperty),r=!!t;if(r&&"undefined"!=typeof document){var n=function(){try{return t(document.createElement("div"),"definePropertyOnDOM",{}),!0}catch(e){}return!1}();n||(t=function(e,t,r){var n;return n="object"==typeof Node?e instanceof Node:"object"==typeof e&&"number"==typeof e.nodeType&&"string"==typeof e.nodeName,n?e[t]=r.value:Object.defineProperty(e,t,r)})}r||(t=function(e,t,r){r.get||(e[t]=r.value)});var i=r,a=r;e.hasES5CompliantDefineProperty=r,e.defineProperty=t,e.hasPropertyAccessors=i,e.canDefineNonEnumerableProperties=a}),e("ember-metal/properties",["exports","ember-metal/core","ember-metal/utils","ember-metal/platform/define_property","ember-metal/property_events"],function(e,t,r,n,i){"use strict";function a(){this.isDescriptor=!0}function s(e){return function(e){}}function o(e){return function(){var t=this.__ember_meta__;return t&&t.values[e]}}function u(e,t,s,o,u){var l,c,h,m;u||(u=r.meta(e));var d=u.watching[t];return l=e[t],c=null!==l&&"object"==typeof l&&l.isDescriptor?l:void 0,h=void 0!==d&&d>0,c&&c.teardown(e,t),s instanceof a?(m=s,e[t]=m,s.setup&&s.setup(e,t)):null==s?(m=o,e[t]=o):(m=s,n.defineProperty(e,t,s)),h&&i.overrideChains(e,t,u),e.didDefineProperty&&e.didDefineProperty(e,t,m),this}e.Descriptor=a,e.MANDATORY_SETTER_FUNCTION=s,e.DEFAULT_GETTER_FUNCTION=o,e.defineProperty=u}),e("ember-metal/property_events",["exports","ember-metal/utils","ember-metal/events","ember-metal/observer_set"],function(e,t,r,n){"use strict";function i(e,t){var r=e.__ember_meta__,n=r&&r.watching[t]>0||"length"===t,i=r&&r.proto,a=e[t],o=null!==a&&"object"==typeof a&&a.isDescriptor?a:void 0;n&&i!==e&&(o&&o.willChange&&o.willChange(e,t),s(e,t,r),c(e,t,r),v(e,t))}function a(e,t){var r=e.__ember_meta__,n=r&&r.watching[t]>0||"length"===t,i=r&&r.proto,a=e[t],s=null!==a&&"object"==typeof a&&a.isDescriptor?a:void 0;i!==e&&(s&&s.didChange&&s.didChange(e,t),(n||"length"===t)&&(r&&r.deps&&r.deps[t]&&o(e,t,r),h(e,t,r,!1),g(e,t)))}function s(e,t,r){if(!e.isDestroying){var n;if(r&&r.deps&&(n=r.deps[t])){var a=b,s=!a;s&&(a=b={}),l(i,e,n,t,a,r),s&&(b=null)}}}function o(e,t,r){if(!e.isDestroying){var n;if(r&&r.deps&&(n=r.deps[t])){var i=y,s=!i;s&&(i=y={}),l(a,e,n,t,i,r),s&&(y=null)}}}function u(e){var t=[];for(var r in e)t.push(r);return t}function l(e,r,n,i,a,s){var o,l,c,h,m,d=t.guidFor(r),f=a[d];if(f||(f=a[d]={}),!f[i]&&(f[i]=!0,n))for(o=u(n),c=0;cn;n++)s[n].willChange(o);for(n=0,a=o.length;a>n;n+=2)i(o[n],o[n+1])}}function h(e,t,r,n){if(r&&r.hasOwnProperty("chainWatchers")&&r.chainWatchers[t]){var i,s,o=r.chainWatchers[t],u=n?null:[];for(i=0,s=o.length;s>i;i++)o[i].didChange(u);if(!n)for(i=0,s=u.length;s>i;i+=2)a(u[i],u[i+1])}}function m(e,t,r){h(e,t,r,!0)}function d(){x++}function f(){x--,0>=x&&(_.clear(),w.flush())}function p(e,r){d(),t.tryFinally(e,f,r)}function v(e,t){if(!e.isDestroying){var n,i,a=t+":before";x?(n=_.add(e,t,a),i=r.accumulateListeners(e,a,n),r.sendEvent(e,a,[e,t],i)):r.sendEvent(e,a,[e,t])}}function g(e,t){if(!e.isDestroying){var n,i=t+":change";x?(n=w.add(e,t,i),r.accumulateListeners(e,i,n)):r.sendEvent(e,i,[e,t])}}e.propertyWillChange=i,e.propertyDidChange=a,e.overrideChains=m,e.beginPropertyChanges=d,e.endPropertyChanges=f,e.changeProperties=p;var b,y,_=new n["default"],w=new n["default"],x=0}),e("ember-metal/property_get",["exports","ember-metal/core","ember-metal/error","ember-metal/path_cache","ember-metal/platform/define_property","ember-metal/is_none"],function(e,t,r,n,i,a){"use strict";function s(e,r){if(""===r)return e;if(r||"string"!=typeof e||(r=e,e=t["default"].lookup),a["default"](e))return l(e,r);var i,s=(e.__ember_meta__,e[r]),o=null!==s&&"object"==typeof s&&s.isDescriptor?s:void 0;return void 0===o&&n.isPath(r)?l(e,r):o?o.get(e,r):(i=e[r],void 0!==i||"object"!=typeof e||r in e||"function"!=typeof e.unknownProperty?i:e.unknownProperty(r))}function o(e,r){var i,a=n.hasThis(r),o=!a&&n.isGlobal(r);return e||o?(a&&(r=r.slice(5)),(!e||o)&&(e=t["default"].lookup),o&&n.isPath(r)&&(i=r.match(h)[0],e=s(e,i),r=r.slice(i.length+1)),u(r),[e,r]):[void 0,""]}function u(e){if(!e||0===e.length)throw new r["default"]("Object in path "+e+" could not be found or was destroyed.")}function l(e,t){var r,i,a,u,l;for(r=n.hasThis(t),(!e||r)&&(a=o(e,t),e=a[0],t=a[1],a.length=0),i=t.split("."),l=i.length,u=0;null!=e&&l>u;u++)if(e=s(e,i[u],!0),e&&e.isDestroyed)return void 0;return e}function c(e,t,r){var n=s(e,t);return void 0===n?r:n}e.get=s,e.normalizeTuple=o,e._getPath=l,e.getWithDefault=c;var h=/^([^\.]+)/;e["default"]=s}),e("ember-metal/property_set",["exports","ember-metal/core","ember-metal/property_get","ember-metal/property_events","ember-metal/properties","ember-metal/error","ember-metal/path_cache","ember-metal/platform/define_property"],function(e,t,r,n,i,a,s,o){"use strict";function u(e,r,i,a){if("string"==typeof e&&(i=r,r=e,e=t["default"].lookup),e===t["default"].lookup)return l(e,r,i,a);var o,u,c;e&&(o=e.__ember_meta__,u=e[r],c=null!==u&&"object"==typeof u&&u.isDescriptor?u:void 0);var h,m;if((!e||void 0===c)&&s.isPath(r))return l(e,r,i,a);if(c)c.set(e,r,i);else{if(null!==e&&void 0!==i&&"object"==typeof e&&e[r]===i)return i;h="object"==typeof e&&!(r in e),h&&"function"==typeof e.setUnknownProperty?e.setUnknownProperty(r,i):o&&o.watching[r]>0?(o.proto!==e&&(m=e[r]),i!==m&&(n.propertyWillChange(e,r),e[r]=i,n.propertyDidChange(e,r))):e[r]=i}return i}function l(e,t,n,i){var s;if(s=t.slice(t.lastIndexOf(".")+1),t=t===s?s:t.slice(0,t.length-(s.length+1)),"this"!==t&&(e=r._getPath(e,t)),!s||0===s.length)throw new a["default"]("Property set failed: You passed an empty path");if(!e){if(i)return;throw new a["default"]('Property set failed: object in path "'+t+'" could not be found or was destroyed.')}return u(e,s,n)}function c(e,t,r){return u(e,t,r,!0)}e.set=u,e.trySet=c}),e("ember-metal/run_loop",["exports","ember-metal/core","ember-metal/utils","ember-metal/array","ember-metal/property_events","backburner"],function(e,t,r,n,i,a){"use strict";function s(e){u.currentRunLoop=e}function o(e,t){u.currentRunLoop=t}function u(){return c.run.apply(c,arguments)}function l(){!u.currentRunLoop}var c=new a["default"](["sync","actions","destroy"],{GUID_KEY:r.GUID_KEY,sync:{before:i.beginPropertyChanges,after:i.endPropertyChanges},defaultQueue:"actions",onBegin:s,onEnd:o,onErrorTarget:t["default"],onErrorMethod:"onerror"});e["default"]=u,u.join=function(){return c.join.apply(c,arguments)},u.bind=function(){for(var e=arguments.length,t=Array(e),r=0;e>r;r++)t[r]=arguments[r];return function(){for(var e=arguments.length,r=Array(e),n=0;e>n;n++)r[n]=arguments[n];return u.join.apply(u,t.concat(r))}},u.backburner=c,u.currentRunLoop=null,u.queues=c.queueNames,u.begin=function(){c.begin()},u.end=function(){c.end()},u.schedule=function(){l(),c.schedule.apply(c,arguments)},u.hasScheduledTimers=function(){return c.hasTimers()},u.cancelTimers=function(){c.cancelTimers()},u.sync=function(){c.currentInstance&&c.currentInstance.queues.sync.flush()},u.later=function(){return c.later.apply(c,arguments)},u.once=function(){for(var e=arguments.length,t=Array(e),r=0;e>r;r++)t[r]=arguments[r];return l(),t.unshift("actions"),c.scheduleOnce.apply(c,t)},u.scheduleOnce=function(){return l(),c.scheduleOnce.apply(c,arguments)},u.next=function(){for(var e=arguments.length,t=Array(e),r=0;e>r;r++)t[r]=arguments[r];return t.push(1),c.later.apply(c,t)},u.cancel=function(e){return c.cancel(e)},u.debounce=function(){return c.debounce.apply(c,arguments)},u.throttle=function(){return c.throttle.apply(c,arguments)},u._addQueue=function(e,t){-1===n.indexOf.call(u.queues,e)&&u.queues.splice(n.indexOf.call(u.queues,t)+1,0,e)}}),e("ember-metal/set_properties",["exports","ember-metal/property_events","ember-metal/property_set","ember-metal/keys"],function(e,t,r,n){"use strict";function i(e,i){return i&&"object"==typeof i?(t.changeProperties(function(){for(var t,a=n["default"](i),s=0,o=a.length;o>s;s++)t=a[s],r.set(e,t,i[t])}),e):e}e["default"]=i}),e("ember-metal/streams/conditional",["exports","ember-metal/streams/stream","ember-metal/streams/utils","ember-metal/platform/create"],function(e,t,r,n){"use strict";function i(e,t,n){return r.isStream(e)?new a(e,t,n):e?t:n}function a(e,t,r){this.init(),this.oldTestResult=void 0,this.test=e,this.consequent=t,this.alternate=r}e["default"]=i,a.prototype=n["default"](t["default"].prototype),a.prototype.valueFn=function(){var e=this.oldTestResult,t=!!r.read(this.test);if(t!==e){switch(e){case!0:r.unsubscribe(this.consequent,this.notify,this);break;case!1:r.unsubscribe(this.alternate,this.notify,this);break;case void 0:r.subscribe(this.test,this.notify,this)}switch(t){case!0:r.subscribe(this.consequent,this.notify,this);break;case!1:r.subscribe(this.alternate,this.notify,this)}this.oldTestResult=t}return t?r.read(this.consequent):r.read(this.alternate)}}),e("ember-metal/streams/simple",["exports","ember-metal/merge","ember-metal/streams/stream","ember-metal/platform/create","ember-metal/streams/utils"],function(e,t,r,n,i){"use strict";function a(e){this.init(),this.source=e,i.isStream(e)&&e.subscribe(this._didChange,this)}a.prototype=n["default"](r["default"].prototype),t["default"](a.prototype,{valueFn:function(){return i.read(this.source)},setValue:function(e){var t=this.source;i.isStream(t)&&t.setValue(e)},setSource:function(e){var t=this.source;e!==t&&(i.isStream(t)&&t.unsubscribe(this._didChange,this),i.isStream(e)&&e.subscribe(this._didChange,this),this.source=e,this.notify())},_didChange:function(){this.notify()},_super$destroy:r["default"].prototype.destroy,destroy:function(){return this._super$destroy()?(i.isStream(this.source)&&this.source.unsubscribe(this._didChange,this),this.source=void 0,!0):void 0}}),e["default"]=a}),e("ember-metal/streams/stream",["exports","ember-metal/platform/create","ember-metal/path_cache"],function(e,t,r){"use strict";function n(e,t){this.next=null,this.prev=null,this.callback=e,this.context=t}function i(e){this.init(),this.valueFn=e}n.prototype.removeFrom=function(e){var t=this.next,r=this.prev;r?r.next=t:e.subscriberHead=t,t?t.prev=r:e.subscriberTail=r},i.prototype={isStream:!0,init:function(){this.state="dirty",this.cache=void 0,this.subscriberHead=null,this.subscriberTail=null,this.children=void 0,this._label=void 0},get:function(e){var n=r.getFirstKey(e),i=r.getTailPath(e);void 0===this.children&&(this.children=t["default"](null));var a=this.children[n];return void 0===a&&(a=this._makeChildStream(n,e),this.children[n]=a),void 0===i?a:a.get(i)},value:function(){return"clean"===this.state?this.cache:"dirty"===this.state?(this.state="clean",this.cache=this.valueFn()):void 0},valueFn:function(){throw new Error("Stream error: valueFn not implemented")},setValue:function(){throw new Error("Stream error: setValue not implemented")},notify:function(){this.notifyExcept()},notifyExcept:function(e,t){"clean"===this.state&&(this.state="dirty",this._notifySubscribers(e,t))},subscribe:function(e,t){var r=new n(e,t,this);if(null===this.subscriberHead)this.subscriberHead=this.subscriberTail=r;else{var i=this.subscriberTail;i.next=r,r.prev=i,this.subscriberTail=r}var a=this;return function(){r.removeFrom(a)}},unsubscribe:function(e,t){for(var r=this.subscriberHead;r;){var n=r.next;r.callback===e&&r.context===t&&r.removeFrom(this),r=n}},_notifySubscribers:function(e,t){for(var r=this.subscriberHead;r;){var n=r.next,i=r.callback,a=r.context;r=n,(i!==e||a!==t)&&(void 0===a?i(this):i.call(a,this))}},destroy:function(){if("destroyed"!==this.state){this.state="destroyed";var e=this.children;for(var t in e)e[t].destroy();return this.subscriberHead=this.subscriberTail=null,!0}},isGlobal:function(){for(var e=this;void 0!==e;){if(e._isRoot)return e._isGlobal;e=e.source}}},e["default"]=i}),e("ember-metal/streams/stream_binding",["exports","ember-metal/platform/create","ember-metal/merge","ember-metal/run_loop","ember-metal/streams/stream"],function(e,t,r,n,i){"use strict";function a(e){this.init(),this.stream=e,this.senderCallback=void 0,this.senderContext=void 0,this.senderValue=void 0,e.subscribe(this._onNotify,this)}a.prototype=t["default"](i["default"].prototype),r["default"](a.prototype,{valueFn:function(){return this.stream.value()},_onNotify:function(){this._scheduleSync(void 0,void 0,this)},setValue:function(e,t,r){this._scheduleSync(e,t,r)},_scheduleSync:function(e,t,r){void 0===this.senderCallback&&void 0===this.senderContext?(this.senderCallback=t,this.senderContext=r,this.senderValue=e,n["default"].schedule("sync",this,this._sync)):this.senderContext!==this&&(this.senderCallback=t,this.senderContext=r,this.senderValue=e)},_sync:function(){if("destroyed"!==this.state){this.senderContext!==this&&this.stream.setValue(this.senderValue);var e=this.senderCallback,t=this.senderContext;this.senderCallback=void 0,this.senderContext=void 0,this.senderValue=void 0,this.state="clean",this.notifyExcept(e,t)}},_super$destroy:i["default"].prototype.destroy,destroy:function(){return this._super$destroy()?(this.stream.unsubscribe(this._onNotify,this),!0):void 0}}),e["default"]=a}),e("ember-metal/streams/utils",["exports","./stream"],function(e,t){"use strict";function r(e){return e&&e.isStream}function n(e,t,r){e&&e.isStream&&e.subscribe(t,r)}function i(e,t,r){e&&e.isStream&&e.unsubscribe(t,r)}function a(e){return e&&e.isStream?e.value():e}function s(e){for(var t=e.length,r=new Array(t),n=0;t>n;n++)r[n]=a(e[n]);return r}function o(e){var t={};for(var r in e)t[r]=a(e[r]);return t}function u(e){for(var t=e.length,n=!1,i=0;t>i;i++)if(r(e[i])){n=!0;break}return n}function l(e){var t=!1;for(var n in e)if(r(e[n])){t=!0;break}return t}function c(e,r){var i=u(e);if(i){var a,o,l=new t["default"](function(){return s(e).join(r)});for(a=0,o=e.length;o>a;a++)n(e[a],l.notify,l);return l}return e.join(r)}function h(e,i){if(r(e)){var a=new t["default"](i);return n(e,a.notify,a),a}return i()}e.isStream=r,e.subscribe=n,e.unsubscribe=i,e.read=a,e.readArray=s,e.readHash=o,e.scanArray=u,e.scanHash=l,e.concat=c,e.chain=h}),e("ember-metal/utils",["exports","ember-metal/core","ember-metal/platform/create","ember-metal/platform/define_property","ember-metal/array"],function(e,t,r,n,i){function a(){return++C}function s(e){var t={};t[e]=1;for(var r in t)if(r===e)return r;return e}function o(e,t){t||(t=E);var r=t+a();return e&&(null===e[S]?e[S]=r:(A.value=r,e.__defineNonEnumerable?e.__defineNonEnumerable(I):n.defineProperty(e,S,A))),r}function u(e){if(void 0===e)return"(undefined)";if(null===e)return"(null)";var t,r=typeof e;switch(r){case"number":return t=O[e],t||(t=O[e]="nu"+e),t;case"string":return t=P[e],t||(t=P[e]="st"+a()),t;case"boolean":return e?"(true)":"(false)";default:return e[S]?e[S]:e===Object?"(Object)":e===Array?"(Array)":(t=E+a(),null===e[S]?e[S]=t:(A.value=t,e.__defineNonEnumerable?e.__defineNonEnumerable(I):n.defineProperty(e,S,A)),t)}}function l(e){this.watching={},this.cache=void 0,this.cacheMeta=void 0,this.source=e,this.deps=void 0,this.listeners=void 0,this.mixins=void 0,this.bindings=void 0,this.chains=void 0,this.values=void 0,this.proto=void 0}function c(e,t){var i=e.__ember_meta__;return t===!1?i||D:(i?i.source!==e&&(e.__defineNonEnumerable?e.__defineNonEnumerable(M):n.defineProperty(e,"__ember_meta__",k),i=r["default"](i),i.watching=r["default"](i.watching),i.cache=void 0,i.cacheMeta=void 0,i.source=e,e.__ember_meta__=i):(n.canDefineNonEnumerableProperties&&(e.__defineNonEnumerable?e.__defineNonEnumerable(M):n.defineProperty(e,"__ember_meta__",k)),i=new l(e),e.__ember_meta__=i),i)}function h(e,t){var r=c(e,!1);return r[t]}function m(e,t,r){var n=c(e,!0);return n[t]=r,r}function d(e,t,n){for(var i,a,s=c(e,n),o=0,u=t.length;u>o;o++){if(i=t[o],a=s[i]){if(a.__ember_source__!==e){if(!n)return void 0;a=s[i]=r["default"](a),a.__ember_source__=e}}else{if(!n)return void 0;a=s[i]={__ember_source__:e}}s=a}return a}function f(e,t){function r(){var r,n=this&&this.__nextSuper,i=arguments.length;if(this&&(this.__nextSuper=t),0===i)r=e.call(this);else if(1===i)r=e.call(this,arguments[0]);else if(2===i)r=e.call(this,arguments[0],arguments[1]);else{for(var a=new Array(i),s=0;i>s;s++)a[s]=arguments[s];r=w(this,e,a)}return this&&(this.__nextSuper=n),r}return r.wrappedFunction=e,r.__ember_observes__=e.__ember_observes__,r.__ember_observesBefore__=e.__ember_observesBefore__,r.__ember_listens__=e.__ember_listens__,r}function p(e){var r,n;return"undefined"==typeof R&&(r="ember-runtime/mixins/array",t["default"].__loader.registry[r]&&(R=t["default"].__loader.require(r)["default"])),!e||e.setInterval?!1:Array.isArray&&Array.isArray(e)?!0:R&&R.detect(e)?!0:(n=y(e),"array"===n?!0:void 0!==e.length&&"object"===n?!0:!1)}function v(e){return null===e||void 0===e?[]:p(e)?e:[e]}function g(e,t){return!(!e||"function"!=typeof e[t])}function b(e,t,r){return g(e,t)?r?x(e,t,r):x(e,t):void 0}function y(e){var r,n;return"undefined"==typeof U&&(n="ember-runtime/system/object",t["default"].__loader.registry[n]&&(U=t["default"].__loader.require(n)["default"])),r=null===e||void 0===e?String(e):z[W.call(e)]||"object","function"===r?U&&U.detect(e)&&(r="class"):"object"===r&&(e instanceof Error?r="error":U&&e instanceof U?r="instance":e instanceof Date&&(r="date")),r}function _(e){var t=y(e);if("array"===t)return"["+e+"]";if("object"!==t)return e+"";var r,n=[];for(var i in e)if(e.hasOwnProperty(i)){if(r=e[i],"toString"===r)continue;"function"===y(r)&&(r="function() { ... }"),r&&"function"!=typeof r.toString?n.push(i+": "+W.call(r)):n.push(i+": "+r)}return"{"+n.join(", ")+"}"}function w(e,t,r){var n=r&&r.length;if(!r||!n)return t.call(e);switch(n){case 1:return t.call(e,r[0]);case 2:return t.call(e,r[0],r[1]);case 3:return t.call(e,r[0],r[1],r[2]);case 4:return t.call(e,r[0],r[1],r[2],r[3]);case 5:return t.call(e,r[0],r[1],r[2],r[3],r[4]);default:return t.apply(e,r)}}function x(e,t,r){var n=r&&r.length;if(!r||!n)return e[t]();switch(n){case 1:return e[t](r[0]);case 2:return e[t](r[0],r[1]);case 3:return e[t](r[0],r[1],r[2]);case 4:return e[t](r[0],r[1],r[2],r[3]);case 5:return e[t](r[0],r[1],r[2],r[3],r[4]);default:return e[t].apply(e,r)}}e.uuid=a,e.generateGuid=o,e.guidFor=u,e.getMeta=h,e.setMeta=m,e.metaPath=d,e.wrap=f,e.makeArray=v,e.tryInvoke=b,e.inspect=_,e.apply=w,e.applyStr=x,e.meta=c,e.typeOf=y,e.isArray=p,e.canInvoke=g;var C=0,E="ember",O=[],P={},S=s("__ember"+ +new Date),A={writable:!0,configurable:!0,enumerable:!1,value:null},N={configurable:!0,writable:!0,enumerable:!1,value:void 0},T={configurable:!0,writable:!0,enumerable:!1,value:null},k={writable:!0,configurable:!0,enumerable:!1,value:null},M={name:"__ember_meta__",descriptor:k},I={name:S,descriptor:T},V={name:"__nextSuper",descriptor:N};l.prototype={chainWatchers:null},n.canDefineNonEnumerableProperties||(l.prototype.__preventPlainObject__=!0,l.prototype.toJSON=function(){});var R,j,D=new l(null),F=function(){var e=0;try{try{}finally{throw e++,new Error("needsFinallyFixTest")}}catch(t){}return 1!==e}();j=F?function(e,t,r){var n,i,a;r=r||this;try{n=e.call(r)}finally{try{i=t.call(r)}catch(s){a=s}}if(a)throw a;return void 0===i?n:i}:function(e,t,r){var n,i;r=r||this;try{n=e.call(r)}finally{i=t.call(r)}return void 0===i?n:i};var L,B=function(){return j.apply(this,arguments)};L=F?function(e,t,r,n){var i,a,s;n=n||this;try{i=e.call(n)}catch(o){i=t.call(n,o)}finally{try{a=r.call(n)}catch(u){s=u}}if(s)throw s;return void 0===a?i:a}:function(e,t,r,n){var i,a;n=n||this;try{i=e.call(n)}catch(s){i=t.call(n,s)}finally{a=r.call(n)}return void 0===a?i:a};var H=function(){return L.apply(this,arguments)},z={},q="Boolean Number String Function Array Date RegExp Object".split(" ");i.forEach.call(q,function(e){z["[object "+e+"]"]=e.toLowerCase()});var U,W=Object.prototype.toString;e.GUID_DESC=A,e.EMBER_META_PROPERTY=M,e.GUID_KEY_PROPERTY=I,e.NEXT_SUPER_PROPERTY=V,e.GUID_KEY=S,e.META_DESC=k,e.EMPTY_META=D,e.tryCatchFinally=L,e.deprecatedTryCatchFinally=H,e.tryFinally=j,e.deprecatedTryFinally=B}),e("ember-metal/watch_key",["exports","ember-metal/core","ember-metal/utils","ember-metal/platform/define_property","ember-metal/properties"],function(e,t,r,n,i){"use strict";function a(e,t,n){if("length"!==t||"array"!==r.typeOf(e)){var i=n||r.meta(e),a=i.watching;if(a[t])a[t]=(a[t]||0)+1;else{a[t]=1;var s=e[t],o=null!==s&&"object"==typeof s&&s.isDescriptor?s:void 0;o&&o.willWatch&&o.willWatch(e,t),"function"==typeof e.willWatchProperty&&e.willWatchProperty(t)}}}function s(e,t,n){var i=n||r.meta(e),a=i.watching;if(1===a[t]){a[t]=0;var s=e[t],o=null!==s&&"object"==typeof s&&s.isDescriptor?s:void 0;o&&o.didUnwatch&&o.didUnwatch(e,t),"function"==typeof e.didUnwatchProperty&&e.didUnwatchProperty(t)}else a[t]>1&&a[t]--}e.watchKey=a,e.unwatchKey=s}),e("ember-metal/watch_path",["exports","ember-metal/utils","ember-metal/chains"],function(e,t,r){"use strict";function n(e,n){var i=n||t.meta(e),a=i.chains;return a?a.value()!==e&&(a=i.chains=a.copy(e)):a=i.chains=new r.ChainNode(null,null,e),a}function i(e,r,i){if("length"!==r||"array"!==t.typeOf(e)){var a=i||t.meta(e),s=a.watching;s[r]?s[r]=(s[r]||0)+1:(s[r]=1,n(e,a).add(r))}}function a(e,r,i){var a=i||t.meta(e),s=a.watching;1===s[r]?(s[r]=0,n(e,a).remove(r)):s[r]>1&&s[r]--}e.watchPath=i,e.unwatchPath=a}),e("ember-metal/watching",["exports","ember-metal/utils","ember-metal/chains","ember-metal/watch_key","ember-metal/watch_path","ember-metal/path_cache"],function(e,t,r,n,i,a){"use strict";function s(e,r,s){("length"!==r||"array"!==t.typeOf(e))&&(a.isPath(r)?i.watchPath(e,r,s):n.watchKey(e,r,s))}function o(e,t){var r=e.__ember_meta__;return(r&&r.watching[t])>0}function u(e,r,s){("length"!==r||"array"!==t.typeOf(e))&&(a.isPath(r)?i.unwatchPath(e,r,s):n.unwatchKey(e,r,s))}function l(e){var t,n,i,a,s=e.__ember_meta__;if(s&&(e.__ember_meta__=null,t=s.chains))for(c.push(t);c.length>0;){if(t=c.pop(),n=t._chains)for(i in n)n.hasOwnProperty(i)&&c.push(n[i]);t._watching&&(a=t._object,a&&r.removeChainWatcher(a,t._key,t))}}e.isWatching=o,e.unwatch=u,e.destroy=l,e.watch=s,s.flushPending=r.flushPendingChains;var c=[]}),e("ember-routing-htmlbars",["exports","ember-metal/core","ember-htmlbars/helpers","ember-routing-htmlbars/helpers/outlet","ember-routing-htmlbars/helpers/render","ember-routing-htmlbars/helpers/link-to","ember-routing-htmlbars/helpers/action","ember-routing-htmlbars/helpers/query-params"],function(e,t,r,n,i,a,s,o){"use strict";r.registerHelper("outlet",n.outletHelper),r.registerHelper("render",i.renderHelper),r.registerHelper("link-to",a.linkToHelper),r.registerHelper("linkTo",a.deprecatedLinkToHelper),r.registerHelper("action",s.actionHelper),r.registerHelper("query-params",o.queryParamsHelper),e["default"]=t["default"]}),e("ember-routing-htmlbars/helpers/action",["exports","ember-metal/core","ember-metal/utils","ember-metal/run_loop","ember-views/streams/utils","ember-views/system/utils","ember-views/system/action_manager","ember-metal/streams/utils"],function(e,t,r,n,i,a,s,o){"use strict";function u(e,t){var r,n,a;if(void 0===t)for(r=new Array(e.length),n=0,a=e.length;a>n;n++)r[n]=i.readUnwrappedModel(e[n]);else for(r=new Array(e.length+1),r[0]=t,n=0,a=e.length;a>n;n++)r[n+1]=i.readUnwrappedModel(e[n]);return r}function l(e,t,r,n){var i,a=n.data.view;i=t.target?o.isStream(t.target)?t.target:a.getStream(t.target):a.getStream("controller");var s={eventName:t.on||"click",parameters:e.slice(1),view:a,bubbles:t.bubbles,preventDefault:t.preventDefault,target:i,withKeyCode:t.withKeyCode},u=c.registerAction(e[0],s,t.allowedKeys);n.dom.setAttribute(r.element,"data-ember-action",u)}e.actionHelper=l;var c={};c.registeredActions=s["default"].registeredActions;var h=["alt","shift","meta","ctrl"],m=/^click|mouse|touch/,d=function(e,t){if("undefined"==typeof t){if(m.test(e.type))return a.isSimpleClick(e);t=""}if(t.indexOf("any")>=0)return!0;for(var r=0,n=h.length;n>r;r++)if(e[h[r]+"Key"]&&-1===t.indexOf(h[r]))return!1;return!0};c.registerAction=function(e,t,i){var a=r.uuid(),l=t.eventName,c=t.parameters;return s["default"].registeredActions[a]={eventName:l,handler:function(r){if(!d(r,i))return!0;t.preventDefault!==!1&&r.preventDefault(),t.bubbles===!1&&r.stopPropagation();var a,s=t.target.value();a=o.isStream(e)?e.value():e,n["default"](function(){s.send?s.send.apply(s,u(c,a)):s[a].apply(s,u(c))})}},t.view.on("willClearRender",function(){delete s["default"].registeredActions[a]}),a},e.ActionHelper=c}),e("ember-routing-htmlbars/helpers/link-to",["exports","ember-metal/core","ember-routing-views/views/link","ember-metal/streams/utils","ember-runtime/mixins/controller","ember-htmlbars/templates/link-to-escaped","ember-htmlbars/templates/link-to-unescaped","ember-htmlbars"],function(e,t,r,n,i,a,s){"use strict";function o(e,t,o,u){var l,c=(u.data.view,e[e.length-1]);if(c&&c.isQueryParams&&(t.queryParamsObject=l=e.pop()),t.disabledWhen&&(t.disabled=t.disabledWhen,delete t.disabledWhen),!o.template){var h=e.shift(),m=o.morph.parseTextAsHTML;m?t.layout=s["default"]:t.layout=a["default"],t.linkTitle=h}for(var d=0;d1){var O=c.lookupFactory(C)||a.generateControllerFactory(c,x,p);d=O.create({modelBinding:b,parentController:E,target:E}),f.one("willDestroyElement",function(){d.destroy()})}else d=c.lookup(C)||a["default"](c,x),d.setProperties({target:E,parentController:E});t.viewName=i.camelize(g),m&&!p&&m._connectActiveView(g,f);var P={template:_,controller:d,helperName:'render "'+g+'"'};h(v,f,g),o["default"](v,P,t),u["default"](v,s.morph,f,P)}function h(e,r,n){r._childOutlets=t["default"].A(),r._isOutlet=!0,r._outletName="__ember_orphans__",r._matchOutletName=n,r._parentOutlet=function(){for(var e=this._parentView;e&&!e._isOutlet;)e=e._parentView;return e},r.setOutletState=function(e){var t;e&&(t=e.outlets[this._matchOutletName])?(this._outletState={render:{name:"render helper stub"},outlets:l["default"](null)},this._outletState.outlets[t.render.outlet]=t,t.wasUsed=!0):this._outletState=null;for(var r=0;rt;++t){var n=e[t];if(null===n||"undefined"==typeof n)return!1}return!0}function f(e,t){if(r.get(e,"loading"))return!1;var n=e["current-when"]||e.currentWhen,i=!!n;n=n||r.get(e,"loadedParams").targetRouteName,n=n.split(" ");for(var a=0,s=n.length;s>a;a++)if(p(e,n[a],i,t))return r.get(e,"activeClass");return!1}function p(e,t,n,i){var a=r.get(e,"router"),s=r.get(e,"loadedParams"),o=s.models,u=a.router.recognizer.handlersFor(t),l=u[u.length-1].handler,c=v(t,u);return o.length>c&&(t=l),i.isActiveIntent(t,o,s.queryParams,!n)}var v=function(e,t){for(var r=0,n=0,i=t.length;i>n&&(r+=t[n].names.length,t[n].handler!==e);n++);return r},g=["active","loading","disabled"];g=["active","loading","disabled","transitioningIn","transitioningOut"];var b=l["default"].extend({tagName:"a",currentWhen:null,"current-when":null,title:null,rel:null,tabindex:null,target:null,activeClass:"active",loadingClass:"loading",disabledClass:"disabled",_isDisabled:!1,replace:!1,attributeBindings:["href","title","rel","tabindex","target"],classNameBindings:g,eventName:"click",init:function(){this._super.apply(this,arguments);var e=r.get(this,"eventName");this.on(e,this,this._invoke)},_paramsChanged:function(){this.notifyPropertyChange("resolvedParams")},_setupPathObservers:function(){for(var e=this.params,t=this._wrapAsScheduled(this._paramsChanged),r=0;ri;++i)u(t[i],n);return n}),_cacheMeta:i.computed(function(){var e=a.meta(this);if(e.proto!==this)return r.get(e.proto,"_cacheMeta");var t={},n=r.get(this,"_normalizedQueryParams");for(var i in n)if(n.hasOwnProperty(i)){var s,o=n[i],u=o.scope;"controller"===u&&(s=[]),t[i]={parts:s,values:null,scope:u,prefix:"",def:r.get(this,i)}}return t}),_updateCacheParams:function(e){var t=r.get(this,"_cacheMeta");for(var i in t)if(t.hasOwnProperty(i)){var a=t[i];a.values=e;var s=this._calculateCacheKey(a.prefix,a.parts,a.values),o=this._bucketCache;if(o){var u=o.lookup(s,i,a.def);n.set(this,i,u)}}},_qpChanged:function(e,t){var n=t.substr(0,t.length-3),i=r.get(e,"_cacheMeta"),a=i[n],s=e._calculateCacheKey(a.prefix||"",a.parts,a.values),o=r.get(e,n),u=this._bucketCache;u&&e._bucketCache.stash(s,n,o);var l=e._qpDelegate;l&&l(e,n)},_calculateCacheKey:function(e,t,n){for(var i=t||[],a="",s=0,o=i.length;o>s;++s){var u=i[s],l=r.get(n,u);a+="::"+u+":"+l}return e+a.replace(c,"-")},transitionToRoute:function(){var e=r.get(this,"target"),t=e.transitionToRoute||e.transitionTo;return t.apply(e,arguments)},transitionTo:function(){return this.transitionToRoute.apply(this,arguments)},replaceRoute:function(){var e=r.get(this,"target"),t=e.replaceRoute||e.replaceWith;return t.apply(e,arguments)},replaceWith:function(){return this.replaceRoute.apply(this,arguments)}});var c=/\./g;e["default"]=o["default"]}),e("ember-routing/ext/run_loop",["ember-metal/run_loop"],function(e){"use strict";e["default"]._addQueue("routerTransitions","actions")}),e("ember-routing/location/api",["exports","ember-metal/core","ember-metal/environment","ember-routing/location/util"],function(e,t,r,n){"use strict";e["default"]={create:function(e){var t=e&&e.implementation,r=this.implementations[t];return r.create.apply(r,arguments)},registerImplementation:function(e,t){this.implementations[e]=t},implementations:{},_location:r["default"].location,_getHash:function(){return n.getHash(this.location)}}}),e("ember-routing/location/auto_location",["exports","ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/utils","ember-runtime/system/object","ember-metal/environment","ember-routing/location/util"],function(e,t,r,n,i,a,s,o){"use strict";function u(e){return function(){for(var t=arguments.length,n=Array(t),a=0;t>a;a++)n[a]=arguments[a];var s=r.get(this,"concreteImplementation");return i.tryInvoke(s,e,n)}}function l(e){var t=e.location,r=e.userAgent,n=e.history,i=e.documentMode,a=e.global,s=e.rootURL,u="none",l=!1,m=o.getFullPath(t);if(o.supportsHistory(r,n)){var d=c(s,t);if(m===d)return"history";"/#"===m.substr(0,2)?(n.replaceState({path:d},null,d),u="history"):(l=!0,o.replacePath(t,d))}else if(o.supportsHashChange(i,a)){var f=h(s,t);m===f||"/"===m&&"/#/"===f?u="hash":(l=!0,o.replacePath(t,f))}return l?!1:u}function c(e,t){var r,n,i=o.getPath(t),a=o.getHash(t),s=o.getQuery(t);i.indexOf(e);return"#/"===a.substr(0,2)?(n=a.substr(1).split("#"),r=n.shift(),"/"===i.slice(-1)&&(r=r.substr(1)),i=i+r+s,n.length&&(i+="#"+n.join("#"))):i=i+s+a,i}function h(e,t){var r=e,n=c(e,t),i=n.substr(e.length);return""!==i&&("/"!==i.charAt(0)&&(i="/"+i),r+="#"+i),r}e.getHistoryPath=c,e.getHashPath=h,e["default"]=a["default"].extend({location:s["default"].location,history:s["default"].history,global:s["default"].global,userAgent:s["default"].userAgent,cancelRouterSetup:!1,rootURL:"/",detect:function(){var e=this.rootURL,t=l({location:this.location,history:this.history,userAgent:this.userAgent,rootURL:e,documentMode:this.documentMode,global:this.global});t===!1&&(n.set(this,"cancelRouterSetup",!0),t="none");var r=this.container.lookup("location:"+t);n.set(r,"rootURL",e),n.set(this,"concreteImplementation",r)},initState:u("initState"),getURL:u("getURL"),setURL:u("setURL"),replaceURL:u("replaceURL"),onUpdateURL:u("onUpdateURL"),formatURL:u("formatURL"),willDestroy:function(){var e=r.get(this,"concreteImplementation");e&&e.destroy()}})}),e("ember-routing/location/hash_location",["exports","ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/run_loop","ember-metal/utils","ember-runtime/system/object","ember-routing/location/api"],function(e,t,r,n,i,a,s,o){"use strict";e["default"]=s["default"].extend({implementation:"hash",init:function(){n.set(this,"location",r.get(this,"_location")||window.location)},getHash:o["default"]._getHash,getURL:function(){var e=this.getHash().substr(1),t=e;return"/"!==t.charAt(0)&&(t="/",e&&(t+="#"+e)),t},setURL:function(e){r.get(this,"location").hash=e,n.set(this,"lastSetURL",e)},replaceURL:function(e){r.get(this,"location").replace("#"+e),n.set(this,"lastSetURL",e)},onUpdateURL:function(e){var s=this,o=a.guidFor(this);t["default"].$(window).on("hashchange.ember-location-"+o,function(){i["default"](function(){var t=s.getURL();r.get(s,"lastSetURL")!==t&&(n.set(s,"lastSetURL",null),e(t))})})},formatURL:function(e){return"#"+e},willDestroy:function(){var e=a.guidFor(this);t["default"].$(window).off("hashchange.ember-location-"+e)}})}),e("ember-routing/location/history_location",["exports","ember-metal/property_get","ember-metal/property_set","ember-metal/utils","ember-runtime/system/object","ember-routing/location/api","ember-views/system/jquery"],function(e,t,r,n,i,a,s){"use strict";var o=!1;e["default"]=i["default"].extend({implementation:"history",init:function(){r.set(this,"location",t.get(this,"location")||window.location),r.set(this,"baseURL",s["default"]("base").attr("href")||"")},initState:function(){var e=t.get(this,"history")||window.history;r.set(this,"history",e),e&&"state"in e&&(this.supportsHistory=!0),this.replaceState(this.formatURL(this.getURL()))},rootURL:"/",getURL:function(){var e=t.get(this,"rootURL"),r=t.get(this,"location"),n=r.pathname,i=t.get(this,"baseURL");e=e.replace(/\/$/,""),i=i.replace(/\/$/,"");var a=n.replace(i,"").replace(e,""),s=r.search||"";return a+=s,a+=this.getHash()},setURL:function(e){var t=this.getState();e=this.formatURL(e),t&&t.path===e||this.pushState(e)},replaceURL:function(e){var t=this.getState();e=this.formatURL(e),t&&t.path===e||this.replaceState(e)},getState:function(){return this.supportsHistory?t.get(this,"history").state:this._historyState},pushState:function(e){var r={path:e};t.get(this,"history").pushState(r,null,e),this._historyState=r,this._previousURL=this.getURL()},replaceState:function(e){var r={path:e};t.get(this,"history").replaceState(r,null,e),this._historyState=r,this._previousURL=this.getURL()},onUpdateURL:function(e){var t=n.guidFor(this),r=this;s["default"](window).on("popstate.ember-location-"+t,function(t){(o||(o=!0,r.getURL()!==r._previousURL))&&e(r.getURL())})},formatURL:function(e){var r=t.get(this,"rootURL"),n=t.get(this,"baseURL");return""!==e?(r=r.replace(/\/$/,""),n=n.replace(/\/$/,"")):n.match(/^\//)&&r.match(/^\//)&&(n=n.replace(/\/$/,"")),n+r+e},willDestroy:function(){var e=n.guidFor(this);s["default"](window).off("popstate.ember-location-"+e)},getHash:a["default"]._getHash})}),e("ember-routing/location/none_location",["exports","ember-metal/property_get","ember-metal/property_set","ember-runtime/system/object"],function(e,t,r,n){"use strict";e["default"]=n["default"].extend({implementation:"none",path:"",getURL:function(){return t.get(this,"path")},setURL:function(e){r.set(this,"path",e)},onUpdateURL:function(e){this.updateCallback=e},handleURL:function(e){r.set(this,"path",e),this.updateCallback(e)},formatURL:function(e){return e}})}),e("ember-routing/location/util",["exports"],function(e){"use strict";function t(e){var t=e.pathname;return"/"!==t.charAt(0)&&(t="/"+t),t}function r(e){return e.search}function n(e){var t=e.href,r=t.indexOf("#");return-1===r?"":t.substr(r)}function i(e){return t(e)+r(e)+n(e)}function a(e){var t=e.origin;return t||(t=e.protocol+"//"+e.hostname,e.port&&(t+=":"+e.port)),t}function s(e,t){return"onhashchange"in t&&(void 0===e||e>7)}function o(e,t){return-1!==e.indexOf("Android 2")&&-1!==e.indexOf("Mobile Safari")&&-1===e.indexOf("Chrome")?!1:!!(t&&"pushState"in t)}function u(e,t){e.replace(a(e)+t)}e.getPath=t,e.getQuery=r,e.getHash=n,e.getFullPath=i,e.getOrigin=a,e.supportsHashChange=s,e.supportsHistory=o,e.replacePath=u}),e("ember-routing/system/cache",["exports","ember-runtime/system/object"],function(e,t){"use strict";e["default"]=t["default"].extend({init:function(){this.cache={}},has:function(e){return e in this.cache},stash:function(e,t,r){var n=this.cache[e];n||(n=this.cache[e]={}),n[t]=r},lookup:function(e,t,r){var n=this.cache;if(!(e in n))return r;var i=n[e];return t in i?i[t]:r},cache:null})}),e("ember-routing/system/controller_for",["exports"],function(e){"use strict";function t(e,t,r){return e.lookup("controller:"+t,r)}e["default"]=t}),e("ember-routing/system/dsl",["exports","ember-metal/core","ember-metal/array"],function(e,t,r){"use strict";function n(e,t){this.parent=e,this.enableLoadingSubstates=t&&t.enableLoadingSubstates,this.matches=[]}function i(e){return e.parent&&"application"!==e.parent}function a(e,t,r){return i(e)&&r!==!0?e.parent+"."+t:t}function s(e,t,r,n){r=r||{};var i=a(e,t,r.resetNamespace);"string"!=typeof r.path&&(r.path="/"+t),e.push(r.path,i,n)}e["default"]=n,n.prototype={route:function(e,t,r){2===arguments.length&&"function"==typeof t&&(r=t,t={}),1===arguments.length&&(t={});t.resetNamespace===!0?"resource":"route";if(this.enableLoadingSubstates&&(s(this,e+"_loading",{resetNamespace:t.resetNamespace}),s(this,e+"_error",{path:"/_unused_dummy_error_path_route_"+e+"/:error"})),r){var i=a(this,e,t.resetNamespace),o=new n(i,{enableLoadingSubstates:this.enableLoadingSubstates});s(o,"loading"),s(o,"error",{path:"/_unused_dummy_error_path_route_"+e+"/:error"}),r.call(o),s(this,e,t,o.generate())}else s(this,e,t)},push:function(e,t,r){var n=t.split(".");(""===e||"/"===e||"index"===n[n.length-1])&&(this.explicitIndex=!0),this.matches.push([e,t,r])},resource:function(e,t,r){2===arguments.length&&"function"==typeof t&&(r=t,t={}),1===arguments.length&&(t={}),t.resetNamespace=!0,this.route(e,t,r)},generate:function(){var e=this.matches;return this.explicitIndex||this.route("index",{path:"/"}),function(t){for(var r=0,n=e.length;n>r;r++){var i=e[r];t(i[0]).to(i[1],i[2])}}}},n.map=function(e){var t=new n;return e.call(t),t}}),e("ember-routing/system/generate_controller",["exports","ember-metal/core","ember-metal/property_get","ember-metal/utils"],function(e,t,r,n){"use strict";function i(e,t,r){var i,a,s,o;return o=r&&n.isArray(r)?"array":r?"object":"basic",s="controller:"+o,i=e.lookupFactory(s).extend({isGenerated:!0,toString:function(){return"(generated "+t+" controller)"}}),a="controller:"+t,e._registry.register(a,i),i}function a(e,t,n){i(e,t,n);var a="controller:"+t,s=e.lookup(a);return r.get(s,"namespace.LOG_ACTIVE_GENERATION"),s}e.generateControllerFactory=i,e["default"]=a}),e("ember-routing/system/query_params",["exports","ember-runtime/system/object"],function(e,t){"use strict";e["default"]=t["default"].extend({isQueryParams:!0,values:null})}),e("ember-routing/system/route",["exports","ember-metal/core","ember-metal/error","ember-metal/property_get","ember-metal/property_set","ember-metal/get_properties","ember-metal/enumerable_utils","ember-metal/is_none","ember-metal/computed","ember-metal/merge","ember-metal/utils","ember-metal/run_loop","ember-metal/keys","ember-runtime/copy","ember-runtime/system/string","ember-runtime/system/object","ember-runtime/mixins/evented","ember-runtime/mixins/action_handler","ember-routing/system/generate_controller","ember-routing/utils"],function(e,t,r,n,i,a,s,o,u,l,c,h,m,d,f,p,v,g,b,y){"use strict";function _(){return this}function w(e){var t=x(e,e.router.router.state.handlerInfos,-1);return t&&t.handler}function x(e,t,r){if(t)for(var n,i=r||0,a=0,s=t.length;s>a;a++)if(n=t[a].handler,n===e)return t[a+i]}function C(e,t,i,a,s){var o,u,l,c,h=s&&s.controller,m=(n.get(e.router,"namespace.LOG_VIEW_LOOKUPS"),s&&s.into&&s.into.replace(/\//g,".")),d=s&&s.outlet||"main";if(a?(a=a.replace(/\//g,"."),o=a):(a=e.routeName,o=e.templateName||a),h||(h=t?e.container.lookup("controller:"+a)||e.controllerName||e.routeName:e.controllerName||e.container.lookup("controller:"+a)),"string"==typeof h){var f=h;if(h=e.container.lookup("controller:"+f),!h)throw new r["default"]("You passed `controller: '"+f+"'` into the `render` method, but no such controller could be found.")}s&&s.model&&h.set("model",s.model),u=s&&s.view||t&&a||e.viewName||a,l=e.container.lookupFactory("view:"+u),c=e.container.lookup("template:"+o);var p;m&&(p=w(e))&&m===w(e).routeName&&(m=void 0);var v={into:m,outlet:d,name:a,controller:h,ViewClass:l,template:c};return v}function E(e,t){if(t.fullQueryParams)return t.fullQueryParams;t.fullQueryParams={},l["default"](t.fullQueryParams,t.queryParams);var r=t.handlerInfos[t.handlerInfos.length-1].name;return e._deserializeQueryParams(r,t.fullQueryParams),t.fullQueryParams}function O(e,t){t.queryParamsFor=t.queryParamsFor||{};var r=e.routeName;if(t.queryParamsFor[r])return t.queryParamsFor[r];for(var i=E(e.router,t),a=t.queryParamsFor[r]={},s=n.get(e,"_qp"),o=s.qps,u=0,l=o.length;l>u;++u){var c=o[u],h=c.prop in i;a[c.prop]=h?i[c.prop]:P(c.def)}return a}function P(e){return c.isArray(e)?t["default"].A(e.slice()):e}var S=Array.prototype.slice,A=p["default"].extend(g["default"],v["default"],{queryParams:{},_qp:u.computed(function(){var e=this.controllerName||this.routeName,r=this.container.lookupFactory("controller:"+e);if(!r)return N;var i=r.proto(),a=n.get(i,"_normalizedQueryParams"),s=n.get(i,"_cacheMeta"),o=[],u={},l=this;for(var h in a)if(a.hasOwnProperty(h)){var m=a[h],d=m.as||this.serializeQueryParamKey(h),f=n.get(i,h);c.isArray(f)&&(f=t["default"].A(f.slice()));var p=c.typeOf(f),v=this.serializeQueryParam(f,d,p),g=e+":"+h,b={def:f,sdef:v,type:p,urlKey:d,prop:h,fprop:g,ctrl:e,cProto:i,svalue:v,cacheType:m.scope,route:this,cacheMeta:s[h]};u[h]=u[d]=u[g]=b,o.push(b)}return{qps:o,map:u,states:{active:function(e,t){return l._activeQPChanged(e,u[t])},allowOverrides:function(e,t){return l._updatingQPChanged(e,u[t])},changingKeys:function(e,t){return l._updateSerializedQPValue(e,u[t])}}}}),_names:null,_stashNames:function(e,t){var r=e;if(!this._names){var i=this._names=r._names;i.length||(r=t,i=r&&r._names||[]);for(var a=n.get(this,"_qp.qps"),s=a.length,o=new Array(i.length),u=0,l=i.length;l>u;++u)o[u]=r.name+"."+i[u];for(var c=0;s>c;++c){var h=a[c],m=h.cacheMeta;"model"===m.scope&&(m.parts=o),m.prefix=h.ctrl}}},_updateSerializedQPValue:function(e,t){var r=n.get(e,t.prop);t.svalue=this.serializeQueryParam(r,t.urlKey,t.type)},_activeQPChanged:function(e,t){var r=n.get(e,t.prop);this.router._queuedQPChanges[t.fprop]=r,h["default"].once(this,this._fireQueryParamTransition)},_updatingQPChanged:function(e,t){var r=this.router;r._qpUpdates||(r._qpUpdates={}),r._qpUpdates[t.urlKey]=!0},mergedProperties:["events","queryParams"],paramsFor:function(e){var t=this.container.lookup("route:"+e);if(!t)return{};var r=this.router.router.activeTransition,n=r?r.state:this.router.router.state,i={};return l["default"](i,n.params[e]),l["default"](i,O(t,n)),i},serializeQueryParamKey:function(e){return e},serializeQueryParam:function(e,t,r){return"array"===r?JSON.stringify(e):""+e},deserializeQueryParam:function(e,r,n){return"boolean"===n?"true"===e?!0:!1:"number"===n?Number(e).valueOf():"array"===n?t["default"].A(JSON.parse(e)):e},_fireQueryParamTransition:function(){this.transitionTo({queryParams:this.router._queuedQPChanges}),this.router._queuedQPChanges={}},_optionsForQueryParam:function(e){return n.get(this,"queryParams."+e.urlKey)||n.get(this,"queryParams."+e.prop)||{}},resetController:_,exit:function(){this.deactivate(),this.trigger("deactivate"),this.teardownViews()},_reset:function(e,t){var r=this.controller;r._qpDelegate=n.get(this,"_qp.states.inactive"),this.resetController(r,e,t)},enter:function(){this.connections=[],this.activate(),this.trigger("activate")},viewName:null,templateName:null,controllerName:null,_actions:{queryParamsDidChange:function(e,t,r){for(var i=n.get(this,"_qp").map,a=m["default"](e).concat(m["default"](r)),s=0,o=a.length;o>s;++s){var u=i[a[s]];u&&n.get(this._optionsForQueryParam(u),"refreshModel")&&this.refresh()}return!0},finalizeQueryParamChange:function(e,t,r){if("application"!==this.routeName)return!0;if(r){var a,o=r.state.handlerInfos,u=this.router,l=u._queryParamsFor(o[o.length-1].name),c=u._qpUpdates;y.stashParamNames(u,o);for(var h=0,m=l.qps.length;m>h;++h){var d,f,p=l.qps[h],v=p.route,g=v.controller,b=p.urlKey in e&&p.urlKey;c&&p.urlKey in c?(d=n.get(g,p.prop),f=v.serializeQueryParam(d,p.urlKey,p.type)):b?(f=e[b],d=v.deserializeQueryParam(f,p.urlKey,p.type)):(f=p.sdef,d=P(p.def)),g._qpDelegate=n.get(this,"_qp.states.inactive");var _=f!==p.svalue;if(_){if(r.queryParamsOnly&&a!==!1){var w=v._optionsForQueryParam(p),x=n.get(w,"replace");x?a=!0:x===!1&&(a=!1)}i.set(g,p.prop,d)}p.svalue=f;var C=p.sdef===f;C||t.push({value:f,visible:!0,key:b||p.urlKey})}a&&r.method("replace"),s.forEach(l.qps,function(e){var t=n.get(e.route,"_qp"),r=e.route.controller;r._qpDelegate=n.get(t,"states.active")}),u._qpUpdates=null}}},events:null,deactivate:_,activate:_,transitionTo:function(e,t){var r=this.router;return r.transitionTo.apply(r,arguments)},intermediateTransitionTo:function(){var e=this.router;e.intermediateTransitionTo.apply(e,arguments)},refresh:function(){return this.router.router.refresh(this)},replaceWith:function(){var e=this.router;return e.replaceWith.apply(e,arguments)},send:function(){if(this.router||!t["default"].testing){var e;(e=this.router).send.apply(e,arguments)}else{var r=arguments[0],n=S.call(arguments,1),i=this._actions[r];if(i)return this._actions[r].apply(this,n)}},setup:function(e,t){var r=this.controllerName||this.routeName,i=this.controllerFor(r,!0);if(i||(i=this.generateController(r,e)),this.controller=i,this.setupControllers)this.setupControllers(i,e);else{var a=n.get(this,"_qp.states");if(t&&(y.stashParamNames(this.router,t.state.handlerInfos),i._qpDelegate=a.changingKeys,i._updateCacheParams(t.params)),i._qpDelegate=a.allowOverrides,t){var s=O(this,t.state);i.setProperties(s)}this.setupController(i,e,t)}this.renderTemplates?this.renderTemplates(e):this.renderTemplate(i,e)},beforeModel:_,afterModel:_,redirect:_,contextDidChange:function(){this.currentModel=this.context},model:function(e,t){var r,i,a,s,o=n.get(this,"_qp.map");for(var u in e)"queryParams"===u||o&&u in o||((r=u.match(/^(.*)_id$/))&&(i=r[1],s=e[u]),a=!0);if(!i&&a)return d["default"](e);if(!i){if(t.resolveIndex<1)return;var l=t.state.handlerInfos[t.resolveIndex-1].context;return l}return this.findModel(i,s)},deserialize:function(e,t){return this.model(this.paramsFor(this.routeName),t)},findModel:function(){var e=n.get(this,"store");return e.find.apply(e,arguments)},store:u.computed(function(){var e=this.container;this.routeName,n.get(this,"router.namespace");return{find:function(t,r){var n=e.lookupFactory("model:"+t);if(n)return n.find(r)}}}),serialize:function(e,t){if(!(t.length<1)&&e){var r=t[0],i={};return 1===t.length?r in e?i[r]=n.get(e,r):/_id$/.test(r)&&(i[r]=n.get(e,"id")):i=a["default"](e,t),i}},setupController:function(e,t,r){e&&void 0!==t&&i.set(e,"model",t)},controllerFor:function(e,t){var r,n=this.container,i=n.lookup("route:"+e);return i&&i.controllerName&&(e=i.controllerName),r=n.lookup("controller:"+e)},generateController:function(e,t){var r=this.container;return t=t||this.modelFor(e),b["default"](r,e,t)},modelFor:function(e){var t=this.container.lookup("route:"+e),r=this.router?this.router.router.activeTransition:null;if(r){var n=t&&t.routeName||e;if(r.resolvedModels.hasOwnProperty(n))return r.resolvedModels[n]}return t&&t.currentModel},renderTemplate:function(e,t){this.render()},render:function(e,r){var n,i="string"==typeof e&&!!e,a=0===arguments.length||t["default"].isEmpty(arguments[0]);"object"!=typeof e||r?n=e:(n=this.routeName,r=e);var s=C(this,i,a,n,r);this.connections.push(s),h["default"].once(this.router,"_setOutlets")},disconnectOutlet:function(e){var t,r;e&&"string"!=typeof e?(t=e.outlet,r=e.parentView):t=e,r=r&&r.replace(/\//g,"."),t=t||"main",this._disconnectOutlet(t,r);for(var n=0;n0&&(this.connections=[],h["default"].once(this.router,"_setOutlets"))}}),N={qps:[],map:{},states:{}};e["default"]=A}),e("ember-routing/system/router",["exports","ember-metal/core","ember-metal/error","ember-metal/property_get","ember-metal/property_set","ember-metal/properties","ember-metal/computed","ember-metal/merge","ember-metal/run_loop","ember-runtime/system/string","ember-runtime/system/object","ember-runtime/mixins/evented","ember-routing/system/dsl","ember-routing/location/api","ember-routing/utils","ember-metal/platform/create","./router_state","router","router/transition"],function(e,t,r,n,i,a,s,o,u,l,c,h,m,d,f,p,v,g){"use strict";function b(){return this}function y(e,t,r){for(var n,i,a=t.state.handlerInfos,s=!1,o=a.length-1;o>=0;--o)if(n=a[o],i=n.handler,s){if(r(i,a[o+1].handler)!==!0)return!1}else e===i&&(s=!0);return!0}function _(e,r){var n,i=[];n=e&&"object"==typeof e&&"object"==typeof e.errorThrown?e.errorThrown:e,r&&i.push(r),n&&(n.message&&i.push(n.message),n.stack&&i.push(n.stack),"string"==typeof n&&i.push(n)),t["default"].Logger.error.apply(this,i)}function w(e,t,r){var n,i=e.router,a=t.routeName.split(".").pop(),s="application"===e.routeName?"":e.routeName+".";return n=s+a+"_"+r,x(i,n)?n:(n=s+r,x(i,n)?n:void 0)}function x(e,t){var r=e.container;return e.hasRoute(t)&&(r._registry.has("template:"+t)||r._registry.has("route:"+t))}function C(e,t,n){var i=n.shift();if(!e){if(t)return;throw new r["default"]("Can't trigger action '"+i+"' because your app hasn't finished transitioning into its first route. To trigger an action on destination routes during a transition, you can call `.send()` on the `Transition` object passed to the `model/beforeModel/afterModel` hooks.")}for(var a,s,o=!1,u=e.length-1;u>=0;u--)if(a=e[u],s=a.handler,s._actions&&s._actions[i]){if(s._actions[i].apply(s,n)!==!0)return;o=!0}if(R[i])return void R[i].apply(null,n);if(!o&&!t)throw new r["default"]("Nothing handled the action '"+i+"'. If you did handle the action, this error can be caused by returning true from an action handler in a controller, causing the action to bubble.")}function E(e,t,r){for(var n=e.router,i=n.applyIntent(t,r),a=i.handlerInfos,s=i.params,o=0,u=a.length;u>o;++o){var l=a[o];l.isResolved||(l=l.becomeResolved(null,l.context)),s[l.name]=l.params}return i}function O(e){var t=e.container.lookup("controller:application");if(t){var r=e.router.currentHandlerInfos,n=V._routePath(r);"currentPath"in t||a.defineProperty(t,"currentPath"),i.set(t,"currentPath",n),"currentRouteName"in t||a.defineProperty(t,"currentRouteName"),i.set(t,"currentRouteName",r[r.length-1].name)}}function P(e,t){var r=v["default"].create({emberRouter:t,routerJs:t.router,routerJsState:e.state});t.currentState||t.set("currentState",r),t.set("targetState",r),e.then(null,function(e){return e&&e.name?e:void 0},"Ember: Process errors from Router")}function S(e){return"string"==typeof e&&(""===e||"/"===e.charAt(0))}function A(e,t,r,n){var i=e._queryParamsFor(t);for(var a in r)if(r.hasOwnProperty(a)){var s=r[a],o=i.map[a];o&&n(a,s,o)}}function N(e,t){if(e)for(var r=[e];r.length>0;){var n=r.shift();if(n.render.name===t)return n;var i=n.outlets;for(var a in i)r.push(i[a])}}function T(e,t,r){var n,a={render:r,outlets:p["default"](null)};return n=r.into?N(e,r.into):t,n?i.set(n.outlets,r.outlet,a):r.into?k(e,r.into,a):e=a,{liveRoutes:e,ownState:a}}function k(e,r,n){e.outlets.__ember_orphans__||(e.outlets.__ember_orphans__={render:{name:"__ember_orphans__"},outlets:p["default"](null)}),e.outlets.__ember_orphans__.outlets[r]=n,t["default"].run.schedule("afterRender",function(){})}function M(e,t,r){var n=N(e,r.routeName);return n?n:(t.outlets.main={render:{name:r.routeName,outlet:"main"},outlets:{}},t)}var I=[].slice,V=c["default"].extend(h["default"],{location:"hash",rootURL:"/",_initRouterJs:function(e){function r(){this.resource("application",{path:"/",overrideNameAssertion:!0},function(){for(var e=0;er;r++)t[r]=arguments[r];var n;if(S(t[0]))return this._doURLTransition("transitionTo",t[0]);var i=t[t.length-1];n=i&&i.hasOwnProperty("queryParams")?t.pop().queryParams:{};var a=t.shift();return this._doTransition(a,t,n)},intermediateTransitionTo:function(){var e;(e=this.router).intermediateTransitionTo.apply(e,arguments),O(this);var r=this.router.currentHandlerInfos;n.get(this,"namespace").LOG_TRANSITIONS&&t["default"].Logger.log("Intermediate-transitioned into '"+V._routePath(r)+"'")},replaceWith:function(){return this.transitionTo.apply(this,arguments).method("replace")},generate:function(){var e,t=(e=this.router).generate.apply(e,arguments);return this.location.formatURL(t)},isActive:function(e){var t=this.router;return t.isActive.apply(t,arguments)},isActiveIntent:function(e,t,r){return this.currentState.isActiveIntent(e,t,r)},send:function(e,t){var r;(r=this.router).trigger.apply(r,arguments)},hasRoute:function(e){return this.router.hasRoute(e)},reset:function(){this.router&&this.router.reset()},willDestroy:function(){this._toplevelView&&(this._toplevelView.destroy(),this._toplevelView=null),this._super.apply(this,arguments),this.reset()},_lookupActiveView:function(e){var t=this._activeViews[e];return t&&t[0]},_connectActiveView:function(e,t){function r(){delete this._activeViews[e]}var n=this._activeViews[e];n&&n[0].off("willDestroyElement",this,n[1]),this._activeViews[e]=[t,r],t.one("willDestroyElement",this,r)},_setupLocation:function(){var e=n.get(this,"location"),t=n.get(this,"rootURL");if("string"==typeof e&&this.container){var r=this.container.lookup("location:"+e);if("undefined"!=typeof r)e=i.set(this,"location",r);else{var a={implementation:e};e=i.set(this,"location",d["default"].create(a))}}null!==e&&"object"==typeof e&&(t&&i.set(e,"rootURL",t),"function"==typeof e.detect&&e.detect(),"function"==typeof e.initState&&e.initState())},_getHandlerFunction:function(){var e=p["default"](null),t=this.container,r=t.lookupFactory("route:basic"),i=this;return function(a){var s="route:"+a,o=t.lookup(s);return e[a]?o:(e[a]=!0,o||(t._registry.register(s,r.extend()),o=t.lookup(s),n.get(i,"namespace.LOG_ACTIVE_GENERATION")),o.routeName=a,o)}},_setupRouter:function(e,t){var r,n=this;e.getHandler=this._getHandlerFunction();var i=function(){t.setURL(r)};if(e.updateURL=function(e){r=e,u["default"].once(i)},t.replaceURL){var a=function(){t.replaceURL(r)};e.replaceURL=function(e){r=e,u["default"].once(a)}}e.didTransition=function(e){n.didTransition(e)},e.willTransition=function(e,t,r){n.willTransition(e,t,r)}},_serializeQueryParams:function(e,t){var r={};A(this,e,t,function(e,n,i){var a=i.urlKey;r[a]||(r[a]=[]),r[a].push({qp:i,value:n}),delete t[e]});for(var n in r){var i=r[n],a=i[0].qp;t[a.urlKey]=a.route.serializeQueryParam(i[0].value,a.urlKey,a.type)}},_deserializeQueryParams:function(e,t){A(this,e,t,function(e,r,n){delete t[e],t[n.prop]=n.route.deserializeQueryParam(r,n.urlKey,n.type)})},_pruneDefaultQueryParamValues:function(e,t){var r=this._queryParamsFor(e);for(var n in t){var i=r.map[n];i&&i.sdef===t[n]&&delete t[n]}},_doTransition:function(e,t,r){var n=e||f.getActiveTargetName(this.router),i={};o["default"](i,r),this._prepareQueryParams(n,t,i);var a=f.routeArgs(n,t,i),s=this.router.transitionTo.apply(this.router,a);return P(s,this),s},_prepareQueryParams:function(e,t,r){this._hydrateUnsuppliedQueryParams(e,t,r),this._serializeQueryParams(e,r),this._pruneDefaultQueryParamValues(e,r)},_queryParamsFor:function(e){if(this._qpCache[e])return this._qpCache[e];var t={},r=[];this._qpCache[e]={map:t,qps:r};for(var i=this.router,a=i.recognizer.handlersFor(e),s=0,u=a.length;u>s;++s){var l=a[s],c=i.getHandler(l.handler),h=n.get(c,"_qp");h&&(o["default"](t,h.map),r.push.apply(r,h.qps))}return{qps:r,map:t}},_hydrateUnsuppliedQueryParams:function(e,t,r){var i=E(this,e,t),a=i.handlerInfos,s=this._bucketCache;f.stashParamNames(this,a);for(var o=0,u=a.length;u>o;++o)for(var l=a[o].handler,c=n.get(l,"_qp"),h=0,m=c.qps.length;m>h;++h){var d=c.qps[h],p=d.prop in r&&d.prop||d.fprop in r&&d.fprop;if(p)p!==d.fprop&&(r[d.fprop]=r[p],delete r[p]);else{var v=d.cProto,g=n.get(v,"_cacheMeta"),b=v._calculateCacheKey(d.ctrl,g[d.prop].parts,i.params);r[d.fprop]=s.lookup(b,d.prop,d.def)}}},_scheduleLoadingEvent:function(e,t){this._cancelSlowTransitionTimer(),this._slowTransitionTimer=u["default"].scheduleOnce("routerTransitions",this,"_handleSlowTransition",e,t)},currentState:null,targetState:null,_handleSlowTransition:function(e,t){this.router.activeTransition&&(this.set("targetState",v["default"].create({emberRouter:this,routerJs:this.router,routerJsState:this.router.activeTransition.state})),e.trigger(!0,"loading",e,t))},_cancelSlowTransitionTimer:function(){this._slowTransitionTimer&&u["default"].cancel(this._slowTransitionTimer),this._slowTransitionTimer=null}}),R={willResolveModel:function(e,t){t.router._scheduleLoadingEvent(e,t)},error:function(e,t,r){var n=r.router,i=y(r,t,function(t,r){var i=w(t,r,"error");return i?void n.intermediateTransitionTo(i,e):!0});return i&&x(r.router,"application_error")?void n.intermediateTransitionTo("application_error",e):void _(e,"Error while processing route: "+t.targetName)},loading:function(e,t){var r=t.router,n=y(t,e,function(t,n){var i=w(t,n,"loading");return i?void r.intermediateTransitionTo(i):e.pivotHandler!==t?!0:void 0});return n&&x(t.router,"application_loading")?void r.intermediateTransitionTo("application_loading"):void 0}};V.reopenClass({router:null,map:function(e){return this.dslCallbacks||(this.dslCallbacks=[],this.reopenClass({dslCallbacks:this.dslCallbacks})),this.dslCallbacks.push(e),this},_routePath:function(e){function t(e,t){for(var r=0,n=e.length;n>r;++r)if(e[r]!==t[r])return!1;return!0}for(var r,n,i,a=[],s=1,o=e.length;o>s;s++){for(r=e[s].name,n=r.split("."),i=I.call(a);i.length&&!t(i,n);)i.shift();a.push.apply(a,n.slice(i.length))}return a.join(".")}}),e["default"]=V}),e("ember-routing/system/router_state",["exports","ember-metal/core","ember-runtime/system/object","ember-metal/merge"],function(e,t,r,n){"use strict";function i(e,t){var r;for(r in e)if(e.hasOwnProperty(r)&&e[r]!==t[r])return!1;for(r in t)if(t.hasOwnProperty(r)&&e[r]!==t[r])return!1;return!0}var a=r["default"].extend({emberRouter:null,routerJs:null,routerJsState:null,isActiveIntent:function(e,r,a,s){var o=this.routerJsState;if(!this.routerJs.isActiveIntent(e,r,null,o))return!1;var u=t["default"].isEmpty(t["default"].keys(a));if(s&&!u){var l={};return n["default"](l,a),this.emberRouter._prepareQueryParams(e,r,l),i(l,o.queryParams)}return!0}});e["default"]=a}),e("ember-routing/utils",["exports","ember-metal/utils"],function(e,t){"use strict";function r(e,r,n){var i=[];return"string"===t.typeOf(e)&&i.push(""+e),i.push.apply(i,r),i.push({queryParams:n}),i}function n(e){var t=e.activeTransition?e.activeTransition.state.handlerInfos:e.state.handlerInfos;return t[t.length-1].name}function i(e,t){if(!t._namesStashed){for(var r=t[t.length-1].name,n=e.router.recognizer.handlersFor(r),i=null,a=0,s=t.length;s>a;++a){var o=t[a],u=n[a].names;u.length&&(i=o),o._names=u;var l=o.handler;l._stashNames(o,i)}t._namesStashed=!0}}e.routeArgs=r,e.getActiveTargetName=n,e.stashParamNames=i}),e("ember-runtime",["exports","ember-metal","ember-runtime/core","ember-runtime/compare","ember-runtime/copy","ember-runtime/inject","ember-runtime/system/namespace","ember-runtime/system/object","ember-runtime/system/tracked_array","ember-runtime/system/subarray","ember-runtime/system/container","ember-runtime/system/array_proxy","ember-runtime/system/object_proxy","ember-runtime/system/core_object","ember-runtime/system/native_array","ember-runtime/system/set","ember-runtime/system/string","ember-runtime/system/deferred","ember-runtime/system/lazy_load","ember-runtime/mixins/array","ember-runtime/mixins/comparable","ember-runtime/mixins/copyable","ember-runtime/mixins/enumerable","ember-runtime/mixins/freezable","ember-runtime/mixins/-proxy","ember-runtime/mixins/observable","ember-runtime/mixins/action_handler","ember-runtime/mixins/deferred","ember-runtime/mixins/mutable_enumerable","ember-runtime/mixins/mutable_array","ember-runtime/mixins/target_action_support","ember-runtime/mixins/evented","ember-runtime/mixins/promise_proxy","ember-runtime/mixins/sortable","ember-runtime/computed/array_computed","ember-runtime/computed/reduce_computed","ember-runtime/computed/reduce_computed_macros","ember-runtime/controllers/array_controller","ember-runtime/controllers/object_controller","ember-runtime/controllers/controller","ember-runtime/mixins/controller","ember-runtime/system/service","ember-runtime/ext/rsvp","ember-runtime/ext/string","ember-runtime/ext/function"],function(e,t,r,n,i,a,s,o,u,l,c,h,m,d,f,p,v,g,b,y,_,w,x,C,E,O,P,S,A,N,T,k,M,I,V,R,j,D,F,L,B,H,z){"use strict";t["default"].compare=n["default"],t["default"].copy=i["default"],t["default"].isEqual=r.isEqual,t["default"].inject=a["default"],t["default"].Array=y["default"],t["default"].Comparable=_["default"],t["default"].Copyable=w["default"],t["default"].SortableMixin=I["default"],t["default"].Freezable=C.Freezable,t["default"].FROZEN_ERROR=C.FROZEN_ERROR,t["default"].DeferredMixin=S["default"],t["default"].MutableEnumerable=A["default"],t["default"].MutableArray=N["default"],t["default"].TargetActionSupport=T["default"],t["default"].Evented=k["default"],t["default"].PromiseProxyMixin=M["default"],t["default"].Observable=O["default"],t["default"].arrayComputed=V.arrayComputed,t["default"].ArrayComputedProperty=V.ArrayComputedProperty,t["default"].reduceComputed=R.reduceComputed,t["default"].ReduceComputedProperty=R.ReduceComputedProperty;var q=t["default"].computed;q.sum=j.sum,q.min=j.min,q.max=j.max,q.map=j.map,q.sort=j.sort,q.setDiff=j.setDiff,q.mapBy=j.mapBy,q.mapProperty=j.mapProperty,q.filter=j.filter,q.filterBy=j.filterBy,q.filterProperty=j.filterProperty,q.uniq=j.uniq,q.union=j.union,q.intersect=j.intersect,t["default"].String=v["default"],t["default"].Object=o["default"],t["default"].TrackedArray=u["default"],t["default"].SubArray=l["default"],t["default"].Container=c.Container,t["default"].Registry=c.Registry,t["default"].Namespace=s["default"],t["default"].Enumerable=x["default"],t["default"].ArrayProxy=h["default"],t["default"].ObjectProxy=m["default"],t["default"].ActionHandler=P["default"],t["default"].CoreObject=d["default"],t["default"].NativeArray=f["default"],t["default"].Set=p["default"],t["default"].Deferred=g["default"],t["default"].onLoad=b.onLoad,t["default"].runLoadHooks=b.runLoadHooks,t["default"].ArrayController=D["default"],t["default"].ObjectController=F["default"],t["default"].Controller=L["default"],t["default"].ControllerMixin=B["default"],t["default"].Service=H["default"],t["default"]._ProxyMixin=E["default"],t["default"].RSVP=z["default"],e["default"]=t["default"]}),e("ember-runtime/compare",["exports","ember-metal/utils","ember-runtime/mixins/comparable"],function(e,t,r){"use strict";function n(e,t){var r=e-t;return(r>0)-(0>r)}function i(e,s){if(e===s)return 0;var o=t.typeOf(e),u=t.typeOf(s);if(r["default"]){if("instance"===o&&r["default"].detect(e)&&e.constructor.compare)return e.constructor.compare(e,s);if("instance"===u&&r["default"].detect(s)&&s.constructor.compare)return-1*s.constructor.compare(s,e)}var l=n(a[o],a[u]);if(0!==l)return l;switch(o){case"boolean":case"number":return n(e,s);case"string":return n(e.localeCompare(s),0);case"array":for(var c=e.length,h=s.length,m=Math.min(c,h),d=0;m>d;d++){var f=i(e[d],s[d]);if(0!==f)return f}return n(c,h);case"instance":return r["default"]&&r["default"].detect(e)?e.compare(e,s):0;case"date":return n(e.getTime(),s.getTime());default:return 0}}e["default"]=i;var a={undefined:0,"null":1,"boolean":2,number:3,string:4,array:5,object:6,instance:7,"function":8,"class":9,date:10}}),e("ember-runtime/computed/array_computed",["exports","ember-metal/core","ember-runtime/computed/reduce_computed","ember-metal/enumerable_utils","ember-metal/platform/create","ember-metal/observer","ember-metal/error"],function(e,t,r,n,i,a,s){"use strict";function o(){var e=this;return r.ReduceComputedProperty.apply(this,arguments),this._getter=function(t){return function(r){return e._hasInstanceMeta(this,r)||n.forEach(e._dependentKeys,function(t){a.addObserver(this,t,function(){e.recomputeOnce.call(this,r)})},this),t.apply(this,arguments)}}(this._getter),this}function u(e){var t;if(arguments.length>1&&(t=l.call(arguments,0,-1),e=l.call(arguments,-1)[0]),"object"!=typeof e)throw new s["default"]("Array Computed Property declared without an options hash");var r=new o(e);return t&&r.property.apply(r,t),r}e.arrayComputed=u,e.ArrayComputedProperty=o;var l=[].slice;o.prototype=i["default"](r.ReduceComputedProperty.prototype),o.prototype.initialValue=function(){return t["default"].A()},o.prototype.resetValue=function(e){return e.clear(),e},o.prototype.didChange=function(e,t){}}),e("ember-runtime/computed/reduce_computed",["exports","ember-metal/core","ember-metal/property_get","ember-metal/utils","ember-metal/error","ember-metal/property_events","ember-metal/expand_properties","ember-metal/observer","ember-metal/computed","ember-metal/platform/create","ember-metal/enumerable_utils","ember-runtime/system/tracked_array","ember-runtime/mixins/array","ember-metal/run_loop"],function(e,t,r,n,i,a,s,o,u,l,c,h,m,d){"use strict";function f(e,t){return"@this"===t?e:r.get(e,t)}function p(e,t,r,n,i,a){this.callbacks=e,this.cp=t,this.instanceMeta=r,this.dependentKeysByGuid={},this.trackedArraysByGuid={},this.suspended=!1,this.changedItems={},this.changedItemCount=0}function v(e,t,r){this.dependentArray=e,this.index=t,this.item=e.objectAt(t),this.trackedArray=r,this.beforeObserver=null,this.observer=null,this.destroyed=!1}function g(e,t,r){return 0>e?Math.max(0,t+e):t>e?e:Math.min(t-r,e)}function b(e,t,r){return Math.min(r,t-e)}function y(e,t,r,n,i,a,s){this.arrayChanged=e,this.index=r,this.item=t,this.propertyName=n,this.property=i,this.changedCount=a,s&&(this.previousValues=s)}function _(e,t,r,n,i){c.forEach(e,function(a,s){i.setValue(t.addedItem.call(this,i.getValue(),a,new y(e,a,s,n,r,e.length),i.sugarMeta))},this),t.flushedChanges.call(this,i.getValue(),i.sugarMeta)}function w(e,t){var r=e._hasInstanceMeta(this,t),n=e._instanceMeta(this,t);r&&n.setValue(e.resetValue(n.getValue())),e.options.initialize&&e.options.initialize.call(this,n.getValue(),{property:e,propertyName:t},n.sugarMeta)}function x(e,t){if(I.test(t))return!1;var r=f(e,t);return m["default"].detect(r)}function C(e,t,r){this.context=e,this.propertyName=t;var i=n.meta(e),a=i.cache;a||(a=i.cache={}),this.cache=a,this.dependentArrays={},this.sugarMeta={},this.initialValue=r}function E(e){var t=this;this.options=e,this._dependentKeys=null,this._cacheable=!0,this._itemPropertyKeys={},this._previousItemPropertyKeys={},this.readOnly(),this.recomputeOnce=function(e){d["default"].once(this,r,e)};var r=function(e){var r=t._instanceMeta(this,e),n=t._callbacks();w.call(this,t,e),r.dependentArraysObserver.suspendArrayObservers(function(){c.forEach(t._dependentKeys,function(e){if(x(this,e)){var n=f(this,e),i=r.dependentArrays[e];n===i?t._previousItemPropertyKeys[e]&&(delete t._previousItemPropertyKeys[e],r.dependentArraysObserver.setupPropertyObservers(e,t._itemPropertyKeys[e])):(r.dependentArrays[e]=n,i&&r.dependentArraysObserver.teardownObservers(i,e),n&&r.dependentArraysObserver.setupObservers(n,e))}},this)},this),c.forEach(t._dependentKeys,function(i){if(x(this,i)){var a=f(this,i);a&&_.call(this,a,n,t,e,r)}},this)};this._getter=function(e){return r.call(this,e),t._instanceMeta(this,e).getValue()}}function O(e){return e}function P(e){var t;if(arguments.length>1&&(t=T.call(arguments,0,-1),e=T.call(arguments,-1)[0]),"object"!=typeof e)throw new i["default"]("Reduce Computed Property declared without an options hash");if(!("initialValue"in e))throw new i["default"]("Reduce Computed Property declared without an initial value");var r=new E(e);return t&&r.property.apply(r,t),r}e.reduceComputed=P,e.ReduceComputedProperty=E;var S=u.cacheFor.set,A=u.cacheFor.get,N=u.cacheFor.remove,T=[].slice,k=/^(.*)\.@each\.(.*)/,M=/(.*\.@each){2,}/,I=/\.\[\]$/;p.prototype={setValue:function(e){this.instanceMeta.setValue(e,!0)},getValue:function(){return this.instanceMeta.getValue()},setupObservers:function(e,t){this.dependentKeysByGuid[n.guidFor(e)]=t,e.addArrayObserver(this,{willChange:"dependentArrayWillChange",didChange:"dependentArrayDidChange"}),this.cp._itemPropertyKeys[t]&&this.setupPropertyObservers(t,this.cp._itemPropertyKeys[t])},teardownObservers:function(e,t){var r=this.cp._itemPropertyKeys[t]||[];delete this.dependentKeysByGuid[n.guidFor(e)],this.teardownPropertyObservers(t,r),e.removeArrayObserver(this,{willChange:"dependentArrayWillChange",didChange:"dependentArrayDidChange"})},suspendArrayObservers:function(e,t){var r=this.suspended;this.suspended=!0,e.call(t),this.suspended=r},setupPropertyObservers:function(e,t){var r=f(this.instanceMeta.context,e),n=f(r,"length"),i=new Array(n);this.resetTransformations(e,i),c.forEach(r,function(n,a){var s=this.createPropertyObserverContext(r,a,this.trackedArraysByGuid[e]);i[a]=s,c.forEach(t,function(e){o.addBeforeObserver(n,e,this,s.beforeObserver),o.addObserver(n,e,this,s.observer)},this)},this)},teardownPropertyObservers:function(e,t){var r,n,i,a=this,s=this.trackedArraysByGuid[e];s&&s.apply(function(e,s,u){u!==h["default"].DELETE&&c.forEach(e,function(e){e.destroyed=!0,r=e.beforeObserver,n=e.observer,i=e.item,c.forEach(t,function(e){o.removeBeforeObserver(i,e,a,r),o.removeObserver(i,e,a,n)})})})},createPropertyObserverContext:function(e,t,r){var n=new v(e,t,r);return this.createPropertyObserver(n),n},createPropertyObserver:function(e){var t=this;e.beforeObserver=function(r,n){return t.itemPropertyWillChange(r,n,e.dependentArray,e)},e.observer=function(r,n){return t.itemPropertyDidChange(r,n,e.dependentArray,e)}},resetTransformations:function(e,t){this.trackedArraysByGuid[e]=new h["default"](t)},trackAdd:function(e,t,r){var n=this.trackedArraysByGuid[e];n&&n.addItems(t,r)},trackRemove:function(e,t,r){var n=this.trackedArraysByGuid[e];return n?n.removeItems(t,r):[]},updateIndexes:function(e,t){var r=f(t,"length");e.apply(function(e,t,n,i){n!==h["default"].DELETE&&(0!==i||n!==h["default"].RETAIN||e.length!==r||0!==t)&&c.forEach(e,function(e,r){e.index=r+t})})},dependentArrayWillChange:function(e,t,r,i){function a(e){m[h].destroyed=!0,o.removeBeforeObserver(u,e,this,m[h].beforeObserver),o.removeObserver(u,e,this,m[h].observer)}if(!this.suspended){var s,u,l,h,m,d=this.callbacks.removedItem,p=n.guidFor(e),v=this.dependentKeysByGuid[p],_=this.cp._itemPropertyKeys[v]||[],w=f(e,"length"),x=g(t,w,0),C=b(x,w,r);for(m=this.trackRemove(v,x,C),h=C-1;h>=0&&(l=x+h,!(l>=w));--h)u=e.objectAt(l),c.forEach(_,a,this),s=new y(e,u,l,this.instanceMeta.propertyName,this.cp,C),this.setValue(d.call(this.instanceMeta.context,this.getValue(),u,s,this.instanceMeta.sugarMeta));this.callbacks.flushedChanges.call(this.instanceMeta.context,this.getValue(),this.instanceMeta.sugarMeta)}},dependentArrayDidChange:function(e,t,r,i){if(!this.suspended){var a,s,u=this.callbacks.addedItem,l=n.guidFor(e),h=this.dependentKeysByGuid[l],m=new Array(i),d=this.cp._itemPropertyKeys[h],p=f(e,"length"),v=g(t,p,i),b=v+i;c.forEach(e.slice(v,b),function(t,r){d&&(s=this.createPropertyObserverContext(e,v+r,this.trackedArraysByGuid[h]),m[r]=s,c.forEach(d,function(e){o.addBeforeObserver(t,e,this,s.beforeObserver),o.addObserver(t,e,this,s.observer)},this)),a=new y(e,t,v+r,this.instanceMeta.propertyName,this.cp,i),this.setValue(u.call(this.instanceMeta.context,this.getValue(),t,a,this.instanceMeta.sugarMeta))},this),this.callbacks.flushedChanges.call(this.instanceMeta.context,this.getValue(),this.instanceMeta.sugarMeta),this.trackAdd(h,v,m)}},itemPropertyWillChange:function(e,t,r,i){var a=n.guidFor(e);this.changedItems[a]||(this.changedItems[a]={array:r,observerContext:i,obj:e,previousValues:{}}),++this.changedItemCount,this.changedItems[a].previousValues[t]=f(e,t)},itemPropertyDidChange:function(e,t,r,n){0===--this.changedItemCount&&this.flushChanges()},flushChanges:function(){var e,t,r,n=this.changedItems;for(e in n)t=n[e],t.observerContext.destroyed||(this.updateIndexes(t.observerContext.trackedArray,t.observerContext.dependentArray),r=new y(t.array,t.obj,t.observerContext.index,this.instanceMeta.propertyName,this.cp,n.length,t.previousValues),this.setValue(this.callbacks.removedItem.call(this.instanceMeta.context,this.getValue(),t.obj,r,this.instanceMeta.sugarMeta)),this.setValue(this.callbacks.addedItem.call(this.instanceMeta.context,this.getValue(),t.obj,r,this.instanceMeta.sugarMeta)));this.changedItems={},this.callbacks.flushedChanges.call(this.instanceMeta.context,this.getValue(),this.instanceMeta.sugarMeta)}},C.prototype={getValue:function(){var e=A(this.cache,this.propertyName);return void 0!==e?e:this.initialValue},setValue:function(e,t){e!==A(this.cache,this.propertyName)&&(t&&a.propertyWillChange(this.context,this.propertyName),void 0===e?N(this.cache,this.propertyName):S(this.cache,this.propertyName,e),t&&a.propertyDidChange(this.context,this.propertyName))}},E.prototype=l["default"](u.ComputedProperty.prototype),E.prototype._callbacks=function(){if(!this.callbacks){var e=this.options;this.callbacks={removedItem:e.removedItem||O,addedItem:e.addedItem||O,flushedChanges:e.flushedChanges||O}}return this.callbacks},E.prototype._hasInstanceMeta=function(e,t){var r=e.__ember_meta__,n=r&&r.cacheMeta;return!(!n||!n[t])},E.prototype._instanceMeta=function(e,t){var r=e.__ember_meta__,n=r.cacheMeta,i=n&&n[t];return n||(n=r.cacheMeta={}),i||(i=n[t]=new C(e,t,this.initialValue()),i.dependentArraysObserver=new p(this._callbacks(),this,i,e,t,i.sugarMeta)),i},E.prototype.initialValue=function(){return"function"==typeof this.options.initialValue?this.options.initialValue():this.options.initialValue},E.prototype.resetValue=function(e){return this.initialValue()},E.prototype.itemPropertyKey=function(e,t){this._itemPropertyKeys[e]=this._itemPropertyKeys[e]||[],this._itemPropertyKeys[e].push(t)},E.prototype.clearItemPropertyKeys=function(e){this._itemPropertyKeys[e]&&(this._previousItemPropertyKeys[e]=this._itemPropertyKeys[e],this._itemPropertyKeys[e]=[])},E.prototype.property=function(){var e,t,r=this,a=T.call(arguments),o={};c.forEach(a,function(a){if(M.test(a))throw new i["default"]("Nested @each properties not supported: "+a);if(e=k.exec(a)){t=e[1];var u=e[2],l=function(e){r.itemPropertyKey(t,e)};s["default"](u,l),o[n.guidFor(t)]=t}else o[n.guidFor(a)]=a});var l=[];for(var h in o)l.push(o[h]);return u.ComputedProperty.prototype.property.apply(this,l)}}),e("ember-runtime/computed/reduce_computed_macros",["exports","ember-metal/core","ember-metal/property_get","ember-metal/utils","ember-metal/error","ember-metal/enumerable_utils","ember-metal/run_loop","ember-metal/observer","ember-runtime/computed/array_computed","ember-runtime/computed/reduce_computed","ember-runtime/system/subarray","ember-metal/keys","ember-runtime/compare"],function(e,t,r,n,i,a,s,o,u,l,c,h,m){"use strict";function d(e){return l.reduceComputed(e,{initialValue:0,addedItem:function(e,t,r,n){return e+t},removedItem:function(e,t,r,n){return e-t}})}function f(e){return l.reduceComputed(e,{initialValue:-(1/0),addedItem:function(e,t,r,n){return Math.max(e,t)},removedItem:function(e,t,r,n){return e>t?e:void 0}})}function p(e){return l.reduceComputed(e,{initialValue:1/0,addedItem:function(e,t,r,n){return Math.min(e,t)},removedItem:function(e,t,r,n){return t>e?e:void 0}})}function v(e,t){var r={addedItem:function(e,r,n,i){var a=t.call(this,r,n.index);return e.insertAt(n.index,a),e},removedItem:function(e,t,r,n){return e.removeAt(r.index,1),e}};return u.arrayComputed(e,r)}function g(e,t){var n=function(e){return r.get(e,t)};return v(e+".@each."+t,n)}function b(e,t){var r={initialize:function(e,t,r){r.filteredArrayIndexes=new c["default"]},addedItem:function(e,r,n,i){var a=!!t.call(this,r,n.index,n.arrayChanged),s=i.filteredArrayIndexes.addItem(n.index,a);return a&&e.insertAt(s,r),e},removedItem:function(e,t,r,n){var i=n.filteredArrayIndexes.removeItem(r.index);return i>-1&&e.removeAt(i),e}};return u.arrayComputed(e,r)}function y(e,t,n){var i;return i=2===arguments.length?function(e){return r.get(e,t)}:function(e){return r.get(e,t)===n},b(e+".@each."+t,i)}function _(){var e=A.call(arguments);return e.push({initialize:function(e,t,r){r.itemCounts={}},addedItem:function(e,t,r,i){var a=n.guidFor(t);return i.itemCounts[a]?++i.itemCounts[a]:(i.itemCounts[a]=1,e.pushObject(t)),e},removedItem:function(e,t,r,i){var a=n.guidFor(t),s=i.itemCounts;return 0===--s[a]&&e.removeObject(t),e}}),u.arrayComputed.apply(null,e)}function w(){var e=A.call(arguments);return e.push({initialize:function(e,t,r){r.itemCounts={}},addedItem:function(e,t,r,i){var a=n.guidFor(t),s=n.guidFor(r.arrayChanged),o=r.property._dependentKeys.length,u=i.itemCounts;return u[a]||(u[a]={}),void 0===u[a][s]&&(u[a][s]=0),1===++u[a][s]&&o===h["default"](u[a]).length&&e.addObject(t),e},removedItem:function(e,t,r,i){var a,s=n.guidFor(t),o=n.guidFor(r.arrayChanged),u=i.itemCounts;return void 0===u[s][o]&&(u[s][o]=0),0===--u[s][o]&&(delete u[s][o],a=h["default"](u[s]).length,0===a&&delete u[s],e.removeObject(t)),e}}),u.arrayComputed.apply(null,e)}function x(e,t){if(2!==arguments.length)throw new i["default"]("setDiff requires exactly two dependent arrays."); +return u.arrayComputed(e,t,{addedItem:function(n,i,a,s){var o=r.get(this,e),u=r.get(this,t);return a.arrayChanged===o?u.contains(i)||n.addObject(i):n.removeObject(i),n},removedItem:function(n,i,a,s){var o=r.get(this,e),u=r.get(this,t);return a.arrayChanged===u?o.contains(i)&&n.addObject(i):n.removeObject(i),n}})}function C(e,t,i,a){var s,o,u,l,c;return arguments.length<4&&(a=r.get(e,"length")),arguments.length<3&&(i=0),i===a?i:(s=i+Math.floor((a-i)/2),o=e.objectAt(s),l=n.guidFor(o),c=n.guidFor(t),l===c?s:(u=this.order(o,t),0===u&&(u=c>l?-1:1),0>u?this.binarySearch(e,t,s+1,a):u>0?this.binarySearch(e,t,i,s):s))}function E(e,t){return"function"==typeof t?O(e,t):P(e,t)}function O(e,t){return u.arrayComputed(e,{initialize:function(e,r,n){n.order=t,n.binarySearch=C,n.waitingInsertions=[],n.insertWaiting=function(){var t,r,i=n.waitingInsertions;n.waitingInsertions=[];for(var a=0;a=0&&n>e&&(t=this.lookupItemController(a))?this.controllerAt(e,a,t):a},arrangedContentDidChange:function(){this._super.apply(this,arguments),this._resetSubControllers()},arrayContentDidChange:function(e,t,r){var i=this._subControllers;if(i.length){var a=i.slice(e,e+t);n.forEach(a,function(e){e&&e.destroy()}),n.replace(i,e,t,new Array(r))}this._super(e,t,r)},init:function(){this._super.apply(this,arguments),this._subControllers=[]},model:o.computed({get:function(e){return t["default"].A()},set:function(e,t){return t}}),_isVirtual:!1,controllerAt:function(e,t,n){var i,a,s,o=r.get(this,"container"),l=this._subControllers;if(l.length>e&&(a=l[e]))return a;if(s=this._isVirtual?r.get(this,"parentController"):this,i="controller:"+n,!o._registry.has(i))throw new u["default"]('Could not resolve itemController: "'+n+'"');return a=o.lookupFactory(i).create({target:s,parentController:s,model:t}),l[e]=a,a},_subControllers:null,_resetSubControllers:function(){var e,t=this._subControllers;if(t.length){for(var r=0,n=t.length;n>r;r++)e=t[r],e&&e.destroy();t.length=0}},willDestroy:function(){this._resetSubControllers(),this._super.apply(this,arguments)}})}),e("ember-runtime/controllers/controller",["exports","ember-metal/core","ember-runtime/system/object","ember-runtime/mixins/controller","ember-runtime/inject"],function(e,t,r,n,i){"use strict";function a(e){}var s=r["default"].extend(n["default"]);i.createInjectionHelper("controller",a),e["default"]=s}),e("ember-runtime/controllers/object_controller",["exports","ember-metal/core","ember-runtime/mixins/controller","ember-runtime/system/object_proxy"],function(e,t,r,n){"use strict";var i="Ember.ObjectController is deprecated, please use Ember.Controller and use `model.propertyName`.";e["default"]=n["default"].extend(r["default"],{init:function(){this._super()}}),e.objectControllerDeprecation=i}),e("ember-runtime/copy",["exports","ember-metal/enumerable_utils","ember-metal/utils","ember-runtime/system/object","ember-runtime/mixins/copyable"],function(e,t,r,n,i){"use strict";function a(e,n,s,o){var u,l,c;if("object"!=typeof e||null===e)return e;if(n&&(l=t.indexOf(s,e))>=0)return o[l];if("array"===r.typeOf(e)){if(u=e.slice(),n)for(l=u.length;--l>=0;)u[l]=a(u[l],n,s,o)}else if(i["default"]&&i["default"].detect(e))u=e.copy(n,s,o);else if(e instanceof Date)u=new Date(e.getTime());else{u={};for(c in e)Object.prototype.hasOwnProperty.call(e,c)&&"__"!==c.substring(0,2)&&(u[c]=n?a(e[c],n,s,o):e[c])}return n&&(s.push(e),o.push(u)),u}function s(e,t){return"object"!=typeof e||null===e?e:i["default"]&&i["default"].detect(e)?e.copy(t):a(e,t,t?[]:null,t?[]:null)}e["default"]=s}),e("ember-runtime/core",["exports"],function(e){"use strict";function t(e,t){return e&&"function"==typeof e.isEqual?e.isEqual(t):e instanceof Date&&t instanceof Date?e.getTime()===t.getTime():e===t}e.isEqual=t}),e("ember-runtime/ext/function",["ember-metal/core","ember-metal/expand_properties","ember-metal/computed","ember-metal/mixin"],function(e,t,r,n){"use strict";var i=Array.prototype.slice,a=Function.prototype;(e["default"].EXTEND_PROTOTYPES===!0||e["default"].EXTEND_PROTOTYPES.Function)&&(a.property=function(){var e=r.computed(this);return e.property.apply(e,arguments)},a.observes=function(){for(var e=arguments.length,t=Array(e),r=0;e>r;r++)t[r]=arguments[r];return t.push(this),n.observer.apply(this,t)},a.observesImmediately=function(){return this.observes.apply(this,arguments)},a.observesBefore=function(){for(var e=[],r=function(t){e.push(t)},n=0,i=arguments.length;i>n;++n)t["default"](arguments[n],r);return this.__ember_observesBefore__=e,this},a.on=function(){var e=i.call(arguments);return this.__ember_listens__=e,this})}),e("ember-runtime/ext/rsvp",["exports","ember-metal/core","ember-metal/logger","ember-metal/run_loop","rsvp"],function(e,r,n,i,a){"use strict";function s(e){var i;if(e&&e.errorThrown?(i=e.errorThrown,"string"==typeof i&&(i=new Error(i)),i.__reason_with_error_thrown__=e):i=e,i&&"TransitionAborted"!==i.name)if(r["default"].testing){if(!o&&r["default"].__loader.registry[u]&&(o=t(u)["default"]),!o||!o.adapter)throw i;o.adapter.exception(i),n["default"].error(i.stack)}else r["default"].onerror?r["default"].onerror(i):n["default"].error(i.stack)}e.onerrorDefault=s;var o,u="ember-testing/test",l=function(){r["default"].Test&&r["default"].Test.adapter&&r["default"].Test.adapter.asyncStart()},c=function(){r["default"].Test&&r["default"].Test.adapter&&r["default"].Test.adapter.asyncEnd()};a.configure("async",function(e,t){var n=!i["default"].currentRunLoop;r["default"].testing&&n&&l(),i["default"].backburner.schedule("actions",function(){r["default"].testing&&n&&c(),e(t)})}),a.Promise.prototype.fail=function(e,t){return this["catch"](e,t)},a.on("error",s),e["default"]=a}),e("ember-runtime/ext/string",["ember-metal/core","ember-runtime/system/string"],function(e,t){"use strict";var r=String.prototype;(e["default"].EXTEND_PROTOTYPES===!0||e["default"].EXTEND_PROTOTYPES.String)&&(r.fmt=function(){return t.fmt(this,arguments)},r.w=function(){return t.w(this)},r.loc=function(){return t.loc(this,arguments)},r.camelize=function(){return t.camelize(this)},r.decamelize=function(){return t.decamelize(this)},r.dasherize=function(){return t.dasherize(this)},r.underscore=function(){return t.underscore(this)},r.classify=function(){return t.classify(this)},r.capitalize=function(){return t.capitalize(this)})}),e("ember-runtime/inject",["exports","ember-metal/core","ember-metal/enumerable_utils","ember-metal/injected_property","ember-metal/keys"],function(e,t,r,n,i){"use strict";function a(){}function s(e,t){u[e]=t,a[e]=function(t){return new n["default"](e,t)}}function o(e){var t,i,a,s,o,l=e.proto(),c=[];for(t in l)i=l[t],i instanceof n["default"]&&-1===r.indexOf(c,i.type)&&c.push(i.type);if(c.length)for(s=0,o=c.length;o>s;s++)a=u[c[s]],"function"==typeof a&&a(e);return!0}e.createInjectionHelper=s,e.validatePropertyInjections=o;var u={};e["default"]=a}),e("ember-runtime/mixins/-proxy",["exports","ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/utils","ember-metal/observer","ember-metal/property_events","ember-metal/computed","ember-metal/properties","ember-metal/mixin","ember-runtime/system/string"],function(e,t,r,n,i,a,s,o,u,l,c){"use strict";function h(e,t){var r=t.slice(8);r in this||s.propertyWillChange(this,r)}function m(e,t){var r=t.slice(8);r in this||s.propertyDidChange(this,r)}e["default"]=l.Mixin.create({content:null,_contentDidChange:l.observer("content",function(){}),isTruthy:o.computed.bool("content"),_debugContainerKey:null,willWatchProperty:function(e){var t="content."+e;a.addBeforeObserver(this,t,null,h),a.addObserver(this,t,null,m)},didUnwatchProperty:function(e){var t="content."+e;a.removeBeforeObserver(this,t,null,h),a.removeObserver(this,t,null,m)},unknownProperty:function(e){var t=r.get(this,"content");return t?r.get(t,e):void 0},setUnknownProperty:function(e,t){var a=i.meta(this);if(a.proto===this)return u.defineProperty(this,e,null,t),t;var s=r.get(this,"content");return n.set(s,e,t)}})}),e("ember-runtime/mixins/action_handler",["exports","ember-metal/merge","ember-metal/mixin","ember-metal/property_get","ember-metal/utils"],function(e,t,r,n,i){"use strict";var a=r.Mixin.create({mergedProperties:["_actions"],willMergeMixin:function(e){var r;e._actions||("object"===i.typeOf(e.actions)?r="actions":"object"===i.typeOf(e.events)&&(r="events"),r&&(e._actions=t["default"](e._actions||{},e[r])),delete e[r])},send:function(e){for(var t=arguments.length,r=Array(t>1?t-1:0),i=1;t>i;i++)r[i-1]=arguments[i];var a;if(this._actions&&this._actions[e]){var s=this._actions[e].apply(this,r)===!0;if(!s)return}(a=n.get(this,"target"))&&a.send.apply(a,arguments)}});e["default"]=a}),e("ember-runtime/mixins/array",["exports","ember-metal/core","ember-metal/property_get","ember-metal/computed","ember-metal/is_none","ember-runtime/mixins/enumerable","ember-metal/enumerable_utils","ember-metal/mixin","ember-metal/property_events","ember-metal/events","ember-metal/watching"],function(e,r,n,i,a,s,o,u,l,c,h){"use strict";function m(e,t,r,i,a){var s=r&&r.willChange||"arrayWillChange",o=r&&r.didChange||"arrayDidChange",u=n.get(e,"hasArrayObservers");return u===a&&l.propertyWillChange(e,"hasArrayObservers"),i(e,"@array:before",t,s),i(e,"@array:change",t,o),u===a&&l.propertyDidChange(e,"hasArrayObservers"),e}e["default"]=u.Mixin.create(s["default"],{length:null,objectAt:function(e){return 0>e||e>=n.get(this,"length")?void 0:n.get(this,e)},objectsAt:function(e){var t=this;return o.map(e,function(e){return t.objectAt(e)})},nextObject:function(e){return this.objectAt(e)},"[]":i.computed({get:function(e){return this},set:function(e,t){return this.replace(0,n.get(this,"length"),t),this}}),firstObject:i.computed(function(){return this.objectAt(0)}),lastObject:i.computed(function(){return this.objectAt(n.get(this,"length")-1)}),contains:function(e){return this.indexOf(e)>=0},slice:function(e,t){var i=r["default"].A(),s=n.get(this,"length");for(a["default"](e)&&(e=0),(a["default"](t)||t>s)&&(t=s),0>e&&(e=s+e),0>t&&(t=s+t);t>e;)i[i.length]=this.objectAt(e++);return i},indexOf:function(e,t){var r,i=n.get(this,"length");for(void 0===t&&(t=0),0>t&&(t+=i),r=t;i>r;r++)if(this.objectAt(r)===e)return r;return-1},lastIndexOf:function(e,t){var r,i=n.get(this,"length");for((void 0===t||t>=i)&&(t=i-1),0>t&&(t+=i),r=t;r>=0;r--)if(this.objectAt(r)===e)return r;return-1},addArrayObserver:function(e,t){return m(this,e,t,c.addListener,!1)},removeArrayObserver:function(e,t){return m(this,e,t,c.removeListener,!0)},hasArrayObservers:i.computed(function(){return c.hasListeners(this,"@array:change")||c.hasListeners(this,"@array:before")}),arrayContentWillChange:function(e,t,r){var i,a;if(void 0===e?(e=0,t=r=-1):(void 0===t&&(t=-1),void 0===r&&(r=-1)),h.isWatching(this,"@each")&&n.get(this,"@each"),c.sendEvent(this,"@array:before",[this,e,t,r]),e>=0&&t>=0&&n.get(this,"hasEnumerableObservers")){i=[],a=e+t;for(var s=e;a>s;s++)i.push(this.objectAt(s))}else i=t;return this.enumerableContentWillChange(i,r),this},arrayContentDidChange:function(e,t,r){var a,s;if(void 0===e?(e=0,t=r=-1):(void 0===t&&(t=-1),void 0===r&&(r=-1)),e>=0&&r>=0&&n.get(this,"hasEnumerableObservers")){a=[],s=e+r;for(var o=e;s>o;o++)a.push(this.objectAt(o))}else a=r;this.enumerableContentDidChange(t,a),c.sendEvent(this,"@array:change",[this,e,t,r]);var u=n.get(this,"length"),h=i.cacheFor(this,"firstObject"),m=i.cacheFor(this,"lastObject");return this.objectAt(0)!==h&&(l.propertyWillChange(this,"firstObject"),l.propertyDidChange(this,"firstObject")),this.objectAt(u-1)!==m&&(l.propertyWillChange(this,"lastObject"),l.propertyDidChange(this,"lastObject")),this},"@each":i.computed(function(){if(!this.__each){var e=t("ember-runtime/system/each_proxy").EachProxy;this.__each=new e(this)}return this.__each})})}),e("ember-runtime/mixins/comparable",["exports","ember-metal/mixin"],function(e,t){"use strict";e["default"]=t.Mixin.create({compare:null})}),e("ember-runtime/mixins/controller",["exports","ember-metal/mixin","ember-metal/alias","ember-runtime/mixins/action_handler","ember-runtime/mixins/controller_content_model_alias_deprecation"],function(e,t,r,n,i){"use strict";e["default"]=t.Mixin.create(n["default"],i["default"],{isController:!0,target:null,container:null,parentController:null,store:null,model:null,content:r["default"]("model")})}),e("ember-runtime/mixins/controller_content_model_alias_deprecation",["exports","ember-metal/core","ember-metal/mixin"],function(e,t,r){"use strict";e["default"]=r.Mixin.create({willMergeMixin:function(e){this._super.apply(this,arguments);var t=!!e.model;e.content&&!t&&(e.model=e.content,delete e.content)}})}),e("ember-runtime/mixins/copyable",["exports","ember-metal/property_get","ember-metal/mixin","ember-runtime/mixins/freezable","ember-runtime/system/string","ember-metal/error"],function(e,t,r,n,i,a){"use strict";e["default"]=r.Mixin.create({copy:null,frozenCopy:function(){if(n.Freezable&&n.Freezable.detect(this))return t.get(this,"isFrozen")?this:this.copy().freeze();throw new a["default"](i.fmt("%@ does not support freezing",[this]))}})}),e("ember-runtime/mixins/deferred",["exports","ember-metal/core","ember-metal/property_get","ember-metal/mixin","ember-metal/computed","ember-runtime/ext/rsvp"],function(e,t,r,n,i,a){"use strict";e["default"]=n.Mixin.create({then:function(e,t,n){function i(t){return e(t===s?o:t)}var a,s,o;return o=this,a=r.get(this,"_deferred"),s=a.promise,s.then(e&&i,t,n)},resolve:function(e){var t,n;t=r.get(this,"_deferred"),n=t.promise,e===this?t.resolve(n):t.resolve(e)},reject:function(e){r.get(this,"_deferred").reject(e)},_deferred:i.computed(function(){return a["default"].defer("Ember: DeferredMixin - "+this)})})}),e("ember-runtime/mixins/enumerable",["exports","ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/mixin","ember-metal/enumerable_utils","ember-metal/computed","ember-metal/property_events","ember-metal/events","ember-runtime/compare"],function(e,t,r,n,i,a,s,o,u,l){"use strict";function c(){return 0===d.length?{}:d.pop()}function h(e){return d.push(e),null}function m(e,t){function n(n){var a=r.get(n,e);return i?t===a:!!a}var i=2===arguments.length;return n}var d=[];e["default"]=i.Mixin.create({nextObject:null,firstObject:s.computed("[]",function(){if(0===r.get(this,"length"))return void 0;var e=c(),t=this.nextObject(0,null,e);return h(e),t}),lastObject:s.computed("[]",function(){var e=r.get(this,"length");if(0===e)return void 0;var t,n=c(),i=0,a=null;do a=t,t=this.nextObject(i++,a,n);while(void 0!==t);return h(n),a}),contains:function(e){var t=this.find(function(t){return t===e});return void 0!==t},forEach:function(e,t){if("function"!=typeof e)throw new TypeError;var n=c(),i=r.get(this,"length"),a=null;void 0===t&&(t=null);for(var s=0;i>s;s++){var o=this.nextObject(s,a,n);e.call(t,o,s,this),a=o}return a=null,n=h(n),this},getEach:i.aliasMethod("mapBy"),setEach:function(e,t){return this.forEach(function(r){n.set(r,e,t)})},map:function(e,r){var n=t["default"].A();return this.forEach(function(t,i,a){n[i]=e.call(r,t,i,a)}),n},mapBy:function(e){return this.map(function(t){return r.get(t,e)})},mapProperty:i.aliasMethod("mapBy"),filter:function(e,r){var n=t["default"].A();return this.forEach(function(t,i,a){e.call(r,t,i,a)&&n.push(t)}),n},reject:function(e,t){return this.filter(function(){return!e.apply(t,arguments)})},filterBy:function(e,t){return this.filter(m.apply(this,arguments))},filterProperty:i.aliasMethod("filterBy"),rejectBy:function(e,t){var n=function(n){return r.get(n,e)===t},i=function(t){return!!r.get(t,e)},a=2===arguments.length?n:i;return this.reject(a)},rejectProperty:i.aliasMethod("rejectBy"),find:function(e,t){var n=r.get(this,"length");void 0===t&&(t=null);for(var i,a,s=c(),o=!1,u=null,l=0;n>l&&!o;l++)i=this.nextObject(l,u,s),(o=e.call(t,i,l,this))&&(a=i),u=i;return i=u=null,s=h(s),a},findBy:function(e,t){return this.find(m.apply(this,arguments))},findProperty:i.aliasMethod("findBy"),every:function(e,t){return!this.find(function(r,n,i){return!e.call(t,r,n,i)})},everyBy:i.aliasMethod("isEvery"),everyProperty:i.aliasMethod("isEvery"),isEvery:function(e,t){return this.every(m.apply(this,arguments))},any:function(e,t){var n,i,a=r.get(this,"length"),s=c(),o=!1,u=null;for(void 0===t&&(t=null),i=0;a>i&&!o;i++)n=this.nextObject(i,u,s),o=e.call(t,n,i,this),u=n;return n=u=null,s=h(s),o},some:i.aliasMethod("any"),isAny:function(e,t){return this.any(m.apply(this,arguments))},anyBy:i.aliasMethod("isAny"),someProperty:i.aliasMethod("isAny"),reduce:function(e,t,r){if("function"!=typeof e)throw new TypeError;var n=t;return this.forEach(function(t,i){n=e(n,t,i,this,r)},this),n},invoke:function(e){for(var r=arguments.length,n=Array(r>1?r-1:0),i=1;r>i;i++)n[i-1]=arguments[i];var a=t["default"].A();return this.forEach(function(t,r){var i=t&&t[e];"function"==typeof i&&(a[r]=n?i.apply(t,n):t[e]())},this),a},toArray:function(){var e=t["default"].A();return this.forEach(function(t,r){e[r]=t}),e},compact:function(){return this.filter(function(e){return null!=e})},without:function(e){if(!this.contains(e))return this;var r=t["default"].A();return this.forEach(function(t){t!==e&&(r[r.length]=t)}),r},uniq:function(){var e=t["default"].A();return this.forEach(function(t){a.indexOf(e,t)<0&&e.push(t)}),e},"[]":s.computed({get:function(e){return this}}),addEnumerableObserver:function(e,t){var n=t&&t.willChange||"enumerableWillChange",i=t&&t.didChange||"enumerableDidChange",a=r.get(this,"hasEnumerableObservers");return a||o.propertyWillChange(this,"hasEnumerableObservers"),u.addListener(this,"@enumerable:before",e,n),u.addListener(this,"@enumerable:change",e,i),a||o.propertyDidChange(this,"hasEnumerableObservers"),this},removeEnumerableObserver:function(e,t){var n=t&&t.willChange||"enumerableWillChange",i=t&&t.didChange||"enumerableDidChange",a=r.get(this,"hasEnumerableObservers");return a&&o.propertyWillChange(this,"hasEnumerableObservers"),u.removeListener(this,"@enumerable:before",e,n),u.removeListener(this,"@enumerable:change",e,i),a&&o.propertyDidChange(this,"hasEnumerableObservers"),this},hasEnumerableObservers:s.computed(function(){return u.hasListeners(this,"@enumerable:change")||u.hasListeners(this,"@enumerable:before")}),enumerableContentWillChange:function(e,t){var n,i,a;return n="number"==typeof e?e:e?r.get(e,"length"):e=-1,i="number"==typeof t?t:t?r.get(t,"length"):t=-1,a=0>i||0>n||i-n!==0,-1===e&&(e=null),-1===t&&(t=null),o.propertyWillChange(this,"[]"),a&&o.propertyWillChange(this,"length"),u.sendEvent(this,"@enumerable:before",[this,e,t]),this},enumerableContentDidChange:function(e,t){var n,i,a;return n="number"==typeof e?e:e?r.get(e,"length"):e=-1,i="number"==typeof t?t:t?r.get(t,"length"):t=-1,a=0>i||0>n||i-n!==0,-1===e&&(e=null),-1===t&&(t=null),u.sendEvent(this,"@enumerable:change",[this,e,t]),a&&o.propertyDidChange(this,"length"),o.propertyDidChange(this,"[]"),this},sortBy:function(){var e=arguments;return this.toArray().sort(function(t,n){for(var i=0;i1?t-1:0),i=1;t>i;i++)n[i-1]=arguments[i];r.sendEvent(this,e,n)},off:function(e,t,n){return r.removeListener(this,e,t,n),this},has:function(e){return r.hasListeners(this,e)}})}),e("ember-runtime/mixins/freezable",["exports","ember-metal/mixin","ember-metal/property_get","ember-metal/property_set"],function(e,t,r,n){"use strict";var i=t.Mixin.create({isFrozen:!1,freeze:function(){return r.get(this,"isFrozen")?this:(n.set(this,"isFrozen",!0),this)}}),a="Frozen object cannot be modified.";e.Freezable=i,e.FROZEN_ERROR=a}),e("ember-runtime/mixins/mutable_array",["exports","ember-metal/property_get","ember-metal/utils","ember-metal/error","ember-metal/mixin","ember-runtime/mixins/array","ember-runtime/mixins/mutable_enumerable","ember-runtime/mixins/enumerable"],function(e,t,r,n,i,a,s,o){"use strict";var u="Index out of range",l=[];e["default"]=i.Mixin.create(a["default"],s["default"],{replace:null,clear:function(){var e=t.get(this,"length");return 0===e?this:(this.replace(0,e,l),this)},insertAt:function(e,r){if(e>t.get(this,"length"))throw new n["default"](u);return this.replace(e,0,[r]),this},removeAt:function(e,r){if("number"==typeof e){if(0>e||e>=t.get(this,"length"))throw new n["default"](u);void 0===r&&(r=1),this.replace(e,r,l)}return this},pushObject:function(e){return this.insertAt(t.get(this,"length"),e),e},pushObjects:function(e){if(!o["default"].detect(e)&&!r.isArray(e))throw new TypeError("Must pass Ember.Enumerable to Ember.MutableArray#pushObjects");return this.replace(t.get(this,"length"),0,e),this},popObject:function(){var e=t.get(this,"length");if(0===e)return null;var r=this.objectAt(e-1);return this.removeAt(e-1,1),r},shiftObject:function(){if(0===t.get(this,"length"))return null;var e=this.objectAt(0);return this.removeAt(0),e},unshiftObject:function(e){return this.insertAt(0,e),e},unshiftObjects:function(e){return this.replace(0,0,e),this},reverseObjects:function(){var e=t.get(this,"length");if(0===e)return this;var r=this.toArray().reverse();return this.replace(0,e,r),this},setObjects:function(e){if(0===e.length)return this.clear();var r=t.get(this,"length");return this.replace(0,r,e),this},removeObject:function(e){for(var r=t.get(this,"length")||0;--r>=0;){var n=this.objectAt(r);n===e&&this.removeAt(r)}return this},addObject:function(e){return this.contains(e)||this.pushObject(e),this}})}),e("ember-runtime/mixins/mutable_enumerable",["exports","ember-metal/enumerable_utils","ember-runtime/mixins/enumerable","ember-metal/mixin","ember-metal/property_events"],function(e,t,r,n,i){"use strict";e["default"]=n.Mixin.create(r["default"],{addObject:null,addObjects:function(e){return i.beginPropertyChanges(this),t.forEach(e,function(e){this.addObject(e)},this),i.endPropertyChanges(this),this},removeObject:null,removeObjects:function(e){i.beginPropertyChanges(this);for(var t=e.length-1;t>=0;t--)this.removeObject(e[t]);return i.endPropertyChanges(this),this}})}),e("ember-runtime/mixins/observable",["exports","ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/get_properties","ember-metal/set_properties","ember-metal/mixin","ember-metal/events","ember-metal/property_events","ember-metal/observer","ember-metal/computed","ember-metal/is_none"],function(e,t,r,n,i,a,s,o,u,l,c,h){"use strict";e["default"]=s.Mixin.create({get:function(e){return r.get(this,e)},getProperties:function(){for(var e=arguments.length,t=Array(e),r=0;e>r;r++)t[r]=arguments[r];return i["default"].apply(null,[this].concat(t))},set:function(e,t){return n.set(this,e,t),this},setProperties:function(e){return a["default"](this,e)},beginPropertyChanges:function(){return u.beginPropertyChanges(),this},endPropertyChanges:function(){return u.endPropertyChanges(),this},propertyWillChange:function(e){return u.propertyWillChange(this,e),this},propertyDidChange:function(e){return u.propertyDidChange(this,e),this},notifyPropertyChange:function(e){return this.propertyWillChange(e),this.propertyDidChange(e),this},addBeforeObserver:function(e,t,r){l.addBeforeObserver(this,e,t,r)},addObserver:function(e,t,r){l.addObserver(this,e,t,r)},removeObserver:function(e,t,r){l.removeObserver(this,e,t,r)},hasObserverFor:function(e){return o.hasListeners(this,e+":change")},getWithDefault:function(e,t){return r.getWithDefault(this,e,t)},incrementProperty:function(e,t){return h["default"](t)&&(t=1),n.set(this,e,(parseFloat(r.get(this,e))||0)+t),r.get(this,e)},decrementProperty:function(e,t){return h["default"](t)&&(t=1),n.set(this,e,(r.get(this,e)||0)-t),r.get(this,e)},toggleProperty:function(e){return n.set(this,e,!r.get(this,e)),r.get(this,e)},cacheFor:function(e){return c.cacheFor(this,e)},observersForKey:function(e){return l.observersFor(this,e)}})}),e("ember-runtime/mixins/promise_proxy",["exports","ember-metal/property_get","ember-metal/set_properties","ember-metal/computed","ember-metal/mixin","ember-metal/error"],function(e,t,r,n,i,a){"use strict";function s(e,t){return r["default"](e,{isFulfilled:!1,isRejected:!1}),t.then(function(t){return r["default"](e,{content:t,isFulfilled:!0}),t},function(t){throw r["default"](e,{reason:t,isRejected:!0}),t},"Ember: PromiseProxy")}function o(e){return function(){var r=t.get(this,"promise");return r[e].apply(r,arguments)}}var u=n.computed.not,l=n.computed.or;e["default"]=i.Mixin.create({reason:null,isPending:u("isSettled").readOnly(),isSettled:l("isRejected","isFulfilled").readOnly(),isRejected:!1,isFulfilled:!1,promise:n.computed({get:function(){throw new a["default"]("PromiseProxy's promise must be set")},set:function(e,t){return s(this,t)}}),then:o("then"),"catch":o("catch"),"finally":o("finally")})}),e("ember-runtime/mixins/sortable",["exports","ember-metal/core","ember-metal/property_get","ember-metal/enumerable_utils","ember-runtime/mixins/mutable_enumerable","ember-runtime/compare","ember-metal/observer","ember-metal/computed","ember-metal/computed_macros","ember-metal/mixin"],function(e,t,r,n,i,a,s,o,u,l){"use strict";e["default"]=l.Mixin.create(i["default"],{sortProperties:null,sortAscending:!0,sortFunction:a["default"],orderBy:function(e,t){var i=0,a=r.get(this,"sortProperties"),s=r.get(this,"sortAscending"),o=r.get(this,"sortFunction");return n.forEach(a,function(n){0===i&&(i=o.call(this,r.get(e,n),r.get(t,n)),0===i||s||(i=-1*i))},this),i},destroy:function(){var e=r.get(this,"content"),t=r.get(this,"sortProperties");return e&&t&&n.forEach(e,function(e){n.forEach(t,function(t){s.removeObserver(e,t,this,"contentItemSortPropertyDidChange")},this)},this),this._super.apply(this,arguments)},isSorted:u.notEmpty("sortProperties"),arrangedContent:o.computed("content","sortProperties.@each",{get:function(e){var i=r.get(this,"content"),a=r.get(this,"isSorted"),o=r.get(this,"sortProperties"),u=this;return i&&a?(i=i.slice(),i.sort(function(e,t){return u.orderBy(e,t)}),n.forEach(i,function(e){n.forEach(o,function(t){s.addObserver(e,t,this,"contentItemSortPropertyDidChange")},this)},this),t["default"].A(i)):i}}),_contentWillChange:l.beforeObserver("content",function(){var e=r.get(this,"content"),t=r.get(this,"sortProperties");e&&t&&n.forEach(e,function(e){n.forEach(t,function(t){s.removeObserver(e,t,this,"contentItemSortPropertyDidChange")},this)},this),this._super.apply(this,arguments)}),sortPropertiesWillChange:l.beforeObserver("sortProperties",function(){this._lastSortAscending=void 0}),sortPropertiesDidChange:l.observer("sortProperties",function(){this._lastSortAscending=void 0}),sortAscendingWillChange:l.beforeObserver("sortAscending",function(){this._lastSortAscending=r.get(this,"sortAscending")}),sortAscendingDidChange:l.observer("sortAscending",function(){if(void 0!==this._lastSortAscending&&r.get(this,"sortAscending")!==this._lastSortAscending){var e=r.get(this,"arrangedContent");e.reverseObjects()}}),contentArrayWillChange:function(e,t,i,a){var o=r.get(this,"isSorted");if(o){var u=r.get(this,"arrangedContent"),l=e.slice(t,t+i),c=r.get(this,"sortProperties");n.forEach(l,function(e){u.removeObject(e),n.forEach(c,function(t){s.removeObserver(e,t,this,"contentItemSortPropertyDidChange")},this)},this)}return this._super(e,t,i,a)},contentArrayDidChange:function(e,t,i,a){var o=r.get(this,"isSorted"),u=r.get(this,"sortProperties");if(o){var l=e.slice(t,t+a);n.forEach(l,function(e){this.insertItemSorted(e),n.forEach(u,function(t){s.addObserver(e,t,this,"contentItemSortPropertyDidChange")},this)},this)}return this._super(e,t,i,a)},insertItemSorted:function(e){var t=r.get(this,"arrangedContent"),n=r.get(t,"length"),i=this._binarySearch(e,0,n);t.insertAt(i,e)},contentItemSortPropertyDidChange:function(e){var t=r.get(this,"arrangedContent"),n=t.indexOf(e),i=t.objectAt(n-1),a=t.objectAt(n+1),s=i&&this.orderBy(e,i),o=a&&this.orderBy(e,a);(0>s||o>0)&&(t.removeObject(e),this.insertItemSorted(e))},_binarySearch:function(e,t,n){var i,a,s,o;return t===n?t:(o=r.get(this,"arrangedContent"),i=t+Math.floor((n-t)/2),a=o.objectAt(i),s=this.orderBy(a,e),0>s?this._binarySearch(e,i+1,n):s>0?this._binarySearch(e,t,i):i)}})}),e("ember-runtime/mixins/target_action_support",["exports","ember-metal/core","ember-metal/property_get","ember-metal/utils","ember-metal/mixin","ember-metal/computed"],function(e,t,r,n,i,a){"use strict";var s=i.Mixin.create({target:null,action:null,actionContext:null,targetObject:a.computed(function(){var e=r.get(this,"target");if("string"===n.typeOf(e)){var i=r.get(this,e);return void 0===i&&(i=r.get(t["default"].lookup,e)),i}return e}).property("target"),actionContextObject:a.computed(function(){var e=r.get(this,"actionContext");if("string"===n.typeOf(e)){var i=r.get(this,e);return void 0===i&&(i=r.get(t["default"].lookup,e)),i}return e}).property("actionContext"),triggerAction:function(e){function t(e,t){var r=[];return t&&r.push(t),r.concat(e)}e=e||{};var n=e.action||r.get(this,"action"),i=e.target||r.get(this,"targetObject"),a=e.actionContext;if("undefined"==typeof a&&(a=r.get(this,"actionContextObject")||this),i&&n){var s;return s=i.send?i.send.apply(i,t(a,n)):i[n].apply(i,t(a)),s!==!1&&(s=!0),s}return!1}});e["default"]=s}),e("ember-runtime/system/application",["exports","ember-runtime/system/namespace"],function(e,t){"use strict";e["default"]=t["default"].extend()}),e("ember-runtime/system/array_proxy",["exports","ember-metal/core","ember-metal/property_get","ember-metal/utils","ember-metal/computed","ember-metal/mixin","ember-metal/property_events","ember-metal/error","ember-runtime/system/object","ember-runtime/mixins/mutable_array","ember-runtime/mixins/enumerable","ember-runtime/system/string","ember-metal/alias"],function(e,t,r,n,i,a,s,o,u,l,c,h,m){ +"use strict";function d(){return this}var f="Index out of range",p=[],v=u["default"].extend(l["default"],{content:null,arrangedContent:m["default"]("content"),objectAtContent:function(e){return r.get(this,"arrangedContent").objectAt(e)},replaceContent:function(e,t,n){r.get(this,"content").replace(e,t,n)},_contentWillChange:a.beforeObserver("content",function(){this._teardownContent()}),_teardownContent:function(){var e=r.get(this,"content");e&&e.removeArrayObserver(this,{willChange:"contentArrayWillChange",didChange:"contentArrayDidChange"})},contentArrayWillChange:d,contentArrayDidChange:d,_contentDidChange:a.observer("content",function(){r.get(this,"content");this._setupContent()}),_setupContent:function(){var e=r.get(this,"content");e&&e.addArrayObserver(this,{willChange:"contentArrayWillChange",didChange:"contentArrayDidChange"})},_arrangedContentWillChange:a.beforeObserver("arrangedContent",function(){var e=r.get(this,"arrangedContent"),t=e?r.get(e,"length"):0;this.arrangedContentArrayWillChange(this,0,t,void 0),this.arrangedContentWillChange(this),this._teardownArrangedContent(e)}),_arrangedContentDidChange:a.observer("arrangedContent",function(){var e=r.get(this,"arrangedContent"),t=e?r.get(e,"length"):0;this._setupArrangedContent(),this.arrangedContentDidChange(this),this.arrangedContentArrayDidChange(this,0,void 0,t)}),_setupArrangedContent:function(){var e=r.get(this,"arrangedContent");e&&e.addArrayObserver(this,{willChange:"arrangedContentArrayWillChange",didChange:"arrangedContentArrayDidChange"})},_teardownArrangedContent:function(){var e=r.get(this,"arrangedContent");e&&e.removeArrayObserver(this,{willChange:"arrangedContentArrayWillChange",didChange:"arrangedContentArrayDidChange"})},arrangedContentWillChange:d,arrangedContentDidChange:d,objectAt:function(e){return r.get(this,"content")&&this.objectAtContent(e)},length:i.computed(function(){var e=r.get(this,"arrangedContent");return e?r.get(e,"length"):0}),_replace:function(e,t,n){var i=r.get(this,"content");return i&&this.replaceContent(e,t,n),this},replace:function(){if(r.get(this,"arrangedContent")!==r.get(this,"content"))throw new o["default"]("Using replace on an arranged ArrayProxy is not allowed.");this._replace.apply(this,arguments)},_insertAt:function(e,t){if(e>r.get(this,"content.length"))throw new o["default"](f);return this._replace(e,0,[t]),this},insertAt:function(e,t){if(r.get(this,"arrangedContent")===r.get(this,"content"))return this._insertAt(e,t);throw new o["default"]("Using insertAt on an arranged ArrayProxy is not allowed.")},removeAt:function(e,t){if("number"==typeof e){var n,i=r.get(this,"content"),a=r.get(this,"arrangedContent"),u=[];if(0>e||e>=r.get(this,"length"))throw new o["default"](f);for(void 0===t&&(t=1),n=e;e+t>n;n++)u.push(i.indexOf(a.objectAt(n)));for(u.sort(function(e,t){return t-e}),s.beginPropertyChanges(),n=0;ny;y++){var w=v[y];if("object"!=typeof w&&void 0!==w)throw new c["default"]("Ember.Object.create only accepts objects.");if(w)for(var x=m["default"](w),C=0,E=x.length;E>C;C++){var O=x[C],S=w[O];if(u.IS_BINDING.test(O)){var A=d.bindings;A?d.hasOwnProperty("bindings")||(A=d.bindings=a["default"](d.bindings)):A=d.bindings={},A[O]=S}var N=this[O],T=null!==N&&"object"==typeof N&&N.isDescriptor?N:void 0;if(g&&g.length>0&&l.indexOf(g,O)>=0){var k=this[O];S=k?"function"==typeof k.concat?k.concat(S):i.makeArray(k).concat(S):i.makeArray(S)}if(b&&b.length&&l.indexOf(b,O)>=0){var M=this[O];S=r["default"](M,S)}T?T.set(this,O,S):"function"!=typeof this.setUnknownProperty||O in this?this[O]=S:this.setUnknownProperty(O,S)}}}P(this,d);var I=arguments.length;if(0===I)this.init();else if(1===I)this.init(arguments[0]);else{for(var V=new Array(I),R=0;I>R;R++)V[R]=arguments[R];this.init.apply(this,V)}d.proto=f,s.finishChains(this),o.sendEvent(this,"init")};return h.toString=u.Mixin.prototype.toString,h.willReopen=function(){n&&(h.PrototypeMixin=u.Mixin.create(h.PrototypeMixin)),n=!1},h._initMixins=function(t){e=t},h._initProperties=function(e){t=e},h.proto=function(){var e=h.superclass;return e&&e.proto(),n||(n=!0,h.PrototypeMixin.applyPartial(h.prototype)),this.prototype},h}function C(e){return function(){return e}}var E=b["default"].schedule,O=u.Mixin._apply,P=u.Mixin.finishPartial,S=u.Mixin.prototype.reopen,A=!1,N=x();N.toString=function(){return"Ember.CoreObject"},N.PrototypeMixin=u.Mixin.create({reopen:function(){for(var e=arguments.length,t=Array(e),r=0;e>r;r++)t[r]=arguments[r];return O(this,t,!0),this},init:function(){},__defineNonEnumerable:function(e){h.defineProperty(this,e.name,e.descriptor)},concatenatedProperties:null,isDestroyed:!1,isDestroying:!1,destroy:function(){return this.isDestroying?void 0:(this.isDestroying=!0,E("actions",this,this.willDestroy),E("destroy",this,this._scheduledDestroy),this)},willDestroy:_.K,_scheduledDestroy:function(){this.isDestroyed||(y.destroy(this),this.isDestroyed=!0)},bind:function(e,t){return t instanceof p.Binding||(t=p.Binding.from(t)),t.to(e).connect(this),t},toString:function(){var e="function"==typeof this.toStringExtension,t=e?":"+this.toStringExtension():"",r="<"+this.constructor.toString()+":"+i.guidFor(this)+t+">";return this.toString=C(r),r}}),N.PrototypeMixin.ownerConstructor=N,N.__super__=null;var T={ClassMixin:u.REQUIRED,PrototypeMixin:u.REQUIRED,isClass:!0,isMethod:!1,extend:function(){var e,t=x();return t.ClassMixin=u.Mixin.create(this.ClassMixin),t.PrototypeMixin=u.Mixin.create(this.PrototypeMixin),t.ClassMixin.ownerConstructor=t,t.PrototypeMixin.ownerConstructor=t,S.apply(t.PrototypeMixin,arguments),t.superclass=this,t.__super__=this.prototype,e=t.prototype=a["default"](this.prototype),e.constructor=t,i.generateGuid(e),i.meta(e).proto=e,t.ClassMixin.apply(t),t},createWithMixins:function(){for(var e=arguments.length,t=Array(e),r=0;e>r;r++)t[r]=arguments[r];var n=this;return t.length>0&&this._initMixins(t),new n},create:function(){for(var e=arguments.length,t=Array(e),r=0;e>r;r++)t[r]=arguments[r];var n=this;return t.length>0&&this._initProperties(t),new n},reopen:function(){return this.willReopen(),S.apply(this.PrototypeMixin,arguments),this},reopenClass:function(){return S.apply(this.ClassMixin,arguments),O(this,arguments,!1),this},detect:function(e){if("function"!=typeof e)return!1;for(;e;){if(e===this)return!0;e=e.superclass}return!1},detectInstance:function(e){return e instanceof this},metaForProperty:function(e){var t=this.proto(),r=t[e],n=null!==r&&"object"==typeof r&&r.isDescriptor?r:void 0;return n._meta||{}},_computedProperties:v.computed(function(){A=!0;var e,t=this.proto(),r=[];for(var n in t)e=t[n],e instanceof v.ComputedProperty&&r.push({name:n,meta:e._meta});return r}).readOnly(),eachComputedProperty:function(e,t){for(var r,i,a={},s=n.get(this,"_computedProperties"),o=0,u=s.length;u>o;o++)r=s[o],i=r.name,e.call(t||this,r.name,r.meta||a)}};T._lazyInjections=function(){var e,t,r={},n=this.proto();for(e in n)t=n[e],t instanceof g["default"]&&(r[e]=t.type+":"+(t.name||e));return r};var k=u.Mixin.create(T);k.ownerConstructor=N,N.ClassMixin=k,k.apply(N),N.reopen({didDefineProperty:function(e,r,n){if(A!==!1&&n instanceof t["default"].ComputedProperty){var i=t["default"].meta(this.constructor).cache;i&&void 0!==i._computedProperties&&(i._computedProperties=void 0)}}}),e["default"]=N}),e("ember-runtime/system/deferred",["exports","ember-metal/core","ember-runtime/mixins/deferred","ember-runtime/system/object"],function(e,t,r,n){"use strict";var i=n["default"].extend(r["default"],{init:function(){this._super.apply(this,arguments)}});i.reopenClass({promise:function(e,t){var r=i.create();return e.call(t,r),r}}),e["default"]=i}),e("ember-runtime/system/each_proxy",["exports","ember-metal/core","ember-metal/property_get","ember-metal/utils","ember-metal/enumerable_utils","ember-metal/array","ember-runtime/mixins/array","ember-runtime/system/object","ember-metal/computed","ember-metal/observer","ember-metal/events","ember-metal/properties","ember-metal/property_events"],function(e,t,r,n,i,a,s,o,u,l,c,h,m){"use strict";function d(e,t,r,i,a){var s,o=r._objects;for(o||(o=r._objects={});--a>=i;){var u=e.objectAt(a);u&&(l.addBeforeObserver(u,t,r,"contentKeyWillChange"),l.addObserver(u,t,r,"contentKeyDidChange"),s=n.guidFor(u),o[s]||(o[s]=[]),o[s].push(a))}}function f(e,t,r,i,s){var o=r._objects;o||(o=r._objects={});for(var u,c;--s>=i;){var h=e.objectAt(s);h&&(l.removeBeforeObserver(h,t,r,"contentKeyWillChange"),l.removeObserver(h,t,r,"contentKeyDidChange"),c=n.guidFor(h),u=o[c],u[a.indexOf.call(u,s)]=null)}}var p=o["default"].extend(s["default"],{init:function(e,t,r){this._super.apply(this,arguments),this._keyName=t,this._owner=r,this._content=e},objectAt:function(e){var t=this._content.objectAt(e);return t&&r.get(t,this._keyName)},length:u.computed(function(){var e=this._content;return e?r.get(e,"length"):0})}),v=/^.+:(before|change)$/,g=o["default"].extend({init:function(e){this._super.apply(this,arguments),this._content=e,e.addArrayObserver(this),i.forEach(c.watchedEvents(this),function(e){this.didAddListener(e)},this)},unknownProperty:function(e,t){var r=new p(this._content,e,this);return h.defineProperty(this,e,null,r),this.beginObservingContentKey(e),r},arrayWillChange:function(e,t,r,n){var i,a,s=this._keys;a=r>0?t+r:-1,m.beginPropertyChanges(this);for(i in s)s.hasOwnProperty(i)&&(a>0&&f(e,i,this,t,a),m.propertyWillChange(this,i));m.propertyWillChange(this._content,"@each"),m.endPropertyChanges(this)},arrayDidChange:function(e,t,r,n){var i,a=this._keys;i=n>0?t+n:-1,m.changeProperties(function(){for(var r in a)a.hasOwnProperty(r)&&(i>0&&d(e,r,this,t,i),m.propertyDidChange(this,r));m.propertyDidChange(this._content,"@each")},this)},didAddListener:function(e){v.test(e)&&this.beginObservingContentKey(e.slice(0,-7))},didRemoveListener:function(e){v.test(e)&&this.stopObservingContentKey(e.slice(0,-7))},beginObservingContentKey:function(e){var t=this._keys;if(t||(t=this._keys={}),t[e])t[e]++;else{t[e]=1;var n=this._content,i=r.get(n,"length");d(n,e,this,0,i)}},stopObservingContentKey:function(e){var t=this._keys;if(t&&t[e]>0&&--t[e]<=0){var n=this._content,i=r.get(n,"length");f(n,e,this,0,i)}},contentKeyWillChange:function(e,t){m.propertyWillChange(this,t)},contentKeyDidChange:function(e,t){m.propertyDidChange(this,t)}});e.EachArray=p,e.EachProxy=g}),e("ember-runtime/system/lazy_load",["exports","ember-metal/core","ember-metal/array","ember-runtime/system/native_array"],function(e,t,r){"use strict";function n(e,r){var n;a[e]=a[e]||t["default"].A(),a[e].pushObject(r),(n=s[e])&&r(n)}function i(e,t){if(s[e]=t,"object"==typeof window&&"function"==typeof window.dispatchEvent&&"function"==typeof CustomEvent){var n=new CustomEvent(e,{detail:t,name:e});window.dispatchEvent(n)}a[e]&&r.forEach.call(a[e],function(e){e(t)})}e.onLoad=n,e.runLoadHooks=i;var a=t["default"].ENV.EMBER_LOAD_HOOKS||{},s={}}),e("ember-runtime/system/namespace",["exports","ember-metal/core","ember-metal/property_get","ember-metal/array","ember-metal/utils","ember-metal/mixin","ember-runtime/system/object"],function(e,t,r,n,i,a,s){"use strict";function o(e,t,r){var n=e.length;p[e.join(".")]=t;for(var a in t)if(v.call(t,a)){var s=t[a];if(e[n]=a,s&&s.toString===h)s.toString=d(e.join(".")),s[b]=e.join(".");else if(s&&s.isNamespace){if(r[i.guidFor(s)])continue;r[i.guidFor(s)]=!0,o(e,s,r)}}e.length=n}function u(e,t){try{var r=e[t];return r&&r.isNamespace&&r}catch(n){}}function l(){var e,r=t["default"].lookup;if(!f.PROCESSED)for(var n in r)g.test(n)&&(!r.hasOwnProperty||r.hasOwnProperty(n))&&(e=u(r,n),e&&(e[b]=n))}function c(e){var t=e.superclass;return t?t[b]?t[b]:c(t):void 0}function h(){t["default"].BOOTED||this[b]||m();var e;if(this[b])e=this[b];else if(this._toString)e=this._toString;else{var r=c(this);e=r?"(subclass of "+r+")":"(unknown mixin)",this.toString=d(e)}return e}function m(){var e=!f.PROCESSED,r=t["default"].anyUnprocessedMixins;if(e&&(l(),f.PROCESSED=!0),e||r){for(var n,i=f.NAMESPACES,a=0,s=i.length;s>a;a++)n=i[a],o([n.toString()],n,{});t["default"].anyUnprocessedMixins=!1}}function d(e){return function(){return e}}var f=s["default"].extend({isNamespace:!0,init:function(){f.NAMESPACES.push(this),f.PROCESSED=!1},toString:function(){var e=r.get(this,"name")||r.get(this,"modulePrefix");return e?e:(l(),this[b])},nameClasses:function(){o([this.toString()],this,{})},destroy:function(){var e=f.NAMESPACES,r=this.toString();r&&(t["default"].lookup[r]=void 0,delete f.NAMESPACES_BY_ID[r]),e.splice(n.indexOf.call(e,this),1),this._super.apply(this,arguments)}});f.reopenClass({NAMESPACES:[t["default"]],NAMESPACES_BY_ID:{},PROCESSED:!1,processAll:m,byName:function(e){return t["default"].BOOTED||m(),p[e]}});var p=f.NAMESPACES_BY_ID,v={}.hasOwnProperty,g=/^[A-Z]/,b=t["default"].NAME_KEY=i.GUID_KEY+"_name";a.Mixin.prototype.toString=h,e["default"]=f}),e("ember-runtime/system/native_array",["exports","ember-metal/core","ember-metal/property_get","ember-metal/enumerable_utils","ember-metal/mixin","ember-metal/array","ember-runtime/mixins/array","ember-runtime/mixins/mutable_array","ember-runtime/mixins/observable","ember-runtime/mixins/copyable","ember-runtime/mixins/freezable","ember-runtime/copy"],function(e,t,r,n,i,a,s,o,u,l,c,h){"use strict";var m=i.Mixin.create(o["default"],u["default"],l["default"],{get:function(e){return"length"===e?this.length:"number"==typeof e?this[e]:this._super(e)},objectAt:function(e){return this[e]},replace:function(e,t,i){if(this.isFrozen)throw c.FROZEN_ERROR;var a=i?r.get(i,"length"):0;return this.arrayContentWillChange(e,t,a),0===a?this.splice(e,t):n._replace(this,e,t,i),this.arrayContentDidChange(e,t,a),this},unknownProperty:function(e,t){var r;return void 0!==t&&void 0===r&&(r=this[e]=t),r},indexOf:a.indexOf,lastIndexOf:a.lastIndexOf,copy:function(e){return e?this.map(function(e){return h["default"](e,!0)}):this.slice()}}),d=["length"];n.forEach(m.keys(),function(e){Array.prototype[e]&&d.push(e)}),m=m.without.apply(m,d);var f=function(e){return void 0===e&&(e=[]),s["default"].detect(e)?e:m.apply(e)};m.activate=function(){m.apply(Array.prototype),e.A=f=function(e){return e||[]}},(t["default"].EXTEND_PROTOTYPES===!0||t["default"].EXTEND_PROTOTYPES.Array)&&m.activate(),t["default"].A=f,e["default"]=m,e.A=f,e.NativeArray=m}),e("ember-runtime/system/object",["exports","ember-runtime/system/core_object","ember-runtime/mixins/observable"],function(e,t,r){"use strict";var n=t["default"].extend(r["default"]);n.toString=function(){return"Ember.Object"},e["default"]=n}),e("ember-runtime/system/object_proxy",["exports","ember-runtime/system/object","ember-runtime/mixins/-proxy"],function(e,t,r){"use strict";e["default"]=t["default"].extend(r["default"])}),e("ember-runtime/system/service",["exports","ember-runtime/system/object","ember-runtime/inject"],function(e,t,r){"use strict";r.createInjectionHelper("service"),e["default"]=t["default"].extend()}),e("ember-runtime/system/set",["exports","ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/utils","ember-metal/is_none","ember-runtime/system/string","ember-runtime/system/core_object","ember-runtime/mixins/mutable_enumerable","ember-runtime/mixins/enumerable","ember-runtime/mixins/copyable","ember-runtime/mixins/freezable","ember-metal/error","ember-metal/property_events","ember-metal/mixin","ember-metal/computed"],function(e,t,r,n,i,a,s,o,u,l,c,h,m,d,f,p){"use strict";e["default"]=o["default"].extend(u["default"],c["default"],h.Freezable,{length:0,clear:function(){if(this.isFrozen)throw new m["default"](h.FROZEN_ERROR);var e=r.get(this,"length");if(0===e)return this;var t;this.enumerableContentWillChange(e,0),d.propertyWillChange(this,"firstObject"),d.propertyWillChange(this,"lastObject");for(var a=0;e>a;a++)t=i.guidFor(this[a]),delete this[t],delete this[a];return n.set(this,"length",0),d.propertyDidChange(this,"firstObject"),d.propertyDidChange(this,"lastObject"),this.enumerableContentDidChange(e,0),this},isEqual:function(e){if(!l["default"].detect(e))return!1;var t=r.get(this,"length");if(r.get(e,"length")!==t)return!1;for(;--t>=0;)if(!e.contains(this[t]))return!1;return!0},add:f.aliasMethod("addObject"),remove:f.aliasMethod("removeObject"),pop:function(){if(r.get(this,"isFrozen"))throw new m["default"](h.FROZEN_ERROR);var e=this.length>0?this[this.length-1]:null;return this.remove(e),e},push:f.aliasMethod("addObject"),shift:f.aliasMethod("pop"),unshift:f.aliasMethod("push"),addEach:f.aliasMethod("addObjects"),removeEach:f.aliasMethod("removeObjects"),init:function(e){this._super.apply(this,arguments),e&&this.addObjects(e)},nextObject:function(e){return this[e]},firstObject:p.computed(function(){return this.length>0?this[0]:void 0}),lastObject:p.computed(function(){return this.length>0?this[this.length-1]:void 0}),addObject:function(e){if(r.get(this,"isFrozen"))throw new m["default"](h.FROZEN_ERROR);if(a["default"](e))return this;var t,s=i.guidFor(e),o=this[s],u=r.get(this,"length");return o>=0&&u>o&&this[o]===e?this:(t=[e],this.enumerableContentWillChange(null,t),d.propertyWillChange(this,"lastObject"),u=r.get(this,"length"),this[s]=u,this[u]=e,n.set(this,"length",u+1),d.propertyDidChange(this,"lastObject"),this.enumerableContentDidChange(null,t),this)},removeObject:function(e){if(r.get(this,"isFrozen"))throw new m["default"](h.FROZEN_ERROR);if(a["default"](e))return this;var t,s,o=i.guidFor(e),u=this[o],l=r.get(this,"length"),c=0===u,f=u===l-1;return u>=0&&l>u&&this[u]===e&&(s=[e],this.enumerableContentWillChange(s,null),c&&d.propertyWillChange(this,"firstObject"),f&&d.propertyWillChange(this,"lastObject"),l-1>u&&(t=this[l-1],this[u]=t,this[i.guidFor(t)]=u),delete this[o],delete this[l-1],n.set(this,"length",l-1),c&&d.propertyDidChange(this,"firstObject"),f&&d.propertyDidChange(this,"lastObject"),this.enumerableContentDidChange(s,null)),this},contains:function(e){return this[i.guidFor(e)]>=0},copy:function(){var e=this.constructor,t=new e,a=r.get(this,"length");for(n.set(t,"length",a);--a>=0;)t[a]=this[a],t[i.guidFor(this[a])]=a;return t},toString:function(){var e,t=this.length,r=[];for(e=0;t>e;e++)r[e]=this[e];return s.fmt("Ember.Set<%@>",[r.join(",")])}})}),e("ember-runtime/system/string",["exports","ember-metal/core","ember-metal/utils","ember-metal/cache"],function(e,t,r,n){"use strict";function i(e,t){var n=t;if(!r.isArray(n)||arguments.length>2){n=new Array(arguments.length-1);for(var i=1,a=arguments.length;a>i;i++)n[i-1]=arguments[i]}var s=0;return e.replace(/%@([0-9]+)?/g,function(e,t){return t=t?parseInt(t,10)-1:s++,e=n[t],null===e?"(null)":void 0===e?"":r.inspect(e)})}function a(e,n){return(!r.isArray(n)||arguments.length>2)&&(n=Array.prototype.slice.call(arguments,1)),e=t["default"].STRINGS[e]||e,i(e,n)}function s(e){return e.split(/\s+/)}function o(e){return y.get(e)}function u(e){return f.get(e)}function l(e){return p.get(e)}function c(e){return v.get(e)}function h(e){return g.get(e)}function m(e){return b.get(e)}e.fmt=i,e.loc=a,e.w=s,e.decamelize=o,e.dasherize=u,e.camelize=l,e.classify=c,e.underscore=h,e.capitalize=m;var d=/[ _]/g,f=new n["default"](1e3,function(e){return o(e).replace(d,"-")}),p=new n["default"](1e3,function(e){return e.replace(w,function(e,t,r){return r?r.toUpperCase():""}).replace(/^([A-Z])/,function(e,t,r){return e.toLowerCase()})}),v=new n["default"](1e3,function(e){for(var t=e.split("."),r=[],n=0,i=t.length;i>n;n++){var a=l(t[n]);r.push(a.charAt(0).toUpperCase()+a.substr(1))}return r.join(".")}),g=new n["default"](1e3,function(e){return e.replace(x,"$1_$2").replace(C,"_").toLowerCase()}),b=new n["default"](1e3,function(e){return e.charAt(0).toUpperCase()+e.substr(1)}),y=new n["default"](1e3,function(e){return e.replace(_,"$1_$2").toLowerCase()}),_=/([a-z\d])([A-Z])/g,w=/(\-|_|\.|\s)+(.)?/g,x=/([a-z\d])([A-Z]+)/g,C=/\-|\s+/g;t["default"].STRINGS={},e["default"]={fmt:i,loc:a,w:s,decamelize:o,dasherize:u,camelize:l,classify:c,underscore:h,capitalize:m}}),e("ember-runtime/system/subarray",["exports","ember-metal/error","ember-metal/enumerable_utils"],function(e,t,r){"use strict";function n(e,t){this.type=e,this.count=t}function i(e){arguments.length<1&&(e=0),e>0?this._operations=[new n(a,e)]:this._operations=[]}var a="r",s="f";e["default"]=i,i.prototype={addItem:function(e,t){var r=-1,i=t?a:s,o=this;return this._findOperation(e,function(s,u,l,c,h){var m,d;i===s.type?++s.count:e===l?o._operations.splice(u,0,new n(i,1)):(m=new n(i,1),d=new n(s.type,c-e+1),s.count=e-l,o._operations.splice(u+1,0,m,d)),t&&(r=s.type===a?h+(e-l):h),o._composeAt(u)},function(e){o._operations.push(new n(i,1)),t&&(r=e),o._composeAt(o._operations.length-1)}),r},removeItem:function(e){var r=-1,n=this;return this._findOperation(e,function(t,i,s,o,u){t.type===a&&(r=u+(e-s)),t.count>1?--t.count:(n._operations.splice(i,1),n._composeAt(i))},function(){throw new t["default"]("Can't remove an item that has never been added.")}),r},_findOperation:function(e,t,r){var n,i,s,o,u,l=0;for(n=o=0,i=this._operations.length;i>n;o=u+1,++n){if(s=this._operations[n],u=o+s.count-1,e>=o&&u>=e)return void t(s,n,o,u,l);s.type===a&&(l+=s.count)}r(l)},_composeAt:function(e){var t,r=this._operations[e];r&&(e>0&&(t=this._operations[e-1],t.type===r.type&&(r.count+=t.count,this._operations.splice(e-1,1),--e)),en)){var a,s,u=this._findArrayOperation(e),l=u.operation,c=u.index,h=u.rangeStart;s=new i(o,n,r),l?u.split?(this._split(c,e-h,s),a=c+1):(this._operations.splice(c,0,s),a=c):(this._operations.push(s),a=c),this._composeInsert(a)}},removeItems:function(e,t){if(!(1>t)){var r,n,a=this._findArrayOperation(e),s=a.index,o=a.rangeStart;return r=new i(u,t),a.split?(this._split(s,e-o,r),n=s+1):(this._operations.splice(s,0,r),n=s),this._composeDelete(n)}},apply:function(e){var t=[],n=0;r.forEach(this._operations,function(r,i){e(r.items,n,r.type,i),r.type!==u&&(n+=r.count,t=t.concat(r.items))}),this._operations=[new i(s,t.length,t)]},_findArrayOperation:function(e){var t,r,n,i,s,o=!1;for(t=n=0,s=this._operations.length;s>t;++t)if(r=this._operations[t],r.type!==u){if(i=n+r.count-1,e===n)break;if(e>n&&i>=e){o=!0;break}n=i+1}return new a(r,t,o,n)},_split:function(e,t,r){var n=this._operations[e],a=n.items.slice(t),s=new i(n.type,a.length,a);n.count=t,n.items=n.items.slice(0,t),this._operations.splice(e+1,0,r,s)},_composeInsert:function(e){var t=this._operations[e],r=this._operations[e-1],n=this._operations[e+1],i=r&&r.type,a=n&&n.type;i===o?(r.count+=t.count,r.items=r.items.concat(t.items),a===o?(r.count+=n.count,r.items=r.items.concat(n.items),this._operations.splice(e,2)):this._operations.splice(e,1)):a===o&&(t.count+=n.count,t.items=t.items.concat(n.items),this._operations.splice(e+1,1))},_composeDelete:function(e){var t,r,n,i=this._operations[e],a=i.count,s=this._operations[e-1],l=s&&s.type,c=!1,h=[];l===u&&(i=s,e-=1);for(var m=e+1;a>0;++m)t=this._operations[m],r=t.type,n=t.count,r!==u?(n>a?(h=h.concat(t.items.splice(0,a)),t.count-=a,m-=1,n=a,a=0):(n===a&&(c=!0),h=h.concat(t.items),a-=n),r===o&&(i.count-=n)):i.count+=n;return i.count>0?this._operations.splice(e+1,m-1-e):this._operations.splice(e,c?2:1),h},toString:function(){var e="";return r.forEach(this._operations,function(t){e+=" "+t.type+":"+t.count}),e.substring(1)}}}),e("ember-template-compiler",["exports","ember-metal/core","ember-template-compiler/system/precompile","ember-template-compiler/system/compile","ember-template-compiler/system/template","ember-template-compiler/plugins","ember-template-compiler/plugins/transform-each-in-to-hash","ember-template-compiler/plugins/transform-with-as-to-hash","ember-template-compiler/compat"],function(e,t,r,n,i,a,s,o){"use strict";a.registerPlugin("ast",o["default"]),a.registerPlugin("ast",s["default"]),e._Ember=t["default"],e.precompile=r["default"],e.compile=n["default"],e.template=i["default"],e.registerPlugin=a.registerPlugin}),e("ember-template-compiler/compat",["ember-metal/core","ember-template-compiler/compat/precompile","ember-template-compiler/system/compile","ember-template-compiler/system/template"],function(e,t,r,n){"use strict";var i=e["default"].Handlebars=e["default"].Handlebars||{};i.precompile=t["default"],i.compile=r["default"],i.template=n["default"]}),e("ember-template-compiler/compat/precompile",["exports","ember-template-compiler/system/compile_options"],function(e,r){"use strict";var n,a;e["default"]=function(e){if((!n||!a)&&i.__loader.registry["htmlbars-compiler/compiler"]){var s=t("htmlbars-compiler/compiler");n=s.compile,a=s.compileSpec}if(!n||!a)throw new Error("Cannot call `precompile` without the template compiler loaded. Please load `ember-template-compiler.js` prior to calling `precompile`.");var o=void 0===arguments[1]?!0:arguments[1],u=o?n:a;return u(e,r["default"]())}}),e("ember-template-compiler/plugins",["exports"],function(e){"use strict";function t(e,t){if(!r[e])throw new Error('Attempting to register "'+t+'" as "'+e+'" which is not a valid HTMLBars plugin type.');r[e].push(t)}e.registerPlugin=t;var r={ast:[]};e["default"]=r}),e("ember-template-compiler/plugins/transform-each-in-to-hash",["exports"],function(e){"use strict";function t(e){this.syntax=null,this.options=e||{}}t.prototype.transform=function(e){var t=this,r=new t.syntax.Walker,n=t.syntax.builders;return r.visit(e,function(e){if(t.validate(e)){if(e.program&&e.program.blockParams.length)throw new Error("You cannot use keyword (`{{each foo in bar}}`) and block params (`{{each bar as |foo|}}`) at the same time.");var r=e.sexpr.params.splice(0,2),i=r[0].original;e.sexpr.hash||(e.sexpr.hash=n.hash()),e.sexpr.hash.pairs.push(n.pair("keyword",n.string(i)))}}),e},t.prototype.validate=function(e){return("BlockStatement"===e.type||"MustacheStatement"===e.type)&&"each"===e.sexpr.path.original&&3===e.sexpr.params.length&&"PathExpression"===e.sexpr.params[1].type&&"in"===e.sexpr.params[1].original},e["default"]=t}),e("ember-template-compiler/plugins/transform-with-as-to-hash",["exports"],function(e){"use strict";function t(e){this.syntax=null,this.options=e}t.prototype.transform=function(e){var t=this,r=new t.syntax.Walker;this.options.moduleName;return r.visit(e,function(e){if(t.validate(e)){if(e.program&&e.program.blockParams.length)throw new Error("You cannot use keyword (`{{with foo as bar}}`) and block params (`{{with foo as |bar|}}`) at the same time.");var r=e.sexpr.params.splice(1,2),n=r[1].original;e.program.blockParams=[n]}}),e},t.prototype.validate=function(e){return"BlockStatement"===e.type&&"with"===e.sexpr.path.original&&3===e.sexpr.params.length&&"PathExpression"===e.sexpr.params[1].type&&"as"===e.sexpr.params[1].original},e["default"]=t}),e("ember-template-compiler/system/compile",["exports","ember-template-compiler/system/compile_options","ember-template-compiler/system/template"],function(e,r,n){"use strict";var a;e["default"]=function(e,s){if(!a&&i.__loader.registry["htmlbars-compiler/compiler"]&&(a=t("htmlbars-compiler/compiler").compile),!a)throw new Error("Cannot call `compile` without the template compiler loaded. Please load `ember-template-compiler.js` prior to calling `compile`.");var o=a(e,r["default"](s));return n["default"](o)}}),e("ember-template-compiler/system/compile_options",["exports","ember-metal/core","ember-template-compiler/plugins"],function(e,t,r){"use strict";e["default"]=function(e){var t=!0,n=e||{};return n===!0&&(n={}),n.revision="Ember@1.12.1",n.disableComponentGeneration=t,n.plugins=r["default"],n}}),e("ember-template-compiler/system/precompile",["exports","ember-template-compiler/system/compile_options"],function(e,r){"use strict";var n;e["default"]=function(e,a){if(!n&&i.__loader.registry["htmlbars-compiler/compiler"]&&(n=t("htmlbars-compiler/compiler").compileSpec),!n)throw new Error("Cannot call `compileSpec` without the template compiler loaded. Please load `ember-template-compiler.js` prior to calling `compileSpec`.");return n(e,r["default"](a))}}),e("ember-template-compiler/system/template",["exports"],function(e){"use strict";e["default"]=function(e){return e.isTop=!0,e.isMethod=!1,e}}),e("ember-views",["exports","ember-runtime","ember-views/system/jquery","ember-views/system/utils","ember-views/system/render_buffer","ember-views/system/renderer","dom-helper","ember-views/system/ext","ember-views/views/states","ember-views/views/core_view","ember-views/views/view","ember-views/views/container_view","ember-views/views/collection_view","ember-views/views/component","ember-views/system/event_dispatcher","ember-views/mixins/view_target_action_support","ember-views/component_lookup","ember-views/views/checkbox","ember-views/mixins/text_support","ember-views/views/text_field","ember-views/views/text_area","ember-views/views/simple_bound_view","ember-views/views/metamorph_view","ember-views/views/select"],function(e,t,r,n,i,a,s,o,u,l,c,h,m,d,f,p,v,g,b,y,_,w,x,C){"use strict";t["default"].$=r["default"],t["default"].ViewTargetActionSupport=p["default"],t["default"].RenderBuffer=i["default"];var E=t["default"].ViewUtils={};E.isSimpleClick=n.isSimpleClick,E.getViewClientRects=n.getViewClientRects, +E.getViewBoundingClientRect=n.getViewBoundingClientRect,t["default"].CoreView=l.DeprecatedCoreView,t["default"].View=c["default"],t["default"].View.states=u.states,t["default"].View.cloneStates=u.cloneStates,t["default"].View.DOMHelper=s["default"],t["default"].View._Renderer=a["default"],t["default"].Checkbox=g["default"],t["default"].TextField=y["default"],t["default"].TextArea=_["default"],t["default"]._SimpleBoundView=w["default"],t["default"]._MetamorphView=x["default"],t["default"]._Metamorph=x._Metamorph,t["default"].Select=C.Select,t["default"].SelectOption=C.SelectOption,t["default"].SelectOptgroup=C.SelectOptgroup,t["default"].TextSupport=b["default"],t["default"].ComponentLookup=v["default"],t["default"].ContainerView=h["default"],t["default"].CollectionView=m["default"],t["default"].Component=d["default"],t["default"].EventDispatcher=f["default"],e["default"]=t["default"]}),e("ember-views/attr_nodes/attr_node",["exports","ember-metal/core","ember-metal/streams/utils","ember-metal/run_loop"],function(e,t,r,n){"use strict";function i(e,t){this.init(e,t)}e["default"]=i;var a="Binding style attributes may introduce cross-site scripting vulnerabilities; please ensure that values being bound are properly escaped. For more information, including how to disable this warning, see http://emberjs.com/deprecations/v1.x/#toc_binding-style-attributes.";i.prototype.init=function(e,t){this.isAttrNode=!0,this.isView=!0,this.tagName="",this.isVirtual=!0,this.attrName=e,this.attrValue=t,this.isDirty=!0,this.isDestroying=!1,this.lastValue=null,this.hasRenderedInitially=!1,r.subscribe(this.attrValue,this.rerender,this)},i.prototype.renderIfDirty=function(){if(this.isDirty&&!this.isDestroying){var e=r.read(this.attrValue);e!==this.lastValue?this._renderer.renderTree(this,this._parentView):this.isDirty=!1}},i.prototype.render=function(e){if(this.isDirty=!1,!this.isDestroying){var t=r.read(this.attrValue);return"value"!==this.attrName||null!==t&&void 0!==t||(t=""),void 0===t&&(t=null),this.hasRenderedInitially&&"value"===this.attrName&&this._morph.element.value===t?void(this.lastValue=t):void((null!==this.lastValue||null!==t)&&(this._deprecateEscapedStyle(t),this._morph.setContent(t),this.lastValue=t,this.hasRenderedInitially=!0))}},i.prototype._deprecateEscapedStyle=function(e){},i.prototype.rerender=function(){this.isDirty=!0,n["default"].schedule("render",this,this.renderIfDirty)},i.prototype.destroy=function(){this.isDestroying=!0,this.isDirty=!1,r.unsubscribe(this.attrValue,this.rerender,this),!this.removedFromDOM&&this._renderer&&this._renderer.remove(this,!0)},i.prototype.propertyDidChange=function(){},i.prototype._notifyBecameHidden=function(){},i.prototype._notifyBecameVisible=function(){},e.styleWarning=a}),e("ember-views/attr_nodes/legacy_bind",["exports","./attr_node","ember-runtime/system/string","ember-metal/utils","ember-metal/streams/utils","ember-metal/platform/create"],function(e,t,r,n,i,a){"use strict";function s(e,t){this.init(e,t)}s.prototype=a["default"](t["default"].prototype),s.prototype.render=function(e){if(this.isDirty=!1,!this.isDestroying){var t=i.read(this.attrValue);void 0===t&&(t=null),"value"!==this.attrName&&"src"!==this.attrName||null!==t||(t=""),(null!==this.lastValue||null!==t)&&(this._deprecateEscapedStyle(t),this._morph.setContent(t),this.lastValue=t)}},e["default"]=s}),e("ember-views/component_lookup",["exports","ember-runtime/system/object"],function(e,t){"use strict";e["default"]=t["default"].extend({lookupFactory:function(e,t){t=t||this.container;var r="component:"+e,n="template:components/"+e,a=t&&t._registry.has(n);a&&t._registry.injection(r,"layout",n);var s=t.lookupFactory(r);return a||s?(s||(t._registry.register(r,i.Component),s=t.lookupFactory(r)),s):void 0}})}),e("ember-views/mixins/attribute_bindings_support",["exports","ember-metal/mixin","ember-views/attr_nodes/attr_node","ember-metal/properties","ember-views/system/platform","ember-metal/streams/utils","ember-metal/property_set"],function(e,t,r,n,i,a,s){"use strict";var o=[],u=t.Mixin.create({concatenatedProperties:["attributeBindings"],attributeBindings:o,_attrNodes:o,_unspecifiedAttributeBindings:null,_applyAttributeBindings:function(e){var t=this.attributeBindings;if(t&&t.length){var n,s,o,u,l,c,h,m,d=this._unspecifiedAttributeBindings=this._unspecifiedAttributeBindings||{};for(h=0,m=t.length;m>h;h++)n=t[h],s=n.indexOf(":"),-1===s?(o=n,u=n):(o=n.substring(0,s),u=n.substring(s+1)),o in this?(c=this.getStream("view."+o),l=new r["default"](u,c),this.appendAttr(l),i.canSetNameOnInputs||"name"!==u||e.attr("name",a.read(c))):d[o]=u;this.setUnknownProperty=this._setUnknownProperty}},setUnknownProperty:null,_setUnknownProperty:function(e,t){var i=this._unspecifiedAttributeBindings&&this._unspecifiedAttributeBindings[e];if(n.defineProperty(this,e),i){var a=this.getStream("view."+e),o=new r["default"](i,a);this.appendAttr(o)}return s.set(this,e,t)}});e["default"]=u}),e("ember-views/mixins/class_names_support",["exports","ember-metal/core","ember-metal/mixin","ember-runtime/system/native_array","ember-metal/enumerable_utils","ember-metal/streams/utils","ember-views/streams/class_name_binding","ember-metal/utils"],function(e,t,r,n,i,a,s,o){"use strict";var u=[],l=r.Mixin.create({concatenatedProperties:["classNames","classNameBindings"],init:function(){this._super.apply(this,arguments),this.classNameBindings=n.A(this.classNameBindings.slice()),this.classNames=n.A(this.classNames.slice())},classNames:["ember-view"],classNameBindings:u,_applyClassNameBindings:function(){var e=this.classNameBindings;if(e&&e.length){var t,r,n,o=this.classNames;i.forEach(e,function(e){var u;u=a.isStream(e)?e:s.streamifyClassNameBinding(this,e,"_view.");var l,c=this._wrapAsScheduled(function(){t=this.$(),r=a.read(u),l&&(t.removeClass(l),o.removeObject(l)),r?(t.addClass(r),l=r):l=null});n=a.read(u),n&&(i.addObject(o,n),l=n),a.subscribe(u,c,this),this.one("willClearRender",function(){l&&(o.removeObject(l),l=null)})},this)}}});e["default"]=l}),e("ember-views/mixins/component_template_deprecation",["exports","ember-metal/core","ember-metal/property_get","ember-metal/mixin"],function(e,t,r,n){"use strict";e["default"]=n.Mixin.create({willMergeMixin:function(e){this._super.apply(this,arguments);var t,n,i=e.layoutName||e.layout||r.get(this,"layoutName");e.templateName&&!i&&(t="templateName",n="layoutName",e.layoutName=e.templateName,delete e.templateName),e.template&&!i&&(t="template",n="layout",e.layout=e.template,delete e.template)}})}),e("ember-views/mixins/instrumentation_support",["exports","ember-metal/mixin","ember-metal/computed","ember-metal/property_get"],function(e,t,r,n){"use strict";var i=t.Mixin.create({instrumentDisplay:r.computed(function(){return this.helperName?"{{"+this.helperName+"}}":void 0}),instrumentName:"view",instrumentDetails:function(e){e.template=n.get(this,"templateName"),this._super(e)}});e["default"]=i}),e("ember-views/mixins/legacy_view_support",["exports","ember-metal/core","ember-metal/mixin","ember-metal/property_get"],function(e,t,r,n){"use strict";var i=r.Mixin.create({beforeRender:function(e){},afterRender:function(e){},mutateChildViews:function(e){for(var t,r=this._childViews,n=r.length;--n>=0;)t=r[n],e(this,t,n);return this},removeAllChildren:function(){return this.mutateChildViews(function(e,t){e.removeChild(t)})},destroyAllChildren:function(){return this.mutateChildViews(function(e,t){t.destroy()})},nearestChildOf:function(e){for(var t=n.get(this,"parentView");t;){if(n.get(t,"parentView")instanceof e)return t;t=n.get(t,"parentView")}},nearestInstanceOf:function(e){for(var t=n.get(this,"parentView");t;){if(t instanceof e)return t;t=n.get(t,"parentView")}}});e["default"]=i}),e("ember-views/mixins/normalized_rerender_if_needed",["exports","ember-metal/property_get","ember-metal/mixin","ember-metal/merge","ember-views/views/states"],function(e,t,r,n,i){"use strict";var a=i.cloneStates(i.states);n["default"](a._default,{rerenderIfNeeded:function(){return this}}),n["default"](a.inDOM,{rerenderIfNeeded:function(e){e.normalizedValue()!==e._lastNormalizedValue&&e.rerender()}}),e["default"]=r.Mixin.create({_states:a,normalizedValue:function(){var e=this.lazyValue.value(),r=t.get(this,"valueNormalizerFunc");return r?r(e):e},rerenderIfNeeded:function(){this.currentState.rerenderIfNeeded(this)}})}),e("ember-views/mixins/template_rendering_support",["exports","ember-metal/mixin","ember-metal/property_get"],function(e,t,n){"use strict";function i(e,t,n){void 0===a&&(a=r("ember-htmlbars/system/render-view")["default"]),a(e,t,n)}var a,s=t.Mixin.create({render:function(e){var t=n.get(this,"layout")||n.get(this,"template");i(this,e,t)}});e["default"]=s}),e("ember-views/mixins/text_support",["exports","ember-metal/property_get","ember-metal/property_set","ember-metal/mixin","ember-runtime/mixins/target_action_support"],function(e,t,r,n,i){"use strict";function a(e,r,n){var i=t.get(r,e),a=t.get(r,"onEvent"),s=t.get(r,"value");(a===e||"keyPress"===a&&"key-press"===e)&&r.sendAction("action",s),r.sendAction(e,s),(i||a===e)&&(t.get(r,"bubbles")||n.stopPropagation())}var s=n.Mixin.create(i["default"],{value:"",attributeBindings:["autocapitalize","autocorrect","autofocus","disabled","form","maxlength","placeholder","readonly","required","selectionDirection","spellcheck","tabindex","title"],placeholder:null,disabled:!1,maxlength:null,init:function(){this._super.apply(this,arguments),this.on("paste",this,this._elementValueDidChange),this.on("cut",this,this._elementValueDidChange),this.on("input",this,this._elementValueDidChange)},action:null,onEvent:"enter",bubbles:!1,interpretKeyEvents:function(e){var t=s.KEY_EVENTS,r=t[e.keyCode];return this._elementValueDidChange(),r?this[r](e):void 0},_elementValueDidChange:function(){r.set(this,"value",this.$().val())},change:function(e){this._elementValueDidChange(e)},insertNewline:function(e){a("enter",this,e),a("insert-newline",this,e)},cancel:function(e){a("escape-press",this,e)},focusIn:function(e){a("focus-in",this,e)},focusOut:function(e){this._elementValueDidChange(e),a("focus-out",this,e)},keyPress:function(e){a("key-press",this,e)},keyUp:function(e){this.interpretKeyEvents(e),this.sendAction("key-up",t.get(this,"value"),e)},keyDown:function(e){this.sendAction("key-down",t.get(this,"value"),e)}});s.KEY_EVENTS={13:"insertNewline",27:"cancel"},e["default"]=s}),e("ember-views/mixins/view_child_views_support",["exports","ember-metal/core","ember-metal/mixin","ember-metal/computed","ember-metal/property_get","ember-metal/property_set","ember-metal/set_properties","ember-metal/error","ember-metal/enumerable_utils","ember-runtime/system/native_array"],function(e,t,r,n,i,a,s,o,u,l){"use strict";var c=n.computed(function(){var e=this._childViews,t=l.A();return u.forEach(e,function(e){var r;e.isVirtual?(r=i.get(e,"childViews"))&&t.pushObjects(r):t.push(e)}),t.replace=function(e,t,r){throw new o["default"]("childViews is immutable")},t}),h=[],m=r.Mixin.create({childViews:c,_childViews:h,init:function(){this._childViews=this._childViews.slice(),this._super.apply(this,arguments)},appendChild:function(e,t){return this.currentState.appendChild(this,e,t)},removeChild:function(e){if(!this.isDestroying){a.set(e,"_parentView",null);var t=this._childViews;return u.removeObject(t,e),this.propertyDidChange("childViews"),this}},createChildView:function(e,t){if(!e)throw new TypeError("createChildViews first argument must exist");if(e.isView&&e._parentView===this&&e.container===this.container)return e;var r,n=t||{};if(n._parentView=this,n.renderer=this.renderer,e.isViewClass)n.container=this.container,r=e.create(n),r.viewName&&a.set(i.get(this,"concreteView"),r.viewName,r);else if("string"==typeof e){var o="view:"+e,u=this.container.lookupFactory(o);r=u.create(n)}else r=e,n.container=this.container,s["default"](r,n);return r}});e["default"]=m,e.childViewsProperty=c}),e("ember-views/mixins/view_context_support",["exports","ember-metal/mixin","ember-metal/computed","ember-metal/property_get","ember-metal/property_set"],function(e,t,r,n,i){"use strict";var a=t.Mixin.create({context:r.computed({get:function(){return n.get(this,"_context")},set:function(e,t){return i.set(this,"_context",t),t}})["volatile"](),_context:r.computed({get:function(){var e,t;return(t=n.get(this,"controller"))?t:(e=this._parentView,e?n.get(e,"_context"):null)},set:function(e,t){return t}}),_controller:null,controller:r.computed({get:function(){return this._controller?this._controller:this._parentView?n.get(this._parentView,"controller"):null},set:function(e,t){return this._controller=t,t}})});e["default"]=a}),e("ember-views/mixins/view_keyword_support",["exports","ember-metal/mixin","ember-metal/platform/create","ember-views/streams/key_stream"],function(e,t,r,n){"use strict";var i=t.Mixin.create({init:function(){this._super.apply(this,arguments),this._keywords||(this._keywords=r["default"](null)),this._keywords._view=this,this._keywords.view=void 0,this._keywords.controller=new n["default"](this,"controller"),this._setupKeywords()},_setupKeywords:function(){var e=this._keywords,t=this._contextView||this._parentView;if(t){var r=t._keywords;e.view=this.isVirtual?r.view:this;for(var n in r)e[n]||(e[n]=r[n])}else e.view=this.isVirtual?null:this}});e["default"]=i}),e("ember-views/mixins/view_state_support",["exports","ember-metal/core","ember-metal/mixin"],function(e,t,r){"use strict";var n=r.Mixin.create({transitionTo:function(e,t){this._transitionTo(e,t)},_transitionTo:function(e,t){var r=this.currentState,n=this.currentState=this._states[e];this._state=e,r&&r.exit&&r.exit(this),n.enter&&n.enter(this)}});e["default"]=n}),e("ember-views/mixins/view_stream_support",["exports","ember-metal/mixin","ember-metal/streams/stream_binding","ember-views/streams/key_stream","ember-views/streams/context_stream","ember-metal/platform/create","ember-metal/streams/utils"],function(e,t,r,n,i,a,s){"use strict";var o=t.Mixin.create({init:function(){this._baseContext=void 0,this._contextStream=void 0,this._streamBindings=void 0,this._super.apply(this,arguments)},getStream:function(e){var t=this._getContextStream().get(e);return t._label=e,t},_willDestroyElement:function(){this._streamBindings&&this._destroyStreamBindings(),this._contextStream&&this._destroyContextStream()},_getBindingForStream:function(e){void 0===this._streamBindings&&(this._streamBindings=a["default"](null));var t=e;if(s.isStream(e)&&(t=e._label,!t))return e;if(void 0!==this._streamBindings[t])return this._streamBindings[t];var n=this._getContextStream().get(t),i=new r["default"](n);return i._label=t,this._streamBindings[t]=i},_destroyStreamBindings:function(){var e=this._streamBindings;for(var t in e)e[t].destroy();this._streamBindings=void 0},_getContextStream:function(){return void 0===this._contextStream&&(this._baseContext=new n["default"](this,"context"),this._contextStream=new i["default"](this)),this._contextStream},_destroyContextStream:function(){this._baseContext.destroy(),this._baseContext=void 0,this._contextStream.destroy(),this._contextStream=void 0},_unsubscribeFromStreamBindings:function(){for(var e in this._streamBindingSubscriptions){var t=this[e+"Binding"],r=this._streamBindingSubscriptions[e];t.unsubscribe(r)}}});e["default"]=o}),e("ember-views/mixins/view_target_action_support",["exports","ember-metal/mixin","ember-runtime/mixins/target_action_support","ember-metal/alias"],function(e,t,r,n){"use strict";e["default"]=t.Mixin.create(r["default"],{target:n["default"]("controller"),actionContext:n["default"]("context")})}),e("ember-views/mixins/visibility_support",["exports","ember-metal/mixin","ember-metal/property_get","ember-metal/run_loop"],function(e,t,r,n){"use strict";function i(){return this}var a=t.Mixin.create({isVisible:!0,becameVisible:i,becameHidden:i,_isVisibleDidChange:t.observer("isVisible",function(){this._isVisible!==r.get(this,"isVisible")&&n["default"].scheduleOnce("render",this,this._toggleVisibility)}),_toggleVisibility:function(){var e=this.$(),t=r.get(this,"isVisible");this._isVisible!==t&&(this._isVisible=t,e&&(e.toggle(t),this._isAncestorHidden()||(t?this._notifyBecameVisible():this._notifyBecameHidden())))},_notifyBecameVisible:function(){this.trigger("becameVisible"),this.forEachChildView(function(e){var t=r.get(e,"isVisible");(t||null===t)&&e._notifyBecameVisible()})},_notifyBecameHidden:function(){this.trigger("becameHidden"),this.forEachChildView(function(e){var t=r.get(e,"isVisible");(t||null===t)&&e._notifyBecameHidden()})},_isAncestorHidden:function(){for(var e=r.get(this,"parentView");e;){if(r.get(e,"isVisible")===!1)return!0;e=r.get(e,"parentView")}return!1}});e["default"]=a}),e("ember-views/streams/class_name_binding",["exports","ember-metal/streams/utils","ember-metal/property_get","ember-runtime/system/string","ember-metal/utils"],function(e,t,r,n,i){"use strict";function a(e){var t,r,n=e.split(":"),i=n[0],a="";return n.length>1&&(t=n[1],3===n.length&&(r=n[2]),a=":"+t,r&&(a+=":"+r)),{path:i,classNames:a,className:""===t?void 0:t,falsyClassName:r}}function s(e,t,a,s){if(i.isArray(t)&&(t=0!==r.get(t,"length")),a||s)return a&&t?a:s&&!t?s:null;if(t===!0){var o=e.split(".");return n.dasherize(o[o.length-1])}return t!==!1&&null!=t?t:null}function o(e,r,n){n=n||"";var i=a(r);if(""===i.path)return s(i.path,!0,i.className,i.falsyClassName);var o=e.getStream(n+i.path);return t.chain(o,function(){return s(i.path,t.read(o),i.className,i.falsyClassName)})}e.parsePropertyPath=a,e.classStringForValue=s,e.streamifyClassNameBinding=o}),e("ember-views/streams/context_stream",["exports","ember-metal/core","ember-metal/merge","ember-metal/platform/create","ember-metal/path_cache","ember-metal/streams/stream","ember-metal/streams/simple"],function(e,t,r,n,i,a,s){"use strict";function o(e){this.init(),this.view=e}o.prototype=n["default"](a["default"].prototype),r["default"](o.prototype,{value:function(){},_makeChildStream:function(e,r){var n;return""===e||"this"===e?n=this.view._baseContext:i.isGlobal(e)&&t["default"].lookup[e]?(n=new s["default"](t["default"].lookup[e]),n._isGlobal=!0):n=e in this.view._keywords?new s["default"](this.view._keywords[e]):new s["default"](this.view._baseContext.get(e)),n._isRoot=!0,"controller"===e&&(n._isController=!0),n}}),e["default"]=o}),e("ember-views/streams/key_stream",["exports","ember-metal/core","ember-metal/merge","ember-metal/platform/create","ember-metal/property_get","ember-metal/property_set","ember-metal/observer","ember-metal/streams/stream","ember-metal/streams/utils"],function(e,t,r,n,i,a,s,o,u){"use strict";function l(e,t){this.init(),this.source=e,this.obj=void 0,this.key=t,u.isStream(e)&&e.subscribe(this._didChange,this)}l.prototype=n["default"](o["default"].prototype),r["default"](l.prototype,{valueFn:function(){var e=this.obj,t=u.read(this.source);return t!==e&&(e&&"object"==typeof e&&s.removeObserver(e,this.key,this,this._didChange),t&&"object"==typeof t&&s.addObserver(t,this.key,this,this._didChange),this.obj=t),t?i.get(t,this.key):void 0},setValue:function(e){this.obj&&a.set(this.obj,this.key,e)},setSource:function(e){var t=this.source;e!==t&&(u.isStream(t)&&t.unsubscribe(this._didChange,this),u.isStream(e)&&e.subscribe(this._didChange,this),this.source=e,this.notify())},_didChange:function(){this.notify()},_super$destroy:o["default"].prototype.destroy,destroy:function(){return this._super$destroy()?(u.isStream(this.source)&&this.source.unsubscribe(this._didChange,this),this.obj&&"object"==typeof this.obj&&s.removeObserver(this.obj,this.key,this,this._didChange),this.source=void 0,this.obj=void 0,!0):void 0}}),e["default"]=l,o["default"].prototype._makeChildStream=function(e){return new l(this,e)}}),e("ember-views/streams/should_display",["exports","ember-metal/streams/stream","ember-metal/streams/utils","ember-metal/platform/create","ember-metal/property_get","ember-metal/utils"],function(e,t,r,n,i,a){"use strict";function s(e){if(r.isStream(e))return new o(e);var t=e&&i.get(e,"isTruthy");return"boolean"==typeof t?t:a.isArray(e)?0!==i.get(e,"length"):!!e}function o(e){this.init(),this.oldPredicate=void 0,this.predicateStream=e,this.isTruthyStream=e.get("isTruthy"),this.lengthStream=void 0,r.subscribe(this.predicateStream,this.notify,this),r.subscribe(this.isTruthyStream,this.notify,this)}e["default"]=s,o.prototype=n["default"](t["default"].prototype),o.prototype.valueFn=function(){var e=this.oldPredicate,t=r.read(this.predicateStream),n=a.isArray(t);t!==e&&(this.lengthStream&&!n&&(r.unsubscribe(this.lengthStream,this.notify,this),this.lengthStream=void 0),!this.lengthStream&&n&&(this.lengthStream=this.predicateStream.get("length"),r.subscribe(this.lengthStream,this.notify,this)),this.oldPredicate=t);var i=r.read(this.isTruthyStream);if("boolean"==typeof i)return i;if(this.lengthStream){var s=r.read(this.lengthStream);return 0!==s}return!!t}}),e("ember-views/streams/utils",["exports","ember-metal/core","ember-metal/property_get","ember-metal/path_cache","ember-runtime/system/string","ember-metal/streams/utils","ember-views/views/view","ember-runtime/mixins/controller"],function(e,t,r,n,i,a,s,o){"use strict";function u(e,t){var i,s=a.read(e);return i="string"==typeof s?n.isGlobal(s)?r.get(null,s):t.lookupFactory("view:"+s):s}function l(e,t){var r=a.read(e),n=t.lookup("component-lookup:main");return n.lookupFactory(r,t)}function c(e){if(a.isStream(e)){var t=e.value();if(!e._isController)for(;o["default"].detect(t);)t=r.get(t,"model");return t}return e}e.readViewFactory=u,e.readComponentFactory=l,e.readUnwrappedModel=c}),e("ember-views/system/action_manager",["exports"],function(e){"use strict";function t(){}t.registeredActions={},e["default"]=t}),e("ember-views/system/event_dispatcher",["exports","ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/is_none","ember-metal/run_loop","ember-metal/utils","ember-runtime/system/string","ember-runtime/system/object","ember-views/system/jquery","ember-views/system/action_manager","ember-views/views/view","ember-metal/merge"],function(e,t,r,n,i,a,s,o,u,l,c,h,m){"use strict";e["default"]=u["default"].extend({events:{touchstart:"touchStart",touchmove:"touchMove",touchend:"touchEnd",touchcancel:"touchCancel",keydown:"keyDown",keyup:"keyUp",keypress:"keyPress",mousedown:"mouseDown",mouseup:"mouseUp",contextmenu:"contextMenu",click:"click",dblclick:"doubleClick",mousemove:"mouseMove",focusin:"focusIn",focusout:"focusOut",mouseenter:"mouseEnter",mouseleave:"mouseLeave",submit:"submit",input:"input",change:"change",dragstart:"dragStart",drag:"drag",dragenter:"dragEnter",dragleave:"dragLeave",dragover:"dragOver",drop:"drop",dragend:"dragEnd"},rootElement:"body",canDispatchToEventManager:!0,setup:function(e,t){var a,s=r.get(this,"events");m["default"](s,e||{}),i["default"](t)||n.set(this,"rootElement",t),t=l["default"](r.get(this,"rootElement")),t.addClass("ember-application");for(a in s)s.hasOwnProperty(a)&&this.setupHandler(t,a,s[a])},setupHandler:function(e,t,r){var n=this;e.on(t+".ember",".ember-view",function(e,t){var i=h["default"].views[this.id],a=!0,s=n.canDispatchToEventManager?n._findNearestEventManager(i,r):null;return s&&s!==t?a=n._dispatchEvent(s,e,r,i):i&&(a=n._bubbleEvent(i,e,r)),a}),e.on(t+".ember","[data-ember-action]",function(e){var t=l["default"](e.currentTarget).attr("data-ember-action"),n=c["default"].registeredActions[t];return n&&n.eventName===r?n.handler(e):void 0})},_findNearestEventManager:function(e,t){for(var n=null;e&&(n=r.get(e,"eventManager"),!n||!n[t]);)e=r.get(e,"parentView");return n},_dispatchEvent:function(e,t,r,n){var i=!0,o=e[r];return"function"===s.typeOf(o)?(i=a["default"](e,o,t,n),t.stopPropagation()):i=this._bubbleEvent(n,t,r),i},_bubbleEvent:function(e,t,r){return a["default"].join(e,e.handleEvent,r,t)},destroy:function(){var e=r.get(this,"rootElement");return l["default"](e).off(".ember","**").removeClass("ember-application"),this._super.apply(this,arguments)},toString:function(){return"(EventDispatcher)"}})}),e("ember-views/system/ext",["ember-metal/run_loop"],function(e){"use strict";e["default"]._addQueue("render","actions"),e["default"]._addQueue("afterRender","render")}),e("ember-views/system/jquery",["exports","ember-metal/core","ember-metal/enumerable_utils","ember-metal/environment"],function(e,t,n,i){"use strict";var s;if(i["default"].hasDOM&&(s=t["default"].imports&&t["default"].imports.jQuery||a&&a.jQuery,s||"function"!=typeof r||(s=r("jquery")),s)){var o=["dragstart","drag","dragenter","dragleave","dragover","drop","dragend"];n.forEach(o,function(e){s.event.fixHooks[e]={props:["dataTransfer"]}})}e["default"]=s}),e("ember-views/system/lookup_partial",["exports","ember-metal/core"],function(e,t){"use strict";function r(e,t){var r=t.split("/"),n=r[r.length-1];r[r.length-1]="_"+n;var i=r.join("/"),a=e.templateForName(i);return a||(a=e.templateForName(t)),a}e["default"]=r}),e("ember-views/system/platform",["exports","ember-metal/environment"],function(e,t){"use strict";var r=t["default"].hasDOM&&function(){var e=document.createElement("div"),t=document.createElement("input");return t.setAttribute("name","foo"),e.appendChild(t),!!e.innerHTML.match("foo")}();e.canSetNameOnInputs=r}),e("ember-views/system/render_buffer",["exports","ember-views/system/jquery","ember-metal/core","ember-metal/platform/create","dom-helper/prop","ember-views/system/platform"],function(e,t,r,n,i,a){"use strict";function s(e,t,r){if(c=c||{tr:e.createElement("tbody"),col:e.createElement("colgroup")},"TABLE"===r.tagName){var n=h.exec(t);if(n)return c[n[1].toLowerCase()]}}function o(){this.seen=n["default"](null),this.list=[]}function u(e){return e&&m.test(e)?e.replace(d,""):e}function l(e){var t={"<":"<",">":">",'"':""","'":"'","`":"`"},r=function(e){return t[e]||"&"},n=e.toString();return p.test(n)?n.replace(f,r):n}var c,h=/(?:"'`]/g,p=/[&<>"'`]/,v=function(e){this.buffer=null,this.childViews=[],this.attrNodes=[],this.dom=e};v.prototype={reset:function(e,t){this.tagName=e,this.buffer=null,this._element=null,this._outerContextualElement=t,this.elementClasses=null,this.elementId=null,this.elementAttributes=null,this.elementProperties=null,this.elementTag=null,this.elementStyle=null,this.childViews.length=0,this.attrNodes.length=0},_element:null,_outerContextualElement:null,elementClasses:null,classes:null,elementId:null,elementAttributes:null,elementProperties:null,elementTag:null,elementStyle:null,pushChildView:function(e){var t=this.childViews.length;this.childViews[t]=e,this.push("")},pushAttrNode:function(e){var t=this.attrNodes.length;this.attrNodes[t]=e},hydrateMorphs:function(e){for(var t=this.childViews,r=this._element,n=0,i=t.length;i>n;n++){var a=t[n],s=r.querySelector("#morph-"+n),o=s.parentNode;a._morph=this.dom.insertMorphBefore(o,s,1===o.nodeType?o:e),o.removeChild(s)}},push:function(e){return"string"==typeof e?(null===this.buffer&&(this.buffer=""),this.buffer+=e):this.buffer=e,this},addClass:function(e){return this.elementClasses=this.elementClasses||new o,this.elementClasses.add(e),this.classes=this.elementClasses.list,this},setClasses:function(e){this.elementClasses=null;var t,r=e.length;for(t=0;r>t;t++)this.addClass(e[t])},id:function(e){return this.elementId=e,this},attr:function(e,t){var r=this.elementAttributes=this.elementAttributes||{};return 1===arguments.length?r[e]:(r[e]=t,this)},removeAttr:function(e){var t=this.elementAttributes;return t&&delete t[e],this},prop:function(e,t){var r=this.elementProperties=this.elementProperties||{};return 1===arguments.length?r[e]:(r[e]=t,this)},removeProp:function(e){var t=this.elementProperties;return t&&delete t[e],this},style:function(e,t){return this.elementStyle=this.elementStyle||{},this.elementStyle[e]=t,this},generateElement:function(){var e,t,r,n=this.tagName,s=this.elementId,o=this.classes,c=this.elementAttributes,h=this.elementProperties,m=this.elementStyle,d="";r=!a.canSetNameOnInputs&&c&&c.name?"<"+u(n)+' name="'+l(c.name)+'">':n;var f=this.dom.createElement(r,this.outerContextualElement());if(s&&(this.dom.setAttribute(f,"id",s),this.elementId=null),o&&(this.dom.setAttribute(f,"class",o.join(" ")),this.classes=null,this.elementClasses=null),m){for(t in m)d+=t+":"+m[t]+";";this.dom.setAttribute(f,"style",d),this.elementStyle=null}if(c){for(e in c)this.dom.setAttribute(f,e,c[e]);this.elementAttributes=null}if(h){for(t in h){var p=i.normalizeProperty(f,t.toLowerCase())||t;this.dom.setPropertyStrict(f,p,h[t])}this.elementProperties=null}this._element=f},element:function(){if(this._element&&this.attrNodes.length>0){var e,t,r,n;for(e=0,t=this.attrNodes.length;t>e;e++)n=this.attrNodes[e],r=this.dom.createAttrMorph(this._element,n.attrName),n._morph=r}var i=this.innerContent();if(null===i)return this._element;var a=this.innerContextualElement(i);if(this.dom.detectNamespace(a),this._element||(this._element=this.dom.createDocumentFragment()),i.nodeType)this._element.appendChild(i);else{var s=this.dom.parseHTML(i,a);this._element.appendChild(s)}return this.childViews.length>0&&this.hydrateMorphs(a),this._element},string:function(){if(this._element){var e=this.element(),r=e.outerHTML;return"undefined"==typeof r?t["default"]("
        ").append(e).html():r}return this.innerString()},outerContextualElement:function(){return void 0===this._outerContextualElement&&(this.outerContextualElement=document.body),this._outerContextualElement},innerContextualElement:function(e){var t;t=this._element&&1===this._element.nodeType?this._element:this.outerContextualElement();var r;return e&&(r=s(this.dom,e,t)),r||t},innerString:function(){var e=this.innerContent();return e&&!e.nodeType?e:void 0},innerContent:function(){return this.buffer}},e["default"]=v}),e("ember-views/system/renderer",["exports","ember-metal/core","ember-metal-views/renderer","ember-metal/platform/create","ember-views/system/render_buffer","ember-metal/run_loop","ember-metal/property_get","ember-metal/instrumentation"],function(e,t,r,n,i,a,s,o){"use strict";function u(e,t){this._super$constructor(e,t),this.buffer=new i["default"](e)}u.prototype=n["default"](r["default"].prototype),u.prototype.constructor=u,u.prototype._super$constructor=r["default"],u.prototype.scheduleRender=function(e,t){return a["default"].scheduleOnce("render",e,t)},u.prototype.cancelRender=function(e){a["default"].cancel(e)},u.prototype.createElement=function(e,t){var r=e.tagName;null!==r&&"object"==typeof r&&r.isDescriptor&&(r=s.get(e,"tagName"));var n=e.classNameBindings;""===r&&n&&n.length>0;(null===r||void 0===r)&&(r="div");var i=e.buffer=this.buffer;i.reset(r,t),e.beforeRender&&e.beforeRender(i),""!==r&&(e.applyAttributesToBuffer&&e.applyAttributesToBuffer(i),i.generateElement()),e.render&&e.render(i),e.afterRender&&e.afterRender(i);var a=i.element();return e.buffer=null,a&&1===a.nodeType&&(e.element=a),a},u.prototype.destroyView=function(e){e.removedFromDOM=!0,e.destroy()},u.prototype.childViews=function(e){return e._attrNodes&&e._childViews?e._attrNodes.concat(e._childViews):e._attrNodes||e._childViews},r["default"].prototype.willCreateElement=function(e){o.subscribers.length&&e.instrumentDetails&&(e._instrumentEnd=o._instrumentStart("render."+e.instrumentName,function(){var t={};return e.instrumentDetails(t),t})),e._transitionTo&&e._transitionTo("inBuffer")},r["default"].prototype.didCreateElement=function(e){e._transitionTo&&e._transitionTo("hasElement"),e._instrumentEnd&&e._instrumentEnd()},r["default"].prototype.willInsertElement=function(e){this._destinedForDOM&&e.trigger&&e.trigger("willInsertElement")},r["default"].prototype.didInsertElement=function(e){e._transitionTo&&e._transitionTo("inDOM"),this._destinedForDOM&&e.trigger&&e.trigger("didInsertElement")},r["default"].prototype.willRemoveElement=function(e){},r["default"].prototype.willDestroyElement=function(e){ +this._destinedForDOM&&(e._willDestroyElement&&e._willDestroyElement(),e.trigger&&(e.trigger("willDestroyElement"),e.trigger("willClearRender")))},r["default"].prototype.didDestroyElement=function(e){e.element=null,e._transitionTo&&e._transitionTo("preRender")},e["default"]=u}),e("ember-views/system/utils",["exports"],function(e){"use strict";function t(e){var t=e.shiftKey||e.metaKey||e.altKey||e.ctrlKey,r=e.which>1;return!t&&!r}function r(e){var t=document.createRange();return t.setStartBefore(e._morph.firstNode),t.setEndAfter(e._morph.lastNode),t}function n(e){var t=r(e);return t.getClientRects()}function i(e){var t=r(e);return t.getBoundingClientRect()}e.isSimpleClick=t,e.getViewClientRects=n,e.getViewBoundingClientRect=i}),e("ember-views/views/bound_component_view",["exports","ember-views/views/metamorph_view","ember-metal/streams/utils","ember-views/streams/utils","ember-htmlbars/system/merge-view-bindings","ember-metal/error","ember-views/views/container_view","ember-views/views/view"],function(e,t,r,n,i,a,s,o){"use strict";e["default"]=s["default"].extend(t._Metamorph,{init:function(){this._super.apply(this,arguments),this.componentNameStream=this._boundComponentOptions.componentNameStream,r.subscribe(this.componentNameStream,this._updateBoundChildComponent,this),this._updateBoundChildComponent()},willDestroy:function(){r.unsubscribe(this.componentNameStream,this._updateBoundChildComponent,this),this._super.apply(this,arguments)},_updateBoundChildComponent:function(){this.replace(0,1,[this._createNewComponent()])},_createNewComponent:function(){var e=r.read(this.componentNameStream);if(!e)return this.createChildView(o["default"]);var t=n.readComponentFactory(e,this.container);if(!t)throw new a["default"]('HTMLBars error: Could not find component named "'+r.read(this._boundComponentOptions.componentNameStream)+'".');var s,u=this._boundComponentOptions,l={};for(s in u)"_boundComponentOptions"!==s&&"componentNameStream"!==s&&(l[s]=u[s]);var c={};return i["default"](this,c,l),this.createChildView(t,c)}})}),e("ember-views/views/bound_if_view",["exports","ember-metal/run_loop","ember-views/views/metamorph_view","ember-views/mixins/normalized_rerender_if_needed","ember-htmlbars/system/render-view"],function(e,t,r,n,i){"use strict";e["default"]=r["default"].extend(n["default"],{init:function(){this._super.apply(this,arguments);var e=this;this.conditionStream.subscribe(this._wrapAsScheduled(function(){t["default"].scheduleOnce("render",e,"rerenderIfNeeded")}))},normalizedValue:function(){return this.conditionStream.value()},render:function(e){var t=this.conditionStream.value();this._lastNormalizedValue=t;var r=t?this.truthyTemplate:this.falsyTemplate;i["default"](this,e,r)}})}),e("ember-views/views/bound_partial_view",["exports","ember-views/views/metamorph_view","ember-views/mixins/normalized_rerender_if_needed","ember-views/system/lookup_partial","ember-metal/run_loop","ember-htmlbars/system/render-view","ember-htmlbars/templates/empty"],function(e,t,r,n,i,a,s){"use strict";e["default"]=t["default"].extend(r["default"],{init:function(){this._super.apply(this,arguments);var e=this;this.templateNameStream.subscribe(this._wrapAsScheduled(function(){i["default"].scheduleOnce("render",e,"rerenderIfNeeded")}))},normalizedValue:function(){return this.templateNameStream.value()},render:function(e){var t=this.normalizedValue();this._lastNormalizedValue=t;var r;t&&(r=n["default"](this,t)),a["default"](this,e,r||s["default"])}})}),e("ember-views/views/checkbox",["exports","ember-metal/property_get","ember-metal/property_set","ember-views/views/view"],function(e,t,r,n){"use strict";e["default"]=n["default"].extend({instrumentDisplay:'{{input type="checkbox"}}',classNames:["ember-checkbox"],tagName:"input",attributeBindings:["type","checked","indeterminate","disabled","tabindex","name","autofocus","required","form"],type:"checkbox",checked:!1,disabled:!1,indeterminate:!1,init:function(){this._super.apply(this,arguments),this.on("change",this,this._updateElementValue)},didInsertElement:function(){this._super.apply(this,arguments),t.get(this,"element").indeterminate=!!t.get(this,"indeterminate")},_updateElementValue:function(){r.set(this,"checked",this.$().prop("checked"))}})}),e("ember-views/views/collection_view",["exports","ember-metal/core","ember-metal/binding","ember-metal/property_get","ember-metal/property_set","ember-runtime/system/string","ember-views/views/container_view","ember-views/views/core_view","ember-views/views/view","ember-metal/mixin","ember-views/streams/utils","ember-runtime/mixins/array"],function(e,t,r,n,i,a,s,o,u,l,c,h){"use strict";var m=s["default"].extend({content:null,emptyViewClass:u["default"],emptyView:null,itemViewClass:u["default"],init:function(){var e=this._super.apply(this,arguments);return this._contentDidChange(),e},_contentWillChange:l.beforeObserver("content",function(){var e=this.get("content");e&&e.removeArrayObserver(this);var t=e?n.get(e,"length"):0;this.arrayWillChange(e,0,t)}),_contentDidChange:l.observer("content",function(){var e=n.get(this,"content");e&&(this._assertArrayLike(e),e.addArrayObserver(this));var t=e?n.get(e,"length"):0;this.arrayDidChange(e,0,null,t)}),_assertArrayLike:function(e){},destroy:function(){if(this._super.apply(this,arguments)){var e=n.get(this,"content");return e&&e.removeArrayObserver(this),this._createdEmptyView&&this._createdEmptyView.destroy(),this}},arrayWillChange:function(e,t,r){var i=n.get(this,"emptyView");i&&i instanceof u["default"]&&i.removeFromParent();var a,s,o=this._childViews;for(s=t+r-1;s>=t;s--)a=o[s],a.destroy()},arrayDidChange:function(e,t,a,s){var u,l,h,m,d,f,p,v=[];if(m=e?n.get(e,"length"):0){for(p=this._itemViewProps||{},d=n.get(this,"itemViewClass"),d=c.readViewFactory(d,this.container),h=t;t+s>h;h++)l=e.objectAt(h),p._context=this.keyword?this.get("context"):l,p.content=l,p.contentIndex=h,u=this.createChildView(d,p),this.blockParams>0&&(u._blockArguments=[l]),this.blockParams>1&&u._blockArguments.push(u.getStream("_view.contentIndex")),v.push(u);if(this.replace(t,0,v),this.blockParams>1){var g=this._childViews;for(h=t+s;m>h;h++)u=g[h],i.set(u,"contentIndex",h)}}else{if(f=n.get(this,"emptyView"),!f)return;"string"==typeof f&&r.isGlobalPath(f)&&(f=n.get(f)||f),f=this.createChildView(f),v.push(f),i.set(this,"emptyView",f),o["default"].detect(f)&&(this._createdEmptyView=f),this.replace(t,0,v)}},createChildView:function(e,t){var r=this._super(e,t),a=n.get(r,"tagName");return(null===a||void 0===a)&&(a=m.CONTAINER_MAP[n.get(this,"tagName")],i.set(r,"tagName",a)),r}});m.CONTAINER_MAP={ul:"li",ol:"li",table:"tr",thead:"tr",tbody:"tr",tfoot:"tr",tr:"td",select:"option"},e["default"]=m}),e("ember-views/views/component",["exports","ember-metal/core","ember-views/mixins/component_template_deprecation","ember-runtime/mixins/target_action_support","ember-views/views/view","ember-metal/property_get","ember-metal/property_set","ember-metal/is_none","ember-metal/computed","ember-metal/computed_macros","ember-htmlbars/templates/component"],function(e,t,r,n,i,a,s,o,u,l,c){"use strict";var h=i["default"].extend(n["default"],r["default"],{controller:null,context:null,instrumentName:"component",instrumentDisplay:u.computed(function(){return this._debugContainerKey?"{{"+this._debugContainerKey.split(":")[1]+"}}":void 0}),init:function(){this._super.apply(this,arguments),this._keywords.view=this,s.set(this,"context",this),s.set(this,"controller",this)},defaultLayout:c["default"],template:u.computed("templateName",{get:function(){var e=a.get(this,"templateName"),t=this.templateForName(e,"template");return t||a.get(this,"defaultTemplate")},set:function(e,t){return t}}),templateName:null,_setupKeywords:function(){},_yield:function(e,t,r,n){var s=t.data.view,o=this._parentView,u=a.get(this,"template");u&&s.appendChild(i["default"],{isVirtual:!0,tagName:"",template:u,_blockArguments:n,_contextView:o,_morph:r,context:a.get(o,"context"),controller:a.get(o,"controller")})},targetObject:u.computed("_parentView",function(e){var t=this._parentView;return t?a.get(t,"controller"):null}),sendAction:function(e){for(var t=arguments.length,r=Array(t>1?t-1:0),n=1;t>n;n++)r[n-1]=arguments[n];var i;i=void 0===e?a.get(this,"action"):a.get(this,e),void 0!==i&&this.triggerAction({action:i,actionContext:r})},send:function(e){for(var r=arguments.length,n=Array(r>1?r-1:0),i=1;r>i;i++)n[i-1]=arguments[i];var s,o=this._actions&&this._actions[e];if(o){var u=this._actions[e].apply(this,n)===!0;if(!u)return}if(s=a.get(this,"target"))s.send.apply(s,arguments);else if(!o)throw new Error(t["default"].inspect(this)+" had no action handler for: "+e)}});e["default"]=h}),e("ember-views/views/container_view",["exports","ember-metal/core","ember-metal/merge","ember-runtime/mixins/mutable_array","ember-metal/property_get","ember-metal/property_set","ember-views/views/view","ember-views/views/states","ember-metal/error","ember-metal/enumerable_utils","ember-metal/computed","ember-metal/run_loop","ember-metal/properties","ember-metal/mixin","ember-runtime/system/native_array"],function(e,t,r,n,i,a,s,o,u,l,c,h,m,d,f){"use strict";function p(){return this}var v=o.cloneStates(o.states),g=s["default"].extend(n["default"],{_states:v,willWatchProperty:function(e){},init:function(){this._super.apply(this,arguments);var e=i.get(this,"childViews");m.defineProperty(this,"childViews",s["default"].childViewsProperty);var t=this._childViews;l.forEach(e,function(e,r){var n;"string"==typeof e?(n=i.get(this,e),n=this.createChildView(n),a.set(this,e,n)):n=this.createChildView(e),t[r]=n},this);var r=i.get(this,"currentView");r&&(t.length||(t=this._childViews=this._childViews.slice()),t.push(this.createChildView(r)))},replace:function(e,t,r){var n=r?i.get(r,"length"):0;if(this.arrayContentWillChange(e,t,n),this.childViewsWillChange(this._childViews,e,t),0===n)this._childViews.splice(e,t);else{var a=[e,t].concat(r);r.length&&!this._childViews.length&&(this._childViews=this._childViews.slice()),this._childViews.splice.apply(this._childViews,a)}return this.arrayContentDidChange(e,t,n),this.childViewsDidChange(this._childViews,e,t,n),this},objectAt:function(e){return this._childViews[e]},length:c.computed(function(){return this._childViews.length})["volatile"](),render:function(e){var t=e.element(),r=e.dom;return""===this.tagName?(t=r.createDocumentFragment(),e._element=t,this._childViewsMorph=r.appendMorph(t,this._morph.contextualElement)):this._childViewsMorph=r.appendMorph(t),t},instrumentName:"container",childViewsWillChange:function(e,t,r){if(this.propertyWillChange("childViews"),r>0){var n=e.slice(t,t+r);this.currentState.childViewsWillChange(this,e,t,r),this.initializeViews(n,null,null)}},removeChild:function(e){return this.removeObject(e),this},childViewsDidChange:function(e,t,r,n){if(n>0){var i=e.slice(t,t+n);this.initializeViews(i,this),this.currentState.childViewsDidChange(this,e,t,n)}this.propertyDidChange("childViews")},initializeViews:function(e,t){l.forEach(e,function(e){a.set(e,"_parentView",t),!e.container&&t&&a.set(e,"container",t.container)})},currentView:null,_currentViewWillChange:d.beforeObserver("currentView",function(){var e=i.get(this,"currentView");e&&e.destroy()}),_currentViewDidChange:d.observer("currentView",function(){var e=i.get(this,"currentView");e&&this.pushObject(e)}),_ensureChildrenAreInDOM:function(){this.currentState.ensureChildrenAreInDOM(this)}});r["default"](v._default,{childViewsWillChange:p,childViewsDidChange:p,ensureChildrenAreInDOM:p}),r["default"](v.inBuffer,{childViewsDidChange:function(e,t,r,n){throw new u["default"]("You cannot modify child views while in the inBuffer state")}}),r["default"](v.hasElement,{childViewsWillChange:function(e,t,r,n){for(var i=r;r+n>i;i++){var a=t[i];a._unsubscribeFromStreamBindings(),a.remove()}},childViewsDidChange:function(e,t,r,n){h["default"].scheduleOnce("render",e,"_ensureChildrenAreInDOM")},ensureChildrenAreInDOM:function(e){for(var t=e._childViews,r=e._renderer,n=null,i=t.length-1;i>=0;i--){var a=t[i];a._elementCreated||r.renderTree(a,e,n),n=a._morph}}}),e["default"]=g}),e("ember-views/views/core_view",["exports","ember-views/system/renderer","dom-helper","ember-views/views/states","ember-runtime/system/object","ember-runtime/mixins/evented","ember-runtime/mixins/action_handler","ember-metal/property_get","ember-metal/computed","ember-metal/utils"],function(e,t,r,n,i,a,s,o,u,l){"use strict";function c(){return this}var h,m=i["default"].extend(a["default"],s["default"],{isView:!0,isVirtual:!1,_states:n.cloneStates(n.states),init:function(){this._super.apply(this,arguments),this._state="preRender",this.currentState=this._states.preRender,this._isVisible=o.get(this,"isVisible"),this.renderer||(h=h||new t["default"](new r["default"]),this.renderer=h)},parentView:u.computed("_parentView",function(){var e=this._parentView;return e&&e.isVirtual?o.get(e,"parentView"):e}),_state:null,_parentView:null,concreteView:u.computed("parentView",function(){return this.isVirtual?o.get(this,"parentView.concreteView"):this}),instrumentName:"core_view",instrumentDetails:function(e){e.object=this.toString(),e.containerKey=this._debugContainerKey,e.view=this},trigger:function(){this._super.apply(this,arguments);var e=arguments[0],t=this[e];if(t){for(var r=arguments.length,n=new Array(r-1),i=1;r>i;i++)n[i-1]=arguments[i];return t.apply(this,n)}},has:function(e){return"function"===l.typeOf(this[e])||this._super(e)},destroy:function(){var e=this._parentView;if(this._super.apply(this,arguments))return!this.removedFromDOM&&this._renderer&&this._renderer.remove(this,!0),e&&e.removeChild(this),this._transitionTo("destroying",!1),this},clearRenderedChildren:c,_transitionTo:c,destroyElement:c});m.reopenClass({isViewClass:!0});var d=m.extend({init:function(){this._super.apply(this,arguments)}});e["default"]=m,e.DeprecatedCoreView=d}),e("ember-views/views/each",["exports","ember-metal/core","ember-runtime/system/string","ember-metal/property_get","ember-metal/property_set","ember-views/views/collection_view","ember-metal/binding","ember-runtime/mixins/controller","ember-runtime/controllers/array_controller","ember-runtime/mixins/array","ember-metal/observer","ember-views/views/metamorph_view"],function(e,t,r,n,i,a,s,o,u,l,c,h){"use strict";e["default"]=a["default"].extend(h._Metamorph,{init:function(){var e,t=n.get(this,"itemController");if(t){var r=n.get(this,"controller.container").lookupFactory("controller:array").create({_isVirtual:!0,parentController:n.get(this,"controller"),itemController:t,target:n.get(this,"controller"),_eachView:this});this.disableContentObservers(function(){i.set(this,"content",r),e=new s.Binding("content","_eachView.dataSource").oneWay(),e.connect(r)}),this._arrayController=r}else this.disableContentObservers(function(){e=new s.Binding("content","dataSource").oneWay(),e.connect(this)});return this._super.apply(this,arguments)},_assertArrayLike:function(e){},disableContentObservers:function(e){c.removeBeforeObserver(this,"content",null,"_contentWillChange"),c.removeObserver(this,"content",null,"_contentDidChange"),e.call(this),c.addBeforeObserver(this,"content",null,"_contentWillChange"),c.addObserver(this,"content",null,"_contentDidChange")},itemViewClass:h["default"],emptyViewClass:h["default"],createChildView:function(e,t){var r=this._super(e,t),a=n.get(r,"content"),s=n.get(this,"keyword");return s&&(r._keywords[s]=a),a&&a.isController&&i.set(r,"controller",a),r},destroy:function(){return this._super.apply(this,arguments)?(this._arrayController&&this._arrayController.destroy(),this):void 0}})}),e("ember-views/views/metamorph_view",["exports","ember-metal/core","ember-views/views/view","ember-metal/mixin"],function(e,t,r,n){"use strict";var i=n.Mixin.create({isVirtual:!0,tagName:"",instrumentName:"metamorph",init:function(){this._super.apply(this,arguments)}});e["default"]=r["default"].extend(i),e._Metamorph=i}),e("ember-views/views/select",["exports","ember-metal/enumerable_utils","ember-metal/property_get","ember-metal/property_set","ember-views/views/view","ember-views/views/collection_view","ember-metal/utils","ember-metal/is_none","ember-metal/computed","ember-runtime/system/native_array","ember-metal/mixin","ember-metal/properties","ember-htmlbars/templates/select","ember-htmlbars/templates/select-option"],function(e,t,r,n,i,a,s,o,u,l,c,h,m,d){"use strict";var f=m["default"],p=i["default"].extend({instrumentDisplay:"Ember.SelectOption",tagName:"option",attributeBindings:["value","selected"],defaultTemplate:d["default"],init:function(){this.labelPathDidChange(),this.valuePathDidChange(),this._super.apply(this,arguments)},selected:u.computed(function(){var e=r.get(this,"value"),n=r.get(this,"parentView.selection");return r.get(this,"parentView.multiple")?n&&t.indexOf(n,e)>-1:e===r.get(this,"parentView.value")}).property("content","parentView.selection"),labelPathDidChange:c.observer("parentView.optionLabelPath",function(){var e=r.get(this,"parentView.optionLabelPath");h.defineProperty(this,"label",u.computed.alias(e))}),valuePathDidChange:c.observer("parentView.optionValuePath",function(){var e=r.get(this,"parentView.optionValuePath");h.defineProperty(this,"value",u.computed.alias(e))})}),v=a["default"].extend({instrumentDisplay:"Ember.SelectOptgroup",tagName:"optgroup",attributeBindings:["label"],selectionBinding:"parentView.selection",multipleBinding:"parentView.multiple",optionLabelPathBinding:"parentView.optionLabelPath",optionValuePathBinding:"parentView.optionValuePath",itemViewClassBinding:"parentView.optionView"}),g=i["default"].extend({instrumentDisplay:"Ember.Select",tagName:"select",classNames:["ember-select"],defaultTemplate:f,attributeBindings:["autofocus","autocomplete","disabled","form","multiple","name","required","size","tabindex"],multiple:!1,disabled:!1,required:!1,content:null,selection:null,value:u.computed({get:function(e){var t=r.get(this,"_valuePath");return t?r.get(this,"selection."+t):r.get(this,"selection")},set:function(e,t){return t}}).property("_valuePath","selection"),prompt:null,optionLabelPath:"content",optionValuePath:"content",optionGroupPath:null,groupView:v,groupedContent:u.computed(function(){var e=r.get(this,"optionGroupPath"),n=l.A(),i=r.get(this,"content")||[];return t.forEach(i,function(t){var i=r.get(t,e);r.get(n,"lastObject.label")!==i&&n.pushObject({label:i,content:l.A()}),r.get(n,"lastObject.content").push(t)}),n}).property("optionGroupPath","content.@each"),optionView:p,_change:function(){r.get(this,"multiple")?this._changeMultiple():this._changeSingle()},selectionDidChange:c.observer("selection.@each",function(){var e=r.get(this,"selection");if(r.get(this,"multiple")){if(!s.isArray(e))return void n.set(this,"selection",l.A([e]));this._selectionDidChangeMultiple()}else this._selectionDidChangeSingle()}),valueDidChange:c.observer("value",function(){var e,t=r.get(this,"content"),n=r.get(this,"value"),i=r.get(this,"optionValuePath").replace(/^content\.?/,""),a=i?r.get(this,"selection."+i):r.get(this,"selection");n!==a&&(e=t?t.find(function(e){return n===(i?r.get(e,i):e)}):null,this.set("selection",e))}),_setDefaults:function(){var e=r.get(this,"selection"),t=r.get(this,"value");o["default"](e)||this.selectionDidChange(),o["default"](t)||this.valueDidChange(),o["default"](e)&&this._change()},_changeSingle:function(){var e=this.$()[0].selectedIndex,t=r.get(this,"content"),i=r.get(this,"prompt");if(t&&r.get(t,"length")){if(i&&0===e)return void n.set(this,"selection",null);i&&(e-=1),n.set(this,"selection",t.objectAt(e))}},_changeMultiple:function(){var e=this.$("option:selected"),i=r.get(this,"prompt"),a=i?1:0,o=r.get(this,"content"),u=r.get(this,"selection");if(o&&e){var l=e.map(function(){return this.index-a}).toArray(),c=o.objectsAt(l);s.isArray(u)?t.replace(u,0,r.get(u,"length"),c):n.set(this,"selection",c)}},_selectionDidChangeSingle:function(){var e=r.get(this,"value"),t=this;e&&e.then?e.then(function(n){r.get(t,"value")===e&&t._setSelectedIndex(n)}):this._setSelectedIndex(e)},_setSelectedIndex:function(e){var n=r.get(this,"element"),i=r.get(this,"contentValues");if(n){var a=t.indexOf(i,e),s=r.get(this,"prompt");s&&(a+=1),n&&(n.selectedIndex=a)}},_valuePath:u.computed("optionValuePath",function(){var e=r.get(this,"optionValuePath");return e.replace(/^content\.?/,"")}),contentValues:u.computed("content.[]","_valuePath",function(){var e=r.get(this,"_valuePath"),n=r.get(this,"content")||[];return e?t.map(n,function(t){return r.get(t,e)}):t.map(n,function(e){return e})}),_selectionDidChangeMultiple:function(){var e,n=r.get(this,"content"),i=r.get(this,"selection"),a=n?t.indexesOf(n,i):[-1],s=r.get(this,"prompt"),o=s?1:0,u=this.$("option");u&&u.each(function(){e=this.index>-1?this.index-o:-1,this.selected=t.indexOf(a,e)>-1})},init:function(){this._super.apply(this,arguments),this.on("didInsertElement",this,this._setDefaults),this.on("change",this,this._change)}});e["default"]=g,e.Select=g,e.SelectOption=p,e.SelectOptgroup=v}),e("ember-views/views/simple_bound_view",["exports","ember-metal/error","ember-metal/run_loop","ember-metal/utils"],function(e,t,r,n){"use strict";function i(){return this}function a(e,t,r,i){this.stream=i,this[n.GUID_KEY]=n.uuid(),this._lastNormalizedValue=void 0,this.state="preRender",this.updateId=null,this._parentView=e,this.buffer=null,this._morph=r,this.renderer=t}function s(e,t,n){var i=e.appendChild(a,{_morph:t,stream:n});n.subscribe(e._wrapAsScheduled(function(){r["default"].scheduleOnce("render",i,"rerender")}))}e.appendSimpleBoundView=s,a.prototype={isVirtual:!0,isView:!0,tagName:"",destroy:function(){this.updateId&&(r["default"].cancel(this.updateId),this.updateId=null),this._parentView&&this._parentView.removeChild(this),this.morph=null,this.state="destroyed"},propertyWillChange:i,propertyDidChange:i,normalizedValue:function(){var e=this.stream.value();return null===e||void 0===e?"":e},render:function(e){var t=this.normalizedValue();this._lastNormalizedValue=t,e._element=t},rerender:function(){switch(this.state){case"preRender":case"destroyed":break;case"inBuffer":throw new t["default"]("Something you did tried to replace an {{expression}} before it was inserted into the DOM.");case"hasElement":case"inDOM":this.updateId=r["default"].scheduleOnce("render",this,"update")}return this},update:function(){this.updateId=null;var e=this.normalizedValue();e!==this._lastNormalizedValue&&(this._lastNormalizedValue=e,this._morph.setContent(e))},_transitionTo:function(e){this.state=e}},a.create=function(e){return new a(e._parentView,e.renderer,e._morph,e.stream)},a.isViewClass=!0,e["default"]=a}),e("ember-views/views/states",["exports","ember-metal/platform/create","ember-metal/merge","ember-views/views/states/default","ember-views/views/states/pre_render","ember-views/views/states/in_buffer","ember-views/views/states/has_element","ember-views/views/states/in_dom","ember-views/views/states/destroying"],function(e,t,r,n,i,a,s,o,u){"use strict";function l(e){var n={};n._default={},n.preRender=t["default"](n._default),n.destroying=t["default"](n._default),n.inBuffer=t["default"](n._default),n.hasElement=t["default"](n._default),n.inDOM=t["default"](n.hasElement);for(var i in e)e.hasOwnProperty(i)&&r["default"](n[i],e[i]);return n}e.cloneStates=l;var c={_default:n["default"],preRender:i["default"],inDOM:o["default"],inBuffer:a["default"],hasElement:s["default"],destroying:u["default"]};e.states=c}),e("ember-views/views/states/default",["exports","ember-metal/error"],function(e,t){"use strict";function r(){return this}e["default"]={appendChild:function(){throw new t["default"]("You can't use appendChild outside of the rendering process")},$:function(){return void 0},getElement:function(){return null},handleEvent:function(){return!0},destroyElement:function(e){return e._renderer&&e._renderer.remove(e,!1),e},rerender:r,invokeObserver:r}}),e("ember-views/views/states/destroying",["exports","ember-metal/merge","ember-metal/platform/create","ember-runtime/system/string","ember-views/views/states/default","ember-metal/error"],function(e,t,r,n,i,a){"use strict";var s="You can't call %@ on a view being destroyed",o=r["default"](i["default"]);t["default"](o,{appendChild:function(){throw new a["default"](n.fmt(s,["appendChild"]))},rerender:function(){throw new a["default"](n.fmt(s,["rerender"]))},destroyElement:function(){throw new a["default"](n.fmt(s,["destroyElement"]))}}),e["default"]=o}),e("ember-views/views/states/has_element",["exports","ember-views/views/states/default","ember-metal/run_loop","ember-metal/merge","ember-metal/platform/create","ember-views/system/jquery","ember-metal/error","ember-metal/property_get"],function(e,t,r,n,i,a,s,o){"use strict";var u=i["default"](t["default"]);n["default"](u,{$:function(e,t){var r=e.get("concreteView").element;return t?a["default"](t,r):a["default"](r)},getElement:function(e){var t=o.get(e,"parentView");return t&&(t=o.get(t,"element")),t?e.findElementInParentElement(t):a["default"]("#"+o.get(e,"elementId"))[0]},rerender:function(e){if(e._root._morph&&!e._elementInserted)throw new s["default"]("Something you did caused a view to re-render after it rendered but before it was inserted into the DOM.");r["default"].scheduleOnce("render",e,"_rerender")},destroyElement:function(e){return e._renderer.remove(e,!1),e},handleEvent:function(e,t,r){return e.has(t)?e.trigger(t,r):!0},invokeObserver:function(e,t){t.call(e)}}),e["default"]=u}),e("ember-views/views/states/in_buffer",["exports","ember-views/views/states/default","ember-metal/error","ember-views/system/jquery","ember-metal/platform/create","ember-metal/merge"],function(e,t,r,n,i,a){"use strict";var s=i["default"](t["default"]);a["default"](s,{$:function(e,t){return e.rerender(),n["default"]()},rerender:function(e){throw new r["default"]("Something you did caused a view to re-render after it rendered but before it was inserted into the DOM.")},appendChild:function(e,t,r){var n=e.buffer,i=e._childViews;return t=e.createChildView(t,r),i.length||(i=e._childViews=i.slice()),i.push(t),t._morph||n.pushChildView(t),e.propertyDidChange("childViews"),t},appendAttr:function(e,t){var r=e.buffer,n=e._attrNodes;return n.length||(n=e._attrNodes=n.slice()),n.push(t),t._morph||r.pushAttrNode(t),e.propertyDidChange("childViews"),t},invokeObserver:function(e,t){t.call(e)}}),e["default"]=s}),e("ember-views/views/states/in_dom",["exports","ember-metal/platform/create","ember-metal/merge","ember-metal/error","ember-metal/observer","ember-views/views/states/has_element"],function(e,t,r,n,i,a){"use strict";var s=t["default"](a["default"]);r["default"](s,{enter:function(e){e.isVirtual||e._register()},exit:function(e){this.isVirtual||e._unregister()},appendAttr:function(e,t){var r=e._attrNodes;return r.length||(r=e._attrNodes=r.slice()),r.push(t),t._parentView=e,e.renderer.appendAttrTo(t,e.element,t.attrName),e.propertyDidChange("childViews"),t}}),e["default"]=s}),e("ember-views/views/states/pre_render",["exports","ember-views/views/states/default","ember-metal/platform/create"],function(e,t,r){"use strict";var n=r["default"](t["default"]);e["default"]=n}),e("ember-views/views/text_area",["exports","ember-metal/property_get","ember-views/views/component","ember-views/mixins/text_support","ember-metal/mixin"],function(e,t,r,n,i){"use strict";e["default"]=r["default"].extend(n["default"],{instrumentDisplay:"{{textarea}}",classNames:["ember-text-area"],tagName:"textarea",attributeBindings:["rows","cols","name","selectionEnd","selectionStart","wrap","lang","dir"],rows:null,cols:null,_updateElementValue:i.observer("value",function(){var e=t.get(this,"value"),r=this.$();r&&e!==r.val()&&r.val(e)}),init:function(){this._super.apply(this,arguments),this.on("didInsertElement",this,this._updateElementValue)}})}),e("ember-views/views/text_field",["exports","ember-metal/core","ember-metal/computed","ember-metal/environment","ember-metal/platform/create","ember-views/views/component","ember-views/mixins/text_support"],function(e,t,r,n,i,a,s){"use strict";function o(e){if(e in c)return c[e];if(!n["default"].hasDOM)return c[e]=e,e;l||(l=document.createElement("input"));try{l.type=e}catch(t){}return c[e]=l.type===e}function u(){return r.computed({get:function(){return"text"},set:function(e,t){var r="text";return o(t)&&(r=t),r}})}var l,c=i["default"](null);e["default"]=a["default"].extend(s["default"],{instrumentDisplay:'{{input type="text"}}',classNames:["ember-text-field"],tagName:"input",attributeBindings:["accept","autocomplete","autosave","dir","formaction","formenctype","formmethod","formnovalidate","formtarget","height","inputmode","lang","list","max","min","multiple","name","pattern","size","step","type","value","width"],defaultLayout:null,value:"",type:u(),size:null,pattern:null,min:null,max:null})}),e("ember-views/views/view",["exports","ember-metal/core","ember-runtime/mixins/evented","ember-runtime/system/object","ember-metal/error","ember-metal/property_get","ember-metal/run_loop","ember-metal/observer","ember-metal/utils","ember-metal/computed","ember-metal/mixin","ember-metal/deprecate_property","ember-metal/property_events","ember-views/system/jquery","ember-views/system/ext","ember-views/views/core_view","ember-views/mixins/view_stream_support","ember-views/mixins/view_keyword_support","ember-views/mixins/view_context_support","ember-views/mixins/view_child_views_support","ember-views/mixins/view_state_support","ember-views/mixins/template_rendering_support","ember-views/mixins/class_names_support","ember-views/mixins/attribute_bindings_support","ember-views/mixins/legacy_view_support","ember-views/mixins/instrumentation_support","ember-views/mixins/visibility_support"],function(e,t,r,n,i,a,s,o,u,l,c,h,m,d,f,p,v,g,b,y,_,w,x,C,E,O,P){"use strict";function S(){return this}t["default"].TEMPLATES={};var A=[],N=p["default"].extend(v["default"],g["default"],b["default"],y["default"],_["default"],w["default"],x["default"],C["default"],E["default"],O["default"],P["default"],{isView:!0,templateName:null,layoutName:null,template:l.computed("templateName",{get:function(){var e=a.get(this,"templateName"),t=this.templateForName(e,"template");return t||a.get(this,"defaultTemplate")},set:function(e,t){return void 0!==t?t:a.get(this,e)}}),layout:l.computed("layoutName",{get:function(e){var t=a.get(this,"layoutName"),r=this.templateForName(t,"layout");return r||a.get(this,"defaultLayout")},set:function(e,t){return t}}),_yield:function(e,t,r){var n=a.get(this,"template");return n?n.isHTMLBars?n.render(e,t,r.contextualElement):n(e,t):void 0},_blockArguments:A,templateForName:function(e,t){if(e){if(!this.container)throw new i["default"]("Container was not found when looking up a views template. This is most likely due to manually instantiating an Ember.View. See: http://git.io/EKPpnA");return this.container.lookup("template:"+e)}},_contextDidChange:c.observer("context",function(){this.rerender()}),_childViewsWillChange:c.beforeObserver("childViews",function(){if(this.isVirtual){var e=a.get(this,"parentView");e&&m.propertyWillChange(e,"childViews")}}),_childViewsDidChange:c.observer("childViews",function(){if(this.isVirtual){var e=a.get(this,"parentView");e&&m.propertyDidChange(e,"childViews")}}),nearestOfType:function(e){for(var t=a.get(this,"parentView"),r=e instanceof c.Mixin?function(t){return e.detect(t)}:function(t){return e.detect(t.constructor)};t;){if(r(t))return t;t=a.get(t,"parentView")}},nearestWithProperty:function(e){for(var t=a.get(this,"parentView");t;){if(e in t)return t;t=a.get(t,"parentView")}},_parentViewDidChange:c.observer("_parentView",function(){this.isDestroying||(this._setupKeywords(),this.trigger("parentViewDidChange"),a.get(this,"parentView.controller")&&!a.get(this,"controller")&&this.notifyPropertyChange("controller"))}),_controllerDidChange:c.observer("controller",function(){this.isDestroying||(this.rerender(),this.forEachChildView(function(e){e.propertyDidChange("controller")}))}),rerender:function(){return this.currentState.rerender(this)},_rerender:function(){this.isDestroying||this.isDestroyed||this._renderer.renderTree(this,this._parentView); +},_classStringForProperty:function(e){return N._classStringForValue(e.path,e.stream.value(),e.className,e.falsyClassName)},element:null,$:function(e){return this.currentState.$(this,e)},forEachChildView:function(e){var t=this._childViews;if(!t)return this;var r,n,i=t.length;for(n=0;i>n;n++)r=t[n],e(r);return this},appendTo:function(e){var t=d["default"](e);return this.renderer.appendTo(this,t[0]),this},renderToElement:function(e){e=e||"body";var t=this.renderer._dom.createElement(e);return this.renderer.appendTo(this,t),t},replaceIn:function(e){var t=d["default"](e);return this.renderer.replaceIn(this,t[0]),this},append:function(){return this.appendTo(document.body)},remove:function(){this.removedFromDOM||this.destroyElement()},elementId:null,findElementInParentElement:function(e){var t="#"+this.elementId;return d["default"](t)[0]||d["default"](t,e)[0]},createElement:function(){return this.element?this:(this._didCreateElementWithoutMorph=!0,this.renderer.renderTree(this),this)},willInsertElement:S,didInsertElement:S,willClearRender:S,destroyElement:function(){return this.currentState.destroyElement(this)},willDestroyElement:S,parentViewDidChange:S,applyAttributesToBuffer:function(e){this._applyClassNameBindings(),this._applyAttributeBindings(e),e.setClasses(this.classNames),e.id(this.elementId);var t=a.get(this,"ariaRole");t&&e.attr("role",t),a.get(this,"isVisible")===!1&&e.style("display","none")},tagName:null,ariaRole:null,init:function(){this.isVirtual||this.elementId||(this.elementId=u.guidFor(this)),this._super.apply(this,arguments),this._viewRegistry||(this._viewRegistry=N.views)},__defineNonEnumerable:function(e){this[e.name]=e.descriptor.value},appendAttr:function(e){return this.currentState.appendAttr(this,e)},removeFromParent:function(){var e=this._parentView;return this.remove(),e&&e.removeChild(this),this},destroy:function(){var e=a.get(this,"parentView"),t=this.viewName;return this._super.apply(this,arguments)?(t&&e&&e.set(t,null),this):void 0},handleEvent:function(e,t){return this.currentState.handleEvent(this,e,t)},_register:function(){this._viewRegistry[this.elementId]=this},_unregister:function(){delete this._viewRegistry[this.elementId]},registerObserver:function(e,t,r,n){if(n||"function"!=typeof r||(n=r,r=null),e&&"object"==typeof e){var i=this._wrapAsScheduled(n);o.addObserver(e,t,r,i),this.one("willClearRender",function(){o.removeObserver(e,t,r,i)})}},_wrapAsScheduled:function(e){var t=this,r=function(){t.currentState.invokeObserver(this,e)},n=function(){s["default"].scheduleOnce("render",this,r)};return n}});h.deprecateProperty(N.prototype,"state","_state"),h.deprecateProperty(N.prototype,"states","_states");var T=n["default"].extend(r["default"]).create();N.addMutationListener=function(e){T.on("change",e)},N.removeMutationListener=function(e){T.off("change",e)},N.notifyMutationListeners=function(){T.trigger("change")},N.views={},N.childViewsProperty=y.childViewsProperty,e["default"]=N,e.ViewKeywordSupport=g["default"],e.ViewStreamSupport=v["default"],e.ViewContextSupport=b["default"],e.ViewChildViewsSupport=y["default"],e.ViewStateSupport=_["default"],e.TemplateRenderingSupport=w["default"],e.ClassNamesSupport=x["default"],e.AttributeBindingsSupport=C["default"]}),e("ember-views/views/with_view",["exports","ember-metal/property_set","ember-views/views/metamorph_view","ember-views/mixins/normalized_rerender_if_needed","ember-metal/run_loop","ember-htmlbars/system/render-view"],function(e,t,r,n,i,a){"use strict";e["default"]=r["default"].extend(n["default"],{init:function(){this._super.apply(this,arguments);var e=this;this.withValue.subscribe(this._wrapAsScheduled(function(){i["default"].scheduleOnce("render",e,"rerenderIfNeeded")}));var r=this.controllerName;if(r){var n=this.container.lookupFactory("controller:"+r),a=n.create({parentController:this.previousContext,target:this.previousContext});this._generatedController=a,this.preserveContext?(this._blockArguments=[a],this.withValue.subscribe(function(e){t.set(a,"model",e.value())})):t.set(this,"controller",a),t.set(a,"model",this.withValue.value())}else this.preserveContext&&(this._blockArguments=[this.withValue])},normalizedValue:function(){return this.withValue.value()},render:function(e){var r=this.normalizedValue();this._lastNormalizedValue=r,this.preserveContext||this.controllerName||t.set(this,"_context",r);var n=r?this.mainTemplate:this.inverseTemplate;a["default"](this,e,n)},willDestroy:function(){this._super.apply(this,arguments),this._generatedController&&this._generatedController.destroy()}})}),e("ember",["ember-metal","ember-runtime","ember-views","ember-routing","ember-application","ember-extension-support","ember-htmlbars","ember-routing-htmlbars","ember-routing-views","ember-metal/environment","ember-runtime/system/lazy_load"],function(e,r,n,a,s,o,u,l,c,h,m){"use strict";i.__loader.registry["ember-template-compiler"]&&t("ember-template-compiler"),i.__loader.registry["ember-testing"]&&t("ember-testing"),m.runLoadHooks("Ember")}),e("htmlbars-util",["./htmlbars-util/safe-string","./htmlbars-util/handlebars/utils","./htmlbars-util/namespaces","exports"],function(e,t,r,n){"use strict";var i=e["default"],a=t.escapeExpression,s=r.getAttrNamespace;n.SafeString=i,n.escapeExpression=a,n.getAttrNamespace=s}),e("htmlbars-util/array-utils",["exports"],function(e){"use strict";function t(e,t,r){var n,i;if(void 0===r)for(n=0,i=e.length;i>n;n++)t(e[n],n,e);else for(n=0,i=e.length;i>n;n++)t.call(r,e[n],n,e)}function r(e,t){var r,n,i=[];for(r=0,n=e.length;n>r;r++)i.push(t(e[r],r,e));return i}e.forEach=t,e.map=r;var n;n=Array.prototype.indexOf?function(e,t,r){return e.indexOf(t,r)}:function(e,t,r){void 0===r||null===r?r=0:0>r&&(r=Math.max(0,e.length+r));for(var n=r,i=e.length;i>n;n++)if(e[n]===t)return n;return-1};var i=n;e.indexOfArray=i}),e("htmlbars-util/handlebars/safe-string",["exports"],function(e){"use strict";function t(e){this.string=e}t.prototype.toString=t.prototype.toHTML=function(){return""+this.string},e["default"]=t}),e("htmlbars-util/handlebars/utils",["./safe-string","exports"],function(e,t){"use strict";function r(e){return o[e]}function n(e){for(var t=1;t":">",'"':""","'":"'","`":"`"}),u=/[&<>"'`]/g,l=/[&<>"'`]/;t.extend=n;var c=Object.prototype.toString;t.toString=c;var h=function(e){return"function"==typeof e};h(/x/)&&(h=function(e){return"function"==typeof e&&"[object Function]"===c.call(e)});var h;t.isFunction=h;var m=Array.isArray||function(e){return e&&"object"==typeof e?"[object Array]"===c.call(e):!1};t.isArray=m,t.escapeExpression=i,t.isEmpty=a,t.appendContextPath=s}),e("htmlbars-util/namespaces",["exports"],function(e){"use strict";function t(e){var t,n=e.indexOf(":");if(-1!==n){var i=e.slice(0,n);t=r[i]}return t||null}var r={html:"http://www.w3.org/1999/xhtml",mathml:"http://www.w3.org/1998/Math/MathML",svg:"http://www.w3.org/2000/svg",xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace"};e.getAttrNamespace=t}),e("htmlbars-util/object-utils",["exports"],function(e){"use strict";function t(e,t){for(var r in t)e.hasOwnProperty(r)||(e[r]=t[r]);return e}e.merge=t}),e("htmlbars-util/quoting",["exports"],function(e){"use strict";function t(e){return e=e.replace(/\\/g,"\\\\"),e=e.replace(/"/g,'\\"'),e=e.replace(/\n/g,"\\n")}function r(e){return'"'+t(e)+'"'}function n(e){return"["+e+"]"}function i(e){return"{"+e.join(", ")+"}"}function a(e,t){for(var r="";t--;)r+=e;return r}e.escapeString=t,e.string=r,e.array=n,e.hash=i,e.repeat=a}),e("htmlbars-util/safe-string",["./handlebars/safe-string","exports"],function(e,t){"use strict";var r=e["default"];t["default"]=r}),e("morph-attr",["./morph-attr/sanitize-attribute-value","./dom-helper/prop","./dom-helper/build-html-dom","./htmlbars-util","exports"],function(e,t,r,n,i){"use strict";function a(e){this.domHelper.setPropertyStrict(this.element,this.attrName,e)}function s(e){c(e)?this.domHelper.removeAttribute(this.element,this.attrName):this.domHelper.setAttribute(this.element,this.attrName,e)}function o(e){c(e)?this.domHelper.removeAttribute(this.element,this.attrName):this.domHelper.setAttributeNS(this.element,this.namespace,this.attrName,e)}function u(e,t,r,n){this.element=e,this.domHelper=r,this.namespace=void 0!==n?n:d(t),this.escaped=!0;var i=h(this.element,t);this.namespace?(this._update=o,this.attrName=t):e.namespaceURI!==m&&"style"!==t&&i?(this.attrName=i,this._update=a):(this.attrName=t,this._update=s)}var l=e.sanitizeAttributeValue,c=t.isAttrRemovalValue,h=t.normalizeProperty,m=r.svgNamespace,d=n.getAttrNamespace;u.prototype.setContent=function(e){if(this.escaped){var t=l(this.domHelper,this.element,this.attrName,e);this._update(t,this.namespace)}else this._update(e,this.namespace)},i["default"]=u,i.sanitizeAttributeValue=l}),e("morph-attr/sanitize-attribute-value",["exports"],function(e){"use strict";function t(e,t,o,u){var l;if(l=t?t.tagName.toUpperCase():null,u&&u.toHTML)return u.toHTML();if((null===l||n[l])&&a[o]){var c=e.protocolForURL(u);if(r[c]===!0)return"unsafe:"+u}return i[l]&&s[o]?"unsafe:"+u:u}var r={"javascript:":!0,"vbscript:":!0},n={A:!0,BODY:!0,LINK:!0,IMG:!0,IFRAME:!0,BASE:!0},i={EMBED:!0},a={href:!0,src:!0,background:!0};e.badAttributes=a;var s={src:!0};e.sanitizeAttributeValue=t}),e("morph-range",["./morph-range/utils","exports"],function(e,t){"use strict";function r(e,t){this.domHelper=e,this.contextualElement=t,this.parseTextAsHTML=!1,this.firstNode=null,this.lastNode=null,this.parentMorph=null,this.firstChildMorph=null,this.lastChildMorph=null,this.previousMorph=null,this.nextMorph=null}function n(e){for(var t,r=e;(t=r.parentMorph)&&r===t.firstChildMorph&&r.firstNode!==t.firstNode;)t.firstNode=r.firstNode,r=t}function i(e){for(var t,r=e;(t=r.parentMorph)&&r===t.lastChildMorph&&r.lastNode!==t.lastNode;)t.lastNode=r.lastNode,r=t}var a=e.clear,s=e.insertBefore;r.prototype.setContent=function(e){if(null===e||void 0===e)return this.clear();var t=typeof e;switch(t){case"string":return this.parseTextAsHTML?this.setHTML(e):this.setText(e);case"object":if("number"==typeof e.nodeType)return this.setNode(e);if("string"==typeof e.string)return this.setHTML(e.string);if(this.parseTextAsHTML)return this.setHTML(e.toString());case"boolean":case"number":return this.setText(e.toString());default:throw new TypeError("unsupported content")}},r.prototype.clear=function(){return this.setNode(this.domHelper.createComment(""))},r.prototype.setText=function(e){var t=this.firstNode,r=this.lastNode;return t&&r===t&&3===t.nodeType?(t.nodeValue=e,t):this.setNode(e?this.domHelper.createTextNode(e):this.domHelper.createComment(""))},r.prototype.setNode=function(e){var t,r;switch(e.nodeType){case 3:t=e,r=e;break;case 11:t=e.firstChild,r=e.lastChild,null===t&&(t=this.domHelper.createComment(""),e.appendChild(t),r=t);break;default:t=e,r=e}var o=this.firstNode;if(null!==o){var u=o.parentNode;s(u,t,r,o),a(u,o,this.lastNode)}return this.firstNode=t,this.lastNode=r,this.parentMorph&&(n(this),i(this)),e},r.prototype.reset=function(){this.firstChildMorph=null,this.lastChildMorph=null},r.prototype.destroy=function(){var e=this.parentMorph,t=this.previousMorph,r=this.nextMorph,s=this.firstNode,o=this.lastNode,u=s&&s.parentNode;if(t?r?(t.nextMorph=r,r.previousMorph=t):(t.nextMorph=null,e&&(e.lastChildMorph=t)):r?(r.previousMorph=null,e&&(e.firstChildMorph=r)):e&&(e.lastChildMorph=e.firstChildMorph=null),this.parentMorph=null,this.firstNode=null,this.lastNode=null,e){if(!e.firstChildMorph)return void e.clear();n(e.firstChildMorph),i(e.lastChildMorph)}a(u,s,o)},r.prototype.setHTML=function(e){var t=this.domHelper.parseHTML(e,this.contextualElement);return this.setNode(t)},r.prototype.appendContent=function(e){return this.insertContentBeforeMorph(e,null)},r.prototype.insertContentBeforeMorph=function(e,t){var n=new r(this.domHelper,this.contextualElement);return n.setContent(e),this.insertBeforeMorph(n,t),n},r.prototype.appendMorph=function(e){this.insertBeforeMorph(e,null)},r.prototype.insertBeforeMorph=function(e,t){if(t&&t.parentMorph!==this)throw new Error("The morph before which the new morph is to be inserted is not a child of this morph.");e.parentMorph=this;var r=this.firstNode.parentNode;s(r,e.firstNode,e.lastNode,t?t.firstNode:this.lastNode.nextSibling),this.firstChildMorph||a(r,this.firstNode,this.lastNode);var o=t?t.previousMorph:this.lastChildMorph;o?(o.nextMorph=e,e.previousMorph=o):this.firstChildMorph=e,t?(t.previousMorph=e,e.nextMorph=t):this.lastChildMorph=e,n(this.firstChildMorph),i(this.lastChildMorph)},t["default"]=r}),e("morph-range/utils",["exports"],function(e){"use strict";function t(e,t,r){if(e){var n,i=t;do{if(n=i.nextSibling,e.removeChild(i),i===r)break;i=n}while(i)}}function r(e,t,r,n){var i,a=r,s=n;do{if(i=a.previousSibling,e.insertBefore(a,s),a===t)break;s=a,a=i}while(a)}e.clear=t,e.insertBefore=r}),e("route-recognizer",["./route-recognizer/dsl","exports"],function(e,t){"use strict";function r(e){return"[object Array]"===Object.prototype.toString.call(e)}function n(e){this.string=e}function i(e){this.name=e}function a(e){this.name=e}function s(){}function o(e,t,r){"/"===e.charAt(0)&&(e=e.substr(1));for(var o=e.split("/"),u=[],l=0,c=o.length;c>l;l++){var h,m=o[l];(h=m.match(/^:([^\/]+)$/))?(u.push(new i(h[1])),t.push(h[1]),r.dynamics++):(h=m.match(/^\*([^\/]+)$/))?(u.push(new a(h[1])),t.push(h[1]),r.stars++):""===m?u.push(new s):(u.push(new n(m)),r.statics++)}return u}function u(e){this.charSpec=e,this.nextStates=[]}function l(e){return e.sort(function(e,t){if(e.types.stars!==t.types.stars)return e.types.stars-t.types.stars;if(e.types.stars){if(e.types.statics!==t.types.statics)return t.types.statics-e.types.statics;if(e.types.dynamics!==t.types.dynamics)return t.types.dynamics-e.types.dynamics}return e.types.dynamics!==t.types.dynamics?e.types.dynamics-t.types.dynamics:e.types.statics!==t.types.statics?t.types.statics-e.types.statics:0})}function c(e,t){for(var r=[],n=0,i=e.length;i>n;n++){var a=e[n];r=r.concat(a.match(t))}return r}function h(e){this.queryParams=e||{}}function m(e,t,r){for(var n=e.handlers,i=e.regex,a=t.match(i),s=1,o=new h(r),u=0,l=n.length;l>u;u++){for(var c=n[u],m=c.names,d={},f=0,p=m.length;p>f;f++)d[m[f]]=a[s++];o.push({handler:c.handler,params:d,isDynamic:!!m.length})}return o}function d(e,t){return t.eachChar(function(t){e=e.put(t)}),e}function f(e){return e=e.replace(/\+/gm,"%20"),decodeURIComponent(e)}var p=e["default"],v=["/",".","*","+","?","|","(",")","[","]","{","}","\\"],g=new RegExp("(\\"+v.join("|\\")+")","g");n.prototype={eachChar:function(e){for(var t,r=this.string,n=0,i=r.length;i>n;n++)t=r.charAt(n),e({validChars:t})},regex:function(){return this.string.replace(g,"\\$1")},generate:function(){return this.string}},i.prototype={eachChar:function(e){e({invalidChars:"/",repeat:!0})},regex:function(){return"([^/]+)"},generate:function(e){return e[this.name]}},a.prototype={eachChar:function(e){e({invalidChars:"",repeat:!0})},regex:function(){return"(.+)"},generate:function(e){return e[this.name]}},s.prototype={eachChar:function(){},regex:function(){return""},generate:function(){return""}},u.prototype={get:function(e){for(var t=this.nextStates,r=0,n=t.length;n>r;r++){var i=t[r],a=i.charSpec.validChars===e.validChars;if(a=a&&i.charSpec.invalidChars===e.invalidChars)return i}},put:function(e){var t;return(t=this.get(e))?t:(t=new u(e),this.nextStates.push(t),e.repeat&&t.nextStates.push(t),t)},match:function(e){for(var t,r,n,i=this.nextStates,a=[],s=0,o=i.length;o>s;s++)t=i[s],r=t.charSpec,"undefined"!=typeof(n=r.validChars)?-1!==n.indexOf(e)&&a.push(t):"undefined"!=typeof(n=r.invalidChars)&&-1===n.indexOf(e)&&a.push(t);return a}};var b=Object.create||function(e){function t(){}return t.prototype=e,new t};h.prototype=b({splice:Array.prototype.splice,slice:Array.prototype.slice,push:Array.prototype.push,length:0,queryParams:null});var y=function(){this.rootState=new u,this.names={}};y.prototype={add:function(e,t){for(var r,n=this.rootState,i="^",a={statics:0,dynamics:0,stars:0},u=[],l=[],c=!0,h=0,m=e.length;m>h;h++){var f=e[h],p=[],v=o(f.path,p,a);l=l.concat(v);for(var g=0,b=v.length;b>g;g++){var y=v[g];y instanceof s||(c=!1,n=n.put({validChars:"/"}),i+="/",n=d(n,y),i+=y.regex())}var _={handler:f.handler,names:p};u.push(_)}c&&(n=n.put({validChars:"/"}),i+="/"),n.handlers=u,n.regex=new RegExp(i+"$"),n.types=a,(r=t&&t.as)&&(this.names[r]={segments:l,handlers:u})},handlersFor:function(e){var t=this.names[e],r=[];if(!t)throw new Error("There is no route named "+e);for(var n=0,i=t.handlers.length;i>n;n++)r.push(t.handlers[n]);return r},hasRoute:function(e){return!!this.names[e]},generate:function(e,t){var r=this.names[e],n="";if(!r)throw new Error("There is no route named "+e);for(var i=r.segments,a=0,o=i.length;o>a;a++){var u=i[a];u instanceof s||(n+="/",n+=u.generate(t))}return"/"!==n.charAt(0)&&(n="/"+n),t&&t.queryParams&&(n+=this.generateQueryString(t.queryParams,r.handlers)),n},generateQueryString:function(e,t){var n=[],i=[];for(var a in e)e.hasOwnProperty(a)&&i.push(a);i.sort();for(var s=0,o=i.length;o>s;s++){a=i[s];var u=e[a];if(null!=u){var l=encodeURIComponent(a);if(r(u))for(var c=0,h=u.length;h>c;c++){var m=a+"[]="+encodeURIComponent(u[c]);n.push(m)}else l+="="+encodeURIComponent(u),n.push(l)}}return 0===n.length?"":"?"+n.join("&")},parseQueryString:function(e){for(var t=e.split("&"),r={},n=0;n2&&"[]"===s.slice(o-2)&&(u=!0,s=s.slice(0,o-2),r[s]||(r[s]=[])),i=a[1]?f(a[1]):""),u?r[s].push(i):r[s]=i}return r},recognize:function(e){var t,r,n,i,a=[this.rootState],s={},o=!1;if(i=e.indexOf("?"),-1!==i){var u=e.substr(i+1,e.length);e=e.substr(0,i),s=this.parseQueryString(u)}for(e=decodeURI(e),"/"!==e.charAt(0)&&(e="/"+e),t=e.length,t>1&&"/"===e.charAt(t-1)&&(e=e.substr(0,t-1),o=!0),r=0,n=e.length;n>r&&(a=c(a,e.charAt(r)),a.length);r++);var h=[];for(r=0,n=a.length;n>r;r++)a[r].handlers&&h.push(a[r]);a=l(h);var d=h[0];return d&&d.handlers?(o&&"(.+)$"===d.regex.source.slice(-5)&&(e+="/"),m(d,e,s)):void 0}},y.prototype.map=p,y.VERSION="0.1.5",t["default"]=y}),e("route-recognizer/dsl",["exports"],function(e){"use strict";function t(e,t,r){this.path=e,this.matcher=t,this.delegate=r}function r(e){this.routes={},this.children={},this.target=e}function n(e,r,i){return function(a,s){var o=e+a;return s?void s(n(o,r,i)):new t(e+a,r,i)}}function i(e,t,r){for(var n=0,i=0,a=e.length;a>i;i++)n+=e[i].path.length;t=t.substr(n);var s={path:t,handler:r};e.push(s)}function a(e,t,r,n){var s=t.routes;for(var o in s)if(s.hasOwnProperty(o)){var u=e.slice();i(u,o,s[o]),t.children[o]?a(u,t.children[o],r,n):r.call(n,u)}}t.prototype={to:function(e,t){var r=this.delegate;if(r&&r.willAddRoute&&(e=r.willAddRoute(this.matcher.target,e)),this.matcher.add(this.path,e),t){if(0===t.length)throw new Error("You must have an argument in the function passed to `to`");this.matcher.addChild(this.path,e,t,this.delegate)}return this}},r.prototype={add:function(e,t){this.routes[e]=t},addChild:function(e,t,i,a){var s=new r(t);this.children[e]=s;var o=n(e,s,a);a&&a.contextEntered&&a.contextEntered(t,o),i(o)}},e["default"]=function(e,t){var i=new r;e(n("",i,this.delegate)),a([],i,function(e){t?t(this,e):this.add(e)},this)}}),e("router",["./router/router","exports"],function(e,t){"use strict";var r=e["default"];t["default"]=r}),e("router/handler-info",["./utils","rsvp/promise","exports"],function(e,t,r){"use strict";function n(e){var t=e||{};s(this,t),this.initialize(t)}function i(e,t){if(!e^!t)return!1;if(!e)return!0;for(var r in e)if(e.hasOwnProperty(r)&&e[r]!==t[r])return!1;return!0}var a=e.bind,s=e.merge,o=(e.serialize,e.promiseLabel),u=e.applyHook,l=t["default"];n.prototype={name:null,handler:null,params:null,context:null,factory:null,initialize:function(){},log:function(e,t){e.log&&e.log(this.name+": "+t)},promiseLabel:function(e){return o("'"+this.name+"' "+e)},getUnresolved:function(){return this},serialize:function(){return this.params||{}},resolve:function(e,t){var r=a(this,this.checkForAbort,e),n=a(this,this.runBeforeModelHook,t),i=a(this,this.getModel,t),s=a(this,this.runAfterModelHook,t),o=a(this,this.becomeResolved,t);return l.resolve(void 0,this.promiseLabel("Start handler")).then(r,null,this.promiseLabel("Check for abort")).then(n,null,this.promiseLabel("Before model")).then(r,null,this.promiseLabel("Check if aborted during 'beforeModel' hook")).then(i,null,this.promiseLabel("Model")).then(r,null,this.promiseLabel("Check if aborted in 'model' hook")).then(s,null,this.promiseLabel("After model")).then(r,null,this.promiseLabel("Check if aborted in 'afterModel' hook")).then(o,null,this.promiseLabel("Become resolved"))},runBeforeModelHook:function(e){return e.trigger&&e.trigger(!0,"willResolveModel",e,this.handler),this.runSharedModelHook(e,"beforeModel",[])},runAfterModelHook:function(e,t){var r=this.name;return this.stashResolvedModel(e,t),this.runSharedModelHook(e,"afterModel",[t]).then(function(){return e.resolvedModels[r]},null,this.promiseLabel("Ignore fulfillment value and return model value"))},runSharedModelHook:function(e,t,r){this.log(e,"calling "+t+" hook"),this.queryParams&&r.push(this.queryParams),r.push(e);var n=u(this.handler,t,r);return n&&n.isTransition&&(n=null),l.resolve(n,this.promiseLabel("Resolve value returned from one of the model hooks"))},getModel:null,checkForAbort:function(e,t){return l.resolve(e(),this.promiseLabel("Check for abort")).then(function(){return t},null,this.promiseLabel("Ignore fulfillment value and continue"))},stashResolvedModel:function(e,t){e.resolvedModels=e.resolvedModels||{},e.resolvedModels[this.name]=t},becomeResolved:function(e,t){var r=this.serialize(t);return e&&(this.stashResolvedModel(e,t),e.params=e.params||{},e.params[this.name]=r),this.factory("resolved",{context:t,name:this.name,handler:this.handler,params:r})},shouldSupercede:function(e){if(!e)return!0;var t=e.context===this.context;return e.name!==this.name||this.hasOwnProperty("context")&&!t||this.hasOwnProperty("params")&&!i(this.params,e.params)}},r["default"]=n}),e("router/handler-info/factory",["router/handler-info/resolved-handler-info","router/handler-info/unresolved-handler-info-by-object","router/handler-info/unresolved-handler-info-by-param","exports"],function(e,t,r,n){"use strict";function i(e,t){var r=i.klasses[e],n=new r(t||{});return n.factory=i,n}var a=e["default"],s=t["default"],o=r["default"];i.klasses={resolved:a,param:o,object:s},n["default"]=i}),e("router/handler-info/resolved-handler-info",["../handler-info","router/utils","rsvp/promise","exports"],function(e,t,r,n){"use strict";var i=e["default"],a=t.subclass,s=(t.promiseLabel,r["default"]),o=a(i,{resolve:function(e,t){return t&&t.resolvedModels&&(t.resolvedModels[this.name]=this.context),s.resolve(this,this.promiseLabel("Resolve"))},getUnresolved:function(){return this.factory("param",{name:this.name,handler:this.handler,params:this.params})},isResolved:!0});n["default"]=o}),e("router/handler-info/unresolved-handler-info-by-object",["../handler-info","router/utils","rsvp/promise","exports"],function(e,t,r,n){"use strict";var i=e["default"],a=(t.merge,t.subclass),s=(t.promiseLabel,t.isParam),o=r["default"],u=a(i,{getModel:function(e){return this.log(e,this.name+": resolving provided model"),o.resolve(this.context)},initialize:function(e){this.names=e.names||[],this.context=e.context},serialize:function(e){var t=e||this.context,r=this.names,n=this.handler,i={};if(s(t))return i[r[0]]=t,i;if(n.serialize)return n.serialize(t,r);if(1===r.length){var a=r[0];return/_id$/.test(a)?i[a]=t.id:i[a]=t,i}}});n["default"]=u}),e("router/handler-info/unresolved-handler-info-by-param",["../handler-info","router/utils","exports"],function(e,t,r){"use strict";var n=e["default"],i=t.resolveHook,a=t.merge,s=t.subclass,o=(t.promiseLabel,s(n,{initialize:function(e){this.params=e.params||{}},getModel:function(e){var t=this.params;e&&e.queryParams&&(t={},a(t,this.params),t.queryParams=e.queryParams);var r=this.handler,n=i(r,"deserialize")||i(r,"model");return this.runSharedModelHook(e,n,[t])}}));r["default"]=o}),e("router/router",["route-recognizer","rsvp/promise","./utils","./transition-state","./transition","./transition-intent/named-transition-intent","./transition-intent/url-transition-intent","./handler-info","exports"],function(e,t,r,n,i,a,s,o,u){"use strict";function l(e){var t=e||{};this.getHandler=t.getHandler||this.getHandler,this.updateURL=t.updateURL||this.updateURL,this.replaceURL=t.replaceURL||this.replaceURL,this.didTransition=t.didTransition||this.didTransition,this.willTransition=t.willTransition||this.willTransition,this.delegate=t.delegate||this.delegate,this.triggerEvent=t.triggerEvent||this.triggerEvent,this.log=t.log||this.log,this.recognizer=new w,this.reset()}function c(e,t){var r,n=!!this.activeTransition,i=n?this.activeTransition.state:this.state,a=e.applyToState(i,this.recognizer,this.getHandler,t),s=N(i.queryParams,a.queryParams);return b(a.handlerInfos,i.handlerInfos)?s&&(r=this.queryParamsTransition(s,n,i,a))?r:this.activeTransition||new V(this):t?void m(this,a):(r=new V(this,e,a),this.activeTransition&&this.activeTransition.abort(),this.activeTransition=r,r.promise=r.promise.then(function(e){return v(r,e.state)},null,T("Settle transition promise when transition is finalized")),n||_(this,a,r),h(this,a,s),r)}function h(e,t,r){r&&(e._changedQueryParams=r.all,C(e,t.handlerInfos,!0,["queryParamsDidChange",r.changed,r.all,r.removed]),e._changedQueryParams=null)}function m(e,t,r){var n,i,a,s=f(e.state,t);for(n=0,i=s.exited.length;i>n;n++)a=s.exited[n].handler,delete a.context,k(a,"reset",!0,r),k(a,"exit",r);var o=e.oldState=e.state;e.state=t;var u=e.currentHandlerInfos=s.unchanged.slice();try{for(n=0,i=s.reset.length;i>n;n++)a=s.reset[n].handler,k(a,"reset",!1,r);for(n=0,i=s.updatedContext.length;i>n;n++)d(u,s.updatedContext[n],!1,r);for(n=0,i=s.entered.length;i>n;n++)d(u,s.entered[n],!0,r)}catch(l){throw e.state=o,e.currentHandlerInfos=o.handlerInfos,l}e.state.queryParams=y(e,u,t.queryParams,r)}function d(e,t,r,n){var i=t.handler,a=t.context;if(r&&k(i,"enter",n),n&&n.isAborted)throw new R;if(i.context=a,k(i,"contextDidChange"),k(i,"setup",a,n),n&&n.isAborted)throw new R;return e.push(t),!0}function f(e,t){var r,n,i,a=e.handlerInfos,s=t.handlerInfos,o={updatedContext:[],exited:[],entered:[],unchanged:[]},u=!1;for(n=0,i=s.length;i>n;n++){var l=a[n],c=s[n];l&&l.handler===c.handler||(r=!0),r?(o.entered.push(c),l&&o.exited.unshift(l)):u||l.context!==c.context?(u=!0,o.updatedContext.push(c)):o.unchanged.push(l)}for(n=s.length,i=a.length;i>n;n++)o.exited.unshift(a[n]);return o.reset=o.updatedContext.slice(),o.reset.reverse(),o}function p(e,t,r){var n=e.urlMethod;if(n){for(var i=e.router,a=t.handlerInfos,s=a[a.length-1].name,o={},u=a.length-1;u>=0;--u){var l=a[u];S(o,l.params),l.handler.inaccessibleByURL&&(n=null)}if(n){o.queryParams=e._visibleQueryParams||t.queryParams;var c=i.recognizer.generate(s,o);"replace"===n?i.replaceURL(c):i.updateURL(c)}}}function v(e,t){try{E(e.router,e.sequence,"Resolved all models on destination route; finalizing transition.");var r=e.router,n=t.handlerInfos;e.sequence;return m(r,t,e),e.isAborted?(r.state.handlerInfos=r.currentHandlerInfos,x.reject(I(e))):(p(e,t,e.intent.url),e.isActive=!1,r.activeTransition=null,C(r,r.currentHandlerInfos,!0,["didTransition"]),r.didTransition&&r.didTransition(r.currentHandlerInfos),E(r,e.sequence,"TRANSITION COMPLETE."),n[n.length-1].handler)}catch(i){if(!(i instanceof R)){var a=e.state.handlerInfos;e.trigger(!0,"error",i,e,a[a.length-1].handler),e.abort()}throw i}}function g(e,t,r){var n=t[0]||"/",i=t[t.length-1],a={};i&&i.hasOwnProperty("queryParams")&&(a=F.call(t).queryParams);var s;if(0===t.length){E(e,"Updating query params");var o=e.state.handlerInfos;s=new j({name:o[o.length-1].name,contexts:[],queryParams:a})}else"/"===n.charAt(0)?(E(e,"Attempting URL transition to "+n),s=new D({url:n})):(E(e,"Attempting transition to "+n),s=new j({name:t[0],contexts:O.call(t,1),queryParams:a}));return e.transitionByIntent(s,r)}function b(e,t){if(e.length!==t.length)return!1;for(var r=0,n=e.length;n>r;++r)if(e[r]!==t[r])return!1;return!0}function y(e,t,r,n){for(var i in r)r.hasOwnProperty(i)&&null===r[i]&&delete r[i];var a=[];C(e,t,!0,["finalizeQueryParamChange",r,a,n]),n&&(n._visibleQueryParams={});for(var s={},o=0,u=a.length;u>o;++o){var l=a[o];s[l.key]=l.value,n&&l.visible!==!1&&(n._visibleQueryParams[l.key]=l.value)}return s}function _(e,t,r){var n,i,a,s,o,u,l=e.state.handlerInfos,c=[],h=null;for(s=l.length,a=0;s>a;a++){if(o=l[a],u=t.handlerInfos[a],!u||o.name!==u.name){h=a;break}u.isResolved||c.push(o)}null!==h&&(n=l.slice(h,s),i=function(e){for(var t=0,r=n.length;r>t;t++)if(n[t].name===e)return!0;return!1}),C(e,l,!0,["willTransition",r]),e.willTransition&&e.willTransition(l,t.handlerInfos,r)}var w=e["default"],x=t["default"],C=r.trigger,E=r.log,O=r.slice,P=r.forEach,S=r.merge,A=(r.serialize,r.extractQueryParams),N=r.getChangelist,T=r.promiseLabel,k=r.callHook,M=n["default"],I=i.logAbort,V=i.Transition,R=i.TransitionAborted,j=a["default"],D=s["default"],F=(o.ResolvedHandlerInfo,Array.prototype.pop);l.prototype={map:function(e){this.recognizer.delegate=this.delegate,this.recognizer.map(e,function(e,t){for(var r=t.length-1,n=!0;r>=0&&n;--r){var i=t[r];e.add(t,{as:i.handler}),n="/"===i.path||""===i.path||".index"===i.handler.slice(-6)}})},hasRoute:function(e){return this.recognizer.hasRoute(e)},getHandler:function(){},queryParamsTransition:function(e,t,r,n){var i=this;if(h(this,n,e),!t&&this.activeTransition)return this.activeTransition;var a=new V(this);return a.queryParamsOnly=!0,r.queryParams=y(this,n.handlerInfos,n.queryParams,a),a.promise=a.promise.then(function(e){return p(a,r,!0),i.didTransition&&i.didTransition(i.currentHandlerInfos),e},null,T("Transition complete")),a},transitionByIntent:function(e,t){try{return c.apply(this,arguments)}catch(r){return new V(this,e,null,r)}},reset:function(){this.state&&P(this.state.handlerInfos.slice().reverse(),function(e){var t=e.handler;k(t,"exit")}),this.state=new M,this.currentHandlerInfos=null},activeTransition:null,handleURL:function(e){var t=O.call(arguments);return"/"!==e.charAt(0)&&(t[0]="/"+e),g(this,t).method(null)},updateURL:function(){throw new Error("updateURL is not implemented")},replaceURL:function(e){this.updateURL(e)},transitionTo:function(e){return g(this,arguments)},intermediateTransitionTo:function(e){return g(this,arguments,!0)},refresh:function(e){for(var t=this.activeTransition?this.activeTransition.state:this.state,r=t.handlerInfos,n={},i=0,a=r.length;a>i;++i){var s=r[i];n[s.name]=s.params||{}}E(this,"Starting a refresh transition");var o=new j({name:r[r.length-1].name,pivotHandler:e||r[0].handler,contexts:[],queryParams:this._changedQueryParams||t.queryParams||{}});return this.transitionByIntent(o,!1)},replaceWith:function(e){return g(this,arguments).method("replace")},generate:function(e){for(var t=A(O.call(arguments,1)),r=t[0],n=t[1],i=new j({name:e,contexts:r}),a=i.applyToState(this.state,this.recognizer,this.getHandler),s={},o=0,u=a.handlerInfos.length;u>o;++o){var l=a.handlerInfos[o],c=l.serialize();S(s,c)}return s.queryParams=n,this.recognizer.generate(e,s)},applyIntent:function(e,t){var r=new j({name:e,contexts:t}),n=this.activeTransition&&this.activeTransition.state||this.state;return r.applyToState(n,this.recognizer,this.getHandler)},isActiveIntent:function(e,t,r,n){var i,a,s=n||this.state,o=s.handlerInfos;if(!o.length)return!1;var u=o[o.length-1].name,l=this.recognizer.handlersFor(u),c=0;for(a=l.length;a>c&&(i=o[c],i.name!==e);++c);if(c===l.length)return!1; +var h=new M;h.handlerInfos=o.slice(0,c+1),l=l.slice(0,c+1);var m=new j({name:u,contexts:t}),d=m.applyToHandlers(h,l,this.getHandler,u,!0,!0),f=b(d.handlerInfos,h.handlerInfos);if(!r||!f)return f;var p={};S(p,r);var v=s.queryParams;for(var g in v)v.hasOwnProperty(g)&&p.hasOwnProperty(g)&&(p[g]=v[g]);return f&&!N(p,r)},isActive:function(e){var t=A(O.call(arguments,1));return this.isActiveIntent(e,t[0],t[1])},trigger:function(e){var t=O.call(arguments);C(this,this.currentHandlerInfos,!1,t)},log:null},u["default"]=l}),e("router/transition-intent",["./utils","exports"],function(e,t){"use strict";function r(e){this.initialize(e),this.data=this.data||{}}e.merge;r.prototype={initialize:null,applyToState:null},t["default"]=r}),e("router/transition-intent/named-transition-intent",["../transition-intent","../transition-state","../handler-info/factory","../utils","exports"],function(e,t,r,n,i){"use strict";var a=e["default"],s=t["default"],o=r["default"],u=n.isParam,l=n.extractQueryParams,c=n.merge,h=n.subclass;i["default"]=h(a,{name:null,pivotHandler:null,contexts:null,queryParams:null,initialize:function(e){this.name=e.name,this.pivotHandler=e.pivotHandler,this.contexts=e.contexts||[],this.queryParams=e.queryParams},applyToState:function(e,t,r,n){var i=l([this.name].concat(this.contexts)),a=i[0],s=(i[1],t.handlersFor(a[0])),o=s[s.length-1].handler;return this.applyToHandlers(e,s,r,o,n)},applyToHandlers:function(e,t,r,n,i,a){var o,u,l=new s,h=this.contexts.slice(0),m=t.length;if(this.pivotHandler)for(o=0,u=t.length;u>o;++o)if(r(t[o].handler)===this.pivotHandler){m=o;break}!this.pivotHandler;for(o=t.length-1;o>=0;--o){var d=t[o],f=d.handler,p=r(f),v=e.handlerInfos[o],g=null;if(g=d.names.length>0?o>=m?this.createParamHandlerInfo(f,p,d.names,h,v):this.getHandlerInfoForDynamicSegment(f,p,d.names,h,v,n,o):this.createParamHandlerInfo(f,p,d.names,h,v),a){g=g.becomeResolved(null,g.context);var b=v&&v.context;d.names.length>0&&g.context===b&&(g.params=v&&v.params),g.context=b}var y=v;(o>=m||g.shouldSupercede(v))&&(m=Math.min(o,m),y=g),i&&!a&&(y=y.becomeResolved(null,y.context)),l.handlerInfos.unshift(y)}if(h.length>0)throw new Error("More context objects were passed than there are dynamic segments for the route: "+n);return i||this.invalidateChildren(l.handlerInfos,m),c(l.queryParams,this.queryParams||{}),l},invalidateChildren:function(e,t){for(var r=t,n=e.length;n>r;++r){e[r];e[r]=e[r].getUnresolved()}},getHandlerInfoForDynamicSegment:function(e,t,r,n,i,a,s){var l;r.length;if(n.length>0){if(l=n[n.length-1],u(l))return this.createParamHandlerInfo(e,t,r,n,i);n.pop()}else{if(i&&i.name===e)return i;if(!this.preTransitionState)return i;var c=this.preTransitionState.handlerInfos[s];l=c&&c.context}return o("object",{name:e,handler:t,context:l,names:r})},createParamHandlerInfo:function(e,t,r,n,i){for(var a={},s=r.length;s--;){var l=i&&e===i.name&&i.params||{},c=n[n.length-1],h=r[s];if(u(c))a[h]=""+n.pop();else{if(!l.hasOwnProperty(h))throw new Error("You didn't provide enough string/numeric parameters to satisfy all of the dynamic segments for route "+e);a[h]=l[h]}}return o("param",{name:e,handler:t,params:a})}})}),e("router/transition-intent/url-transition-intent",["../transition-intent","../transition-state","../handler-info/factory","../utils","./../unrecognized-url-error","exports"],function(e,t,r,n,i,a){"use strict";var s=e["default"],o=t["default"],u=r["default"],l=(n.oCreate,n.merge),c=n.subclass,h=i["default"];a["default"]=c(s,{url:null,initialize:function(e){this.url=e.url},applyToState:function(e,t,r){var n,i,a=new o,s=t.recognize(this.url);if(!s)throw new h(this.url);var c=!1;for(n=0,i=s.length;i>n;++n){var m=s[n],d=m.handler,f=r(d);if(f.inaccessibleByURL)throw new h(this.url);var p=u("param",{name:d,handler:f,params:m.params}),v=e.handlerInfos[n];c||p.shouldSupercede(v)?(c=!0,a.handlerInfos[n]=p):a.handlerInfos[n]=v}return l(a.queryParams,s.queryParams),a}})}),e("router/transition-state",["./handler-info","./utils","rsvp/promise","exports"],function(e,t,r,n){"use strict";function i(e){this.handlerInfos=[],this.queryParams={},this.params={}}var a=(e.ResolvedHandlerInfo,t.forEach),s=t.promiseLabel,o=t.callHook,u=r["default"];i.prototype={handlerInfos:null,queryParams:null,params:null,promiseLabel:function(e){var t="";return a(this.handlerInfos,function(e){""!==t&&(t+="."),t+=e.name}),s("'"+t+"': "+e)},resolve:function(e,t){function r(){return u.resolve(e(),c.promiseLabel("Check if should continue"))["catch"](function(e){return h=!0,u.reject(e)},c.promiseLabel("Handle abort"))}function n(e){var r=c.handlerInfos,n=t.resolveIndex>=r.length?r.length-1:t.resolveIndex;return u.reject({error:e,handlerWithError:c.handlerInfos[n].handler,wasAborted:h,state:c})}function i(e){var n=c.handlerInfos[t.resolveIndex].isResolved;if(c.handlerInfos[t.resolveIndex++]=e,!n){var i=e.handler;o(i,"redirect",e.context,t)}return r().then(s,null,c.promiseLabel("Resolve handler"))}function s(){if(t.resolveIndex===c.handlerInfos.length)return{error:null,state:c};var e=c.handlerInfos[t.resolveIndex];return e.resolve(r,t).then(i,null,c.promiseLabel("Proceed"))}var l=this.params;a(this.handlerInfos,function(e){l[e.name]=e.params||{}}),t=t||{},t.resolveIndex=0;var c=this,h=!1;return u.resolve(null,this.promiseLabel("Start transition")).then(s,null,this.promiseLabel("Resolve handler"))["catch"](n,this.promiseLabel("Handle error"))}},n["default"]=i}),e("router/transition",["rsvp/promise","./handler-info","./utils","exports"],function(e,t,r,n){"use strict";function i(e,t,r,n){function s(){return u.isAborted?o.reject(void 0,h("Transition aborted - reject")):void 0}var u=this;if(this.state=r||e.state,this.intent=t,this.router=e,this.data=this.intent&&this.intent.data||{},this.resolvedModels={},this.queryParams={},n)return this.promise=o.reject(n),void(this.error=n);if(r){this.params=r.params,this.queryParams=r.queryParams,this.handlerInfos=r.handlerInfos;var l=r.handlerInfos.length;l&&(this.targetName=r.handlerInfos[l-1].name);for(var c=0;l>c;++c){var m=r.handlerInfos[c];if(!m.isResolved)break;this.pivotHandler=m.handler}this.sequence=i.currentSequence++,this.promise=r.resolve(s,this)["catch"](function(e){return e.wasAborted||u.isAborted?o.reject(a(u)):(u.trigger("error",e.error,u,e.handlerWithError),u.abort(),o.reject(e.error))},h("Handle Abort"))}else this.promise=o.resolve(this.state),this.params={}}function a(e){return c(e.router,e.sequence,"detected abort."),new s}function s(e){this.message=e||"TransitionAborted",this.name="TransitionAborted"}var o=e["default"],u=(t.ResolvedHandlerInfo,r.trigger),l=r.slice,c=r.log,h=r.promiseLabel;i.currentSequence=0,i.prototype={targetName:null,urlMethod:"update",intent:null,params:null,pivotHandler:null,resolveIndex:0,handlerInfos:null,resolvedModels:null,isActive:!0,state:null,queryParamsOnly:!1,isTransition:!0,isExiting:function(e){for(var t=this.handlerInfos,r=0,n=t.length;n>r;++r){var i=t[r];if(i.name===e||i.handler===e)return!1}return!0},promise:null,data:null,then:function(e,t,r){return this.promise.then(e,t,r)},"catch":function(e,t){return this.promise["catch"](e,t)},"finally":function(e,t){return this.promise["finally"](e,t)},abort:function(){return this.isAborted?this:(c(this.router,this.sequence,this.targetName+": transition was aborted"),this.intent.preTransitionState=this.router.state,this.isAborted=!0,this.isActive=!1,this.router.activeTransition=null,this)},retry:function(){return this.abort(),this.router.transitionByIntent(this.intent,!1)},method:function(e){return this.urlMethod=e,this},trigger:function(e){var t=l.call(arguments);"boolean"==typeof e?t.shift():e=!1,u(this.router,this.state.handlerInfos.slice(0,this.resolveIndex+1),e,t)},followRedirects:function(){var e=this.router;return this.promise["catch"](function(t){return e.activeTransition?e.activeTransition.followRedirects():o.reject(t)})},toString:function(){return"Transition (sequence "+this.sequence+")"},log:function(e){c(this.router,this.sequence,e)}},i.prototype.send=i.prototype.trigger,n.Transition=i,n.logAbort=a,n.TransitionAborted=s}),e("router/unrecognized-url-error",["./utils","exports"],function(e,t){"use strict";function r(e){this.message=e||"UnrecognizedURLError",this.name="UnrecognizedURLError",Error.call(this)}var n=e.oCreate;r.prototype=n(Error.prototype),t["default"]=r}),e("router/utils",["exports"],function(e){"use strict";function t(e,t){for(var r in t)t.hasOwnProperty(r)&&(e[r]=t[r])}function r(e){var t,r,n=e&&e.length;return n&&n>0&&e[n-1]&&e[n-1].hasOwnProperty("queryParams")?(r=e[n-1].queryParams,t=v.call(e,0,n-1),[t,r]):[e,null]}function n(e){for(var t in e)if("number"==typeof e[t])e[t]=""+e[t];else if(g(e[t]))for(var r=0,n=e[t].length;n>r;r++)e[t][r]=""+e[t][r]}function i(e,t,r){e.log&&(3===arguments.length?e.log("Transition #"+t+": "+r):(r=t,e.log(r)))}function a(e,t){var r=arguments;return function(n){var i=v.call(r,2);return i.push(n),t.apply(e,i)}}function s(e){return"string"==typeof e||e instanceof String||"number"==typeof e||e instanceof Number}function o(e,t){for(var r=0,n=e.length;n>r&&!1!==t(e[r]);r++);}function u(e,t,r,n){if(e.triggerEvent)return void e.triggerEvent(t,r,n);var i=n.shift();if(!t){if(r)return;throw new Error("Could not trigger event '"+i+"'. There are no active handlers")}for(var a=!1,s=t.length-1;s>=0;s--){var o=t[s],u=o.handler;if(u.events&&u.events[i]){if(u.events[i].apply(u,n)!==!0)return;a=!0}}if(!a&&!r)throw new Error("Nothing handled the event '"+i+"'.")}function l(e,r){var i,a={all:{},changed:{},removed:{}};t(a.all,r);var s=!1;n(e),n(r);for(i in e)e.hasOwnProperty(i)&&(r.hasOwnProperty(i)||(s=!0,a.removed[i]=e[i]));for(i in r)if(r.hasOwnProperty(i))if(g(e[i])&&g(r[i]))if(e[i].length!==r[i].length)a.changed[i]=r[i],s=!0;else for(var o=0,u=e[i].length;u>o;o++)e[i][o]!==r[i][o]&&(a.changed[i]=r[i],s=!0);else e[i]!==r[i]&&(a.changed[i]=r[i],s=!0);return s&&a}function c(e){return"Router: "+e}function h(e,r){function n(t){e.call(this,t||{})}return n.prototype=b(e.prototype),t(n.prototype,r),n}function m(e,t){if(e){var r="_"+t;return e[r]&&r||e[t]&&t}}function d(e,t,r,n){var i=m(e,t);return i&&e[i].call(e,r,n)}function f(e,t,r){var n=m(e,t);return n?0===r.length?e[n].call(e):1===r.length?e[n].call(e,r[0]):2===r.length?e[n].call(e,r[0],r[1]):e[n].apply(e,r):void 0}var p,v=Array.prototype.slice;p=Array.isArray?Array.isArray:function(e){return"[object Array]"===Object.prototype.toString.call(e)};var g=p;e.isArray=g;var b=Object.create||function(e){function t(){}return t.prototype=e,new t};e.oCreate=b,e.extractQueryParams=r,e.log=i,e.bind=a,e.forEach=o,e.trigger=u,e.getChangelist=l,e.promiseLabel=c,e.subclass=h,e.merge=t,e.slice=v,e.isParam=s,e.coerceQueryParamsToString=n,e.callHook=d,e.resolveHook=m,e.applyHook=f}),e("rsvp",["./rsvp/promise","./rsvp/events","./rsvp/node","./rsvp/all","./rsvp/all-settled","./rsvp/race","./rsvp/hash","./rsvp/hash-settled","./rsvp/rethrow","./rsvp/defer","./rsvp/config","./rsvp/map","./rsvp/resolve","./rsvp/reject","./rsvp/filter","./rsvp/asap","exports"],function(e,t,r,n,i,a,s,o,u,l,c,h,m,d,f,p,v){"use strict";function g(e,t){T.async(e,t)}function b(){T.on.apply(T,arguments)}function y(){T.off.apply(T,arguments)}var _=e["default"],w=t["default"],x=r["default"],C=n["default"],E=i["default"],O=a["default"],P=s["default"],S=o["default"],A=u["default"],N=l["default"],T=c.config,k=c.configure,M=h["default"],I=m["default"],V=d["default"],R=f["default"],j=p["default"];T.async=j;var D=I;if("undefined"!=typeof window&&"object"==typeof window.__PROMISE_INSTRUMENTATION__){var F=window.__PROMISE_INSTRUMENTATION__;k("instrument",!0);for(var L in F)F.hasOwnProperty(L)&&b(L,F[L])}v.cast=D,v.Promise=_,v.EventTarget=w,v.all=C,v.allSettled=E,v.race=O,v.hash=P,v.hashSettled=S,v.rethrow=A,v.defer=N,v.denodeify=x,v.configure=k,v.on=b,v.off=y,v.resolve=I,v.reject=V,v.async=g,v.map=M,v.filter=R}),e("rsvp.umd",["./rsvp"],function(t){"use strict";var r=t.Promise,n=t.allSettled,i=t.hash,a=t.hashSettled,s=t.denodeify,o=t.on,u=t.off,l=t.map,c=t.filter,h=t.resolve,m=t.reject,d=t.rethrow,f=t.all,p=t.defer,v=t.EventTarget,g=t.configure,b=t.race,y=t.async,_={race:b,Promise:r,allSettled:n,hash:i,hashSettled:a,denodeify:s,on:o,off:u,map:l,filter:c,resolve:h,reject:m,all:f,rethrow:d,defer:p,EventTarget:v,configure:g,async:y};"function"==typeof e&&e.amd?e(function(){return _}):"undefined"!=typeof module&&module.exports?module.exports=_:"undefined"!=typeof this&&(this.RSVP=_)}),e("rsvp/-internal",["./utils","./instrument","./config","exports"],function(e,t,r,n){"use strict";function i(){return new TypeError("A promises callback cannot return that same promise.")}function a(){}function s(e){try{return e.then}catch(t){return A.error=t,A}}function o(e,t,r,n){try{e.call(t,r,n)}catch(i){return i}}function u(e,t,r){E.async(function(e){var n=!1,i=o(r,t,function(r){n||(n=!0,t!==r?h(e,r):d(e,r))},function(t){n||(n=!0,f(e,t))},"Settle: "+(e._label||" unknown promise"));!n&&i&&(n=!0,f(e,i))},e)}function l(e,t){t._state===P?d(e,t._result):e._state===S?f(e,t._result):p(t,void 0,function(r){t!==r?h(e,r):d(e,r)},function(t){f(e,t)})}function c(e,t){if(t.constructor===e.constructor)l(e,t);else{var r=s(t);r===A?f(e,A.error):void 0===r?d(e,t):x(r)?u(e,t,r):d(e,t)}}function h(e,t){e===t?d(e,t):w(t)?c(e,t):d(e,t)}function m(e){e._onerror&&e._onerror(e._result),v(e)}function d(e,t){e._state===O&&(e._result=t,e._state=P,0===e._subscribers.length?E.instrument&&C("fulfilled",e):E.async(v,e))}function f(e,t){e._state===O&&(e._state=S,e._result=t,E.async(m,e))}function p(e,t,r,n){var i=e._subscribers,a=i.length;e._onerror=null,i[a]=t,i[a+P]=r,i[a+S]=n,0===a&&e._state&&E.async(v,e)}function v(e){var t=e._subscribers,r=e._state;if(E.instrument&&C(r===P?"fulfilled":"rejected",e),0!==t.length){for(var n,i,a=e._result,s=0;se;e+=2){var t=p[e],r=p[e+1];t(r),p[e]=void 0,p[e+1]=void 0}l=0}function u(){try{var e=r("vertx");e.runOnLoop||e.runOnContext;return n()}catch(t){return s()}}var l=0;e["default"]=function(e,t){p[l]=e,p[l+1]=t,l+=2,2===l&&c()};var c,h="undefined"!=typeof window?window:void 0,m=h||{},d=m.MutationObserver||m.WebKitMutationObserver,f="undefined"!=typeof Uint8ClampedArray&&"undefined"!=typeof importScripts&&"undefined"!=typeof MessageChannel,p=new Array(1e3);c="undefined"!=typeof process&&"[object process]"==={}.toString.call(process)?t():d?i():f?a():void 0===h&&"function"==typeof r?u():s()}),e("rsvp/config",["./events","exports"],function(e,t){"use strict";function r(e,t){return"onerror"===e?void i.on("error",t):2!==arguments.length?i[e]:void(i[e]=t)}var n=e["default"],i={instrument:!1};n.mixin(i),t.config=i,t.configure=r}),e("rsvp/defer",["./promise","exports"],function(e,t){"use strict";var r=e["default"];t["default"]=function(e){var t={};return t.promise=new r(function(e,r){t.resolve=e,t.reject=r},e),t}}),e("rsvp/enumerator",["./utils","./-internal","exports"],function(e,t,r){"use strict";function n(e,t,r){return e===h?{state:"fulfilled",value:r}:{state:"rejected",reason:r}}function i(e,t,r,n){this._instanceConstructor=e,this.promise=new e(o,n),this._abortOnReject=r,this._validateInput(t)?(this._input=t,this.length=t.length,this._remaining=t.length,this._init(),0===this.length?l(this.promise,this._result):(this.length=this.length||0,this._enumerate(),0===this._remaining&&l(this.promise,this._result))):u(this.promise,this._validationError())}var a=e.isArray,s=e.isMaybeThenable,o=t.noop,u=t.reject,l=t.fulfill,c=t.subscribe,h=t.FULFILLED,m=t.REJECTED,d=t.PENDING;r.makeSettledResult=n,i.prototype._validateInput=function(e){return a(e)},i.prototype._validationError=function(){return new Error("Array Methods must be provided an Array")},i.prototype._init=function(){this._result=new Array(this.length)},r["default"]=i,i.prototype._enumerate=function(){for(var e=this.length,t=this.promise,r=this._input,n=0;t._state===d&&e>n;n++)this._eachEntry(r[n],n)},i.prototype._eachEntry=function(e,t){var r=this._instanceConstructor;s(e)?e.constructor===r&&e._state!==d?(e._onerror=null,this._settledAt(e._state,t,e._result)):this._willSettleAt(r.resolve(e),t):(this._remaining--,this._result[t]=this._makeResult(h,t,e))},i.prototype._settledAt=function(e,t,r){var n=this.promise;n._state===d&&(this._remaining--,this._abortOnReject&&e===m?u(n,r):this._result[t]=this._makeResult(e,t,r)),0===this._remaining&&l(n,this._result)},i.prototype._makeResult=function(e,t,r){return r},i.prototype._willSettleAt=function(e,t){var r=this;c(e,void 0,function(e){r._settledAt(h,t,e)},function(e){r._settledAt(m,t,e)})}}),e("rsvp/events",["exports"],function(e){"use strict";function t(e,t){for(var r=0,n=e.length;n>r;r++)if(e[r]===t)return r;return-1}function r(e){var t=e._promiseCallbacks;return t||(t=e._promiseCallbacks={}),t}e["default"]={mixin:function(e){return e.on=this.on,e.off=this.off,e.trigger=this.trigger,e._promiseCallbacks=void 0,e},on:function(e,n){var i,a=r(this);i=a[e],i||(i=a[e]=[]),-1===t(i,n)&&i.push(n)},off:function(e,n){var i,a,s=r(this);return n?(i=s[e],a=t(i,n),void(-1!==a&&i.splice(a,1))):void(s[e]=[])},trigger:function(e,t){var n,i,a=r(this);if(n=a[e])for(var s=0;so;o++)s[o]=t(e[o]);return n.all(s,r).then(function(t){for(var r=new Array(a),n=0,i=0;a>i;i++)t[i]&&(r[n]=e[i],n++);return r.length=n,r})})}}),e("rsvp/hash-settled",["./promise","./enumerator","./promise-hash","./utils","exports"],function(e,t,r,n,i){"use strict";function a(e,t,r){this._superConstructor(e,t,!1,r)}var s=e["default"],o=t.makeSettledResult,u=r["default"],l=t["default"],c=n.o_create;a.prototype=c(u.prototype),a.prototype._superConstructor=l,a.prototype._makeResult=o,a.prototype._validationError=function(){return new Error("hashSettled must be called with an object")},i["default"]=function(e,t){return new a(s,e,t).promise}}),e("rsvp/hash",["./promise","./promise-hash","exports"],function(e,t,r){"use strict";var n=e["default"],i=t["default"];r["default"]=function(e,t){return new i(n,e,t).promise}}),e("rsvp/instrument",["./config","./utils","exports"],function(e,t,r){"use strict";function n(){setTimeout(function(){for(var e,t=0;to;o++)s[o]=t(e[o]);return n.all(s,r)})}}),e("rsvp/node",["./promise","./-internal","./utils","exports"],function(e,t,r,n){"use strict";function i(){this.value=void 0}function a(e){try{return e.then}catch(t){return b.value=t,b}}function s(e,t,r){try{e.apply(t,r)}catch(n){return b.value=n,b}}function o(e,t){for(var r,n,i={},a=e.length,s=new Array(a),o=0;a>o;o++)s[o]=e[o];for(n=0;nn;n++)r[n-1]=e[n];return r}function l(e,t){return{then:function(r,n){return e.call(t,r,n)}}}function c(e,t,r,n){var i=s(r,n,t);return i===b&&v(e,i.value),e}function h(e,t,r,n){return d.all(t).then(function(t){var i=s(r,n,t);return i===b&&v(e,i.value),e})}function m(e){return e&&"object"==typeof e?e.constructor===d?!0:a(e):!1}var d=e["default"],f=t.noop,p=t.resolve,v=t.reject,g=r.isArray,b=new i,y=new i;n["default"]=function(e,t){var r=function(){for(var r,n=this,i=arguments.length,a=new Array(i+1),s=!1,b=0;i>b;++b){if(r=arguments[b],!s){if(s=m(r),s===y){var _=new d(f);return v(_,y.value),_}s&&s!==!0&&(r=l(s,r))}a[b]=r}var w=new d(f);return a[i]=function(e,r){e?v(w,e):void 0===t?p(w,r):t===!0?p(w,u(arguments)):g(t)?p(w,o(arguments,t)):p(w,r)},s?h(w,a,e,n):c(w,a,e,n)};return r.__proto__=e,r}}),e("rsvp/promise-hash",["./enumerator","./-internal","./utils","exports"],function(e,t,r,n){"use strict";function i(e,t,r){this._superConstructor(e,t,!0,r)}var a=e["default"],s=t.PENDING,o=r.o_create;n["default"]=i,i.prototype=o(a.prototype),i.prototype._superConstructor=a,i.prototype._init=function(){this._result={}},i.prototype._validateInput=function(e){return e&&"object"==typeof e},i.prototype._validationError=function(){return new Error("Promise.hash must be called with an object")},i.prototype._enumerate=function(){var e=this.promise,t=this._input,r=[];for(var n in t)e._state===s&&t.hasOwnProperty(n)&&r.push({position:n,entry:t[n]});var i=r.length;this._remaining=i;for(var a,o=0;e._state===s&&i>o;o++)a=r[o],this._eachEntry(a.entry,a.position)}}),e("rsvp/promise",["./config","./instrument","./utils","./-internal","./promise/all","./promise/race","./promise/resolve","./promise/reject","exports"],function(e,t,r,n,i,a,s,o,u){"use strict";function l(){throw new TypeError("You must pass a resolver function as the first argument to the promise constructor")}function c(){throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.")}function h(e,t){this._id=S++,this._label=t,this._state=void 0,this._result=void 0,this._subscribers=[],m.instrument&&d("created",this),v!==e&&(f(e)||l(),this instanceof h||c(),b(this,e))}var m=e.config,d=t["default"],f=r.isFunction,p=r.now,v=n.noop,g=n.subscribe,b=n.initializePromise,y=n.invokeCallback,_=n.FULFILLED,w=n.REJECTED,x=i["default"],C=a["default"],E=s["default"],O=o["default"],P="rsvp_"+p()+"-",S=0;u["default"]=h,h.cast=E,h.all=x,h.race=C,h.resolve=E,h.reject=O,h.prototype={constructor:h,_guidKey:P,_onerror:function(e){m.trigger("error",e)},then:function(e,t,r){var n=this,i=n._state;if(i===_&&!e||i===w&&!t)return m.instrument&&d("chained",this,this),this;n._onerror=null;var a=new this.constructor(v,r),s=n._result;if(m.instrument&&d("chained",n,a),i){var o=arguments[i-1];m.async(function(){y(i,a,o,s)})}else g(n,a,e,t);return a},"catch":function(e,t){return this.then(null,e,t)},"finally":function(e,t){var r=this.constructor;return this.then(function(t){return r.resolve(e()).then(function(){return t})},function(t){return r.resolve(e()).then(function(){throw t})},t)}}}),e("rsvp/promise/all",["../enumerator","exports"],function(e,t){"use strict";var r=e["default"];t["default"]=function(e,t){return new r(this,e,!0,t).promise}}),e("rsvp/promise/race",["../utils","../-internal","exports"],function(e,t,r){"use strict";var n=e.isArray,i=t.noop,a=t.resolve,s=t.reject,o=t.subscribe,u=t.PENDING;r["default"]=function(e,t){function r(e){a(h,e)}function l(e){s(h,e)}var c=this,h=new c(i,t);if(!n(e))return s(h,new TypeError("You must pass an array to race.")),h;for(var m=e.length,d=0;h._state===u&&m>d;d++)o(c.resolve(e[d]),void 0,r,l);return h}}),e("rsvp/promise/reject",["../-internal","exports"],function(e,t){"use strict";var r=e.noop,n=e.reject;t["default"]=function(e,t){var i=this,a=new i(r,t);return n(a,e),a}}),e("rsvp/promise/resolve",["../-internal","exports"],function(e,t){"use strict";var r=e.noop,n=e.resolve;t["default"]=function(e,t){var i=this;if(e&&"object"==typeof e&&e.constructor===i)return e;var a=new i(r,t);return n(a,e),a}}),e("rsvp/race",["./promise","exports"],function(e,t){"use strict";var r=e["default"];t["default"]=function(e,t){return r.race(e,t)}}),e("rsvp/reject",["./promise","exports"],function(e,t){"use strict";var r=e["default"];t["default"]=function(e,t){return r.reject(e,t)}}),e("rsvp/resolve",["./promise","exports"],function(e,t){"use strict";var r=e["default"];t["default"]=function(e,t){return r.resolve(e,t)}}),e("rsvp/rethrow",["exports"],function(e){"use strict";e["default"]=function(e){throw setTimeout(function(){throw e}),e}}),e("rsvp/utils",["exports"],function(e){"use strict";function t(e){return"function"==typeof e||"object"==typeof e&&null!==e}function r(e){return"function"==typeof e}function n(e){return"object"==typeof e&&null!==e}function i(){}e.objectOrFunction=t,e.isFunction=r,e.isMaybeThenable=n;var a;a=Array.isArray?Array.isArray:function(e){return"[object Array]"===Object.prototype.toString.call(e)};var s=a;e.isArray=s;var o=Date.now||function(){return(new Date).getTime()};e.now=o;var u=Object.create||function(e){if(arguments.length>1)throw new Error("Second argument not supported");if("object"!=typeof e)throw new TypeError("Argument must be an object");return i.prototype=e,new i};e.o_create=u}),t("ember")}(); +//# sourceMappingURL=ember.min.map \ No newline at end of file diff --git a/dejacode/static/js/ember/ember-template-compiler-1.12.1.js b/dejacode/static/js/ember/ember-template-compiler-1.12.1.js new file mode 100644 index 00000000..2e49d243 --- /dev/null +++ b/dejacode/static/js/ember/ember-template-compiler-1.12.1.js @@ -0,0 +1,17092 @@ +/*! + * @overview Ember - JavaScript Application Framework + * @copyright Copyright 2011-2015 Tilde Inc. and contributors + * Portions Copyright 2006-2011 Strobe Inc. + * Portions Copyright 2008-2011 Apple Inc. All rights reserved. + * @license Licensed under MIT license + * See https://raw.github.com/emberjs/ember.js/master/LICENSE + * @version 1.12.1 + */ + +(function() { +var enifed, requireModule, eriuqer, requirejs, Ember; +var mainContext = this; + +(function() { + + Ember = this.Ember = this.Ember || {}; + if (typeof Ember === 'undefined') { Ember = {}; }; + + if (typeof Ember.__loader === 'undefined') { + var registry = {}; + var seen = {}; + + enifed = function(name, deps, callback) { + var value = { }; + + if (!callback) { + value.deps = []; + value.callback = deps; + } else { + value.deps = deps; + value.callback = callback; + } + + registry[name] = value; + }; + + requirejs = eriuqer = requireModule = function(name) { + return internalRequire(name, null); + } + + function internalRequire(name, referrerName) { + var exports = seen[name]; + + if (exports !== undefined) { + return exports; + } + + exports = seen[name] = {}; + + if (!registry[name]) { + if (referrerName) { + throw new Error('Could not find module ' + name + ' required by: ' + referrerName); + } else { + throw new Error('Could not find module ' + name); + } + } + + var mod = registry[name]; + var deps = mod.deps; + var callback = mod.callback; + var reified = []; + var length = deps.length; + + for (var i=0; i\s*\(([^\)]+)\)/gm, "{anonymous}($1)").split("\n"); + stack.shift(); + } else { + // Firefox + stack = error.stack.replace(/(?:\n@:0)?\s+$/m, "").replace(/^\(/gm, "{anonymous}(").split("\n"); + } + + stackStr = "\n " + stack.slice(2).join("\n "); + message = message + stackStr; + } + + Logger['default'].warn("DEPRECATION: " + message); + }; + + /** + Alias an old, deprecated method with its new counterpart. + + Display a deprecation warning with the provided message and a stack trace + (Chrome and Firefox only) when the assigned method is called. + + Ember build tools will not remove calls to `Ember.deprecateFunc()`, though + no warnings will be shown in production. + + ```javascript + Ember.oldMethod = Ember.deprecateFunc('Please use the new, updated method', Ember.newMethod); + ``` + + @method deprecateFunc + @param {String} message A description of the deprecation. + @param {Function} func The new function called to replace its deprecated counterpart. + @return {Function} a new function that wrapped the original function with a deprecation warning + */ + Ember['default'].deprecateFunc = function (message, func) { + return function () { + Ember['default'].deprecate(message); + return func.apply(this, arguments); + }; + }; + + /** + Run a function meant for debugging. Ember build tools will remove any calls to + `Ember.runInDebug()` when doing a production build. + + ```javascript + Ember.runInDebug(function() { + Ember.Handlebars.EachView.reopen({ + didInsertElement: function() { + console.log('I\'m happy'); + } + }); + }); + ``` + + @method runInDebug + @param {Function} func The function to be executed. + @since 1.5.0 + */ + Ember['default'].runInDebug = function (func) { + func(); + }; + + /** + Will call `Ember.warn()` if ENABLE_ALL_FEATURES, ENABLE_OPTIONAL_FEATURES, or + any specific FEATURES flag is truthy. + + This method is called automatically in debug canary builds. + + @private + @method _warnIfUsingStrippedFeatureFlags + @return {void} + */ + function _warnIfUsingStrippedFeatureFlags(FEATURES, featuresWereStripped) { + if (featuresWereStripped) { + Ember['default'].warn("Ember.ENV.ENABLE_ALL_FEATURES is only available in canary builds.", !Ember['default'].ENV.ENABLE_ALL_FEATURES); + Ember['default'].warn("Ember.ENV.ENABLE_OPTIONAL_FEATURES is only available in canary builds.", !Ember['default'].ENV.ENABLE_OPTIONAL_FEATURES); + + for (var key in FEATURES) { + if (FEATURES.hasOwnProperty(key) && key !== "isEnabled") { + Ember['default'].warn("FEATURE[\"" + key + "\"] is set as enabled, but FEATURE flags are only available in canary builds.", !FEATURES[key]); + } + } + } + } + + if (!Ember['default'].testing) { + // Complain if they're using FEATURE flags in builds other than canary + Ember['default'].FEATURES["features-stripped-test"] = true; + var featuresWereStripped = true; + + + delete Ember['default'].FEATURES["features-stripped-test"]; + _warnIfUsingStrippedFeatureFlags(Ember['default'].ENV.FEATURES, featuresWereStripped); + + // Inform the developer about the Ember Inspector if not installed. + var isFirefox = typeof InstallTrigger !== "undefined"; + var isChrome = environment['default'].isChrome; + + if (typeof window !== "undefined" && (isFirefox || isChrome) && window.addEventListener) { + window.addEventListener("load", function () { + if (document.documentElement && document.documentElement.dataset && !document.documentElement.dataset.emberExtension) { + var downloadURL; + + if (isChrome) { + downloadURL = "https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi"; + } else if (isFirefox) { + downloadURL = "https://addons.mozilla.org/en-US/firefox/addon/ember-inspector/"; + } + + Ember['default'].debug("For more advanced debugging, install the Ember Inspector from " + downloadURL); + } + }, false); + } + } + + /* + We are transitioning away from `ember.js` to `ember.debug.js` to make + it much clearer that it is only for local development purposes. + + This flag value is changed by the tooling (by a simple string replacement) + so that if `ember.js` (which must be output for backwards compat reasons) is + used a nice helpful warning message will be printed out. + */ + var runningNonEmberDebugJS = false; + if (runningNonEmberDebugJS) { + Ember['default'].warn("Please use `ember.debug.js` instead of `ember.js` for development and debugging."); + } + + exports.runningNonEmberDebugJS = runningNonEmberDebugJS; + +}); +enifed('ember-metal', ['exports', 'ember-metal/core', 'ember-metal/merge', 'ember-metal/instrumentation', 'ember-metal/utils', 'ember-metal/error', 'ember-metal/enumerable_utils', 'ember-metal/cache', 'ember-metal/platform/define_property', 'ember-metal/platform/create', 'ember-metal/array', 'ember-metal/logger', 'ember-metal/property_get', 'ember-metal/events', 'ember-metal/observer_set', 'ember-metal/property_events', 'ember-metal/properties', 'ember-metal/property_set', 'ember-metal/map', 'ember-metal/get_properties', 'ember-metal/set_properties', 'ember-metal/watch_key', 'ember-metal/chains', 'ember-metal/watch_path', 'ember-metal/watching', 'ember-metal/expand_properties', 'ember-metal/computed', 'ember-metal/alias', 'ember-metal/computed_macros', 'ember-metal/observer', 'ember-metal/mixin', 'ember-metal/binding', 'ember-metal/run_loop', 'ember-metal/libraries', 'ember-metal/is_none', 'ember-metal/is_empty', 'ember-metal/is_blank', 'ember-metal/is_present', 'ember-metal/keys', 'backburner', 'ember-metal/streams/utils', 'ember-metal/streams/stream'], function (exports, Ember, merge, instrumentation, utils, EmberError, EnumerableUtils, Cache, define_property, create, array, Logger, property_get, events, ObserverSet, property_events, properties, property_set, map, getProperties, setProperties, watch_key, chains, watch_path, watching, expandProperties, computed, alias, computed_macros, observer, mixin, binding, run, Libraries, isNone, isEmpty, isBlank, isPresent, keys, Backburner, streams__utils, Stream) { + + 'use strict'; + + /** + Ember Metal + + @module ember + @submodule ember-metal + */ + + // BEGIN IMPORTS + computed.computed.empty = computed_macros.empty; + computed.computed.notEmpty = computed_macros.notEmpty; + computed.computed.none = computed_macros.none; + computed.computed.not = computed_macros.not; + computed.computed.bool = computed_macros.bool; + computed.computed.match = computed_macros.match; + computed.computed.equal = computed_macros.equal; + computed.computed.gt = computed_macros.gt; + computed.computed.gte = computed_macros.gte; + computed.computed.lt = computed_macros.lt; + computed.computed.lte = computed_macros.lte; + computed.computed.alias = alias['default']; + computed.computed.oneWay = computed_macros.oneWay; + computed.computed.reads = computed_macros.oneWay; + computed.computed.readOnly = computed_macros.readOnly; + computed.computed.defaultTo = computed_macros.defaultTo; + computed.computed.deprecatingAlias = computed_macros.deprecatingAlias; + computed.computed.and = computed_macros.and; + computed.computed.or = computed_macros.or; + computed.computed.any = computed_macros.any; + computed.computed.collect = computed_macros.collect; // END IMPORTS + + // BEGIN EXPORTS + var EmberInstrumentation = Ember['default'].Instrumentation = {}; + EmberInstrumentation.instrument = instrumentation.instrument; + EmberInstrumentation.subscribe = instrumentation.subscribe; + EmberInstrumentation.unsubscribe = instrumentation.unsubscribe; + EmberInstrumentation.reset = instrumentation.reset; + + Ember['default'].instrument = instrumentation.instrument; + Ember['default'].subscribe = instrumentation.subscribe; + + Ember['default']._Cache = Cache['default']; + + Ember['default'].generateGuid = utils.generateGuid; + Ember['default'].GUID_KEY = utils.GUID_KEY; + Ember['default'].create = create['default']; + Ember['default'].keys = keys['default']; + Ember['default'].platform = { + defineProperty: properties.defineProperty, + hasPropertyAccessors: define_property.hasPropertyAccessors + }; + + var EmberArrayPolyfills = Ember['default'].ArrayPolyfills = {}; + + EmberArrayPolyfills.map = array.map; + EmberArrayPolyfills.forEach = array.forEach; + EmberArrayPolyfills.filter = array.filter; + EmberArrayPolyfills.indexOf = array.indexOf; + + Ember['default'].Error = EmberError['default']; + Ember['default'].guidFor = utils.guidFor; + Ember['default'].META_DESC = utils.META_DESC; + Ember['default'].EMPTY_META = utils.EMPTY_META; + Ember['default'].meta = utils.meta; + Ember['default'].getMeta = utils.getMeta; + Ember['default'].setMeta = utils.setMeta; + Ember['default'].metaPath = utils.metaPath; + Ember['default'].inspect = utils.inspect; + Ember['default'].typeOf = utils.typeOf; + Ember['default'].tryCatchFinally = utils.deprecatedTryCatchFinally; + Ember['default'].isArray = utils.isArray; + Ember['default'].makeArray = utils.makeArray; + Ember['default'].canInvoke = utils.canInvoke; + Ember['default'].tryInvoke = utils.tryInvoke; + Ember['default'].tryFinally = utils.deprecatedTryFinally; + Ember['default'].wrap = utils.wrap; + Ember['default'].apply = utils.apply; + Ember['default'].applyStr = utils.applyStr; + Ember['default'].uuid = utils.uuid; + + Ember['default'].Logger = Logger['default']; + + Ember['default'].get = property_get.get; + Ember['default'].getWithDefault = property_get.getWithDefault; + Ember['default'].normalizeTuple = property_get.normalizeTuple; + Ember['default']._getPath = property_get._getPath; + + Ember['default'].EnumerableUtils = EnumerableUtils['default']; + + Ember['default'].on = events.on; + Ember['default'].addListener = events.addListener; + Ember['default'].removeListener = events.removeListener; + Ember['default']._suspendListener = events.suspendListener; + Ember['default']._suspendListeners = events.suspendListeners; + Ember['default'].sendEvent = events.sendEvent; + Ember['default'].hasListeners = events.hasListeners; + Ember['default'].watchedEvents = events.watchedEvents; + Ember['default'].listenersFor = events.listenersFor; + Ember['default'].accumulateListeners = events.accumulateListeners; + + Ember['default']._ObserverSet = ObserverSet['default']; + + Ember['default'].propertyWillChange = property_events.propertyWillChange; + Ember['default'].propertyDidChange = property_events.propertyDidChange; + Ember['default'].overrideChains = property_events.overrideChains; + Ember['default'].beginPropertyChanges = property_events.beginPropertyChanges; + Ember['default'].endPropertyChanges = property_events.endPropertyChanges; + Ember['default'].changeProperties = property_events.changeProperties; + + Ember['default'].defineProperty = properties.defineProperty; + + Ember['default'].set = property_set.set; + Ember['default'].trySet = property_set.trySet; + + Ember['default'].OrderedSet = map.OrderedSet; + Ember['default'].Map = map.Map; + Ember['default'].MapWithDefault = map.MapWithDefault; + + Ember['default'].getProperties = getProperties['default']; + Ember['default'].setProperties = setProperties['default']; + + Ember['default'].watchKey = watch_key.watchKey; + Ember['default'].unwatchKey = watch_key.unwatchKey; + + Ember['default'].flushPendingChains = chains.flushPendingChains; + Ember['default'].removeChainWatcher = chains.removeChainWatcher; + Ember['default']._ChainNode = chains.ChainNode; + Ember['default'].finishChains = chains.finishChains; + + Ember['default'].watchPath = watch_path.watchPath; + Ember['default'].unwatchPath = watch_path.unwatchPath; + + Ember['default'].watch = watching.watch; + Ember['default'].isWatching = watching.isWatching; + Ember['default'].unwatch = watching.unwatch; + Ember['default'].rewatch = watching.rewatch; + Ember['default'].destroy = watching.destroy; + + Ember['default'].expandProperties = expandProperties['default']; + + Ember['default'].ComputedProperty = computed.ComputedProperty; + Ember['default'].computed = computed.computed; + Ember['default'].cacheFor = computed.cacheFor; + + Ember['default'].addObserver = observer.addObserver; + Ember['default'].observersFor = observer.observersFor; + Ember['default'].removeObserver = observer.removeObserver; + Ember['default'].addBeforeObserver = observer.addBeforeObserver; + Ember['default']._suspendBeforeObserver = observer._suspendBeforeObserver; + Ember['default']._suspendBeforeObservers = observer._suspendBeforeObservers; + Ember['default']._suspendObserver = observer._suspendObserver; + Ember['default']._suspendObservers = observer._suspendObservers; + Ember['default'].beforeObserversFor = observer.beforeObserversFor; + Ember['default'].removeBeforeObserver = observer.removeBeforeObserver; + + Ember['default'].IS_BINDING = mixin.IS_BINDING; + Ember['default'].required = mixin.required; + Ember['default'].aliasMethod = mixin.aliasMethod; + Ember['default'].observer = mixin.observer; + Ember['default'].immediateObserver = mixin.immediateObserver; + Ember['default'].beforeObserver = mixin.beforeObserver; + Ember['default'].mixin = mixin.mixin; + Ember['default'].Mixin = mixin.Mixin; + + Ember['default'].oneWay = binding.oneWay; + Ember['default'].bind = binding.bind; + Ember['default'].Binding = binding.Binding; + Ember['default'].isGlobalPath = binding.isGlobalPath; + + Ember['default'].run = run['default']; + + /** + * @class Backburner + * @for Ember + * @private + */ + Ember['default'].Backburner = Backburner['default']; + + Ember['default'].libraries = new Libraries['default'](); + Ember['default'].libraries.registerCoreLibrary("Ember", Ember['default'].VERSION); + + Ember['default'].isNone = isNone['default']; + Ember['default'].isEmpty = isEmpty['default']; + Ember['default'].isBlank = isBlank['default']; + Ember['default'].isPresent = isPresent['default']; + + Ember['default'].merge = merge['default']; + + + /** + A function may be assigned to `Ember.onerror` to be called when Ember + internals encounter an error. This is useful for specialized error handling + and reporting code. + + ```javascript + Ember.onerror = function(error) { + Em.$.ajax('/report-error', 'POST', { + stack: error.stack, + otherInformation: 'whatever app state you want to provide' + }); + }; + ``` + + Internally, `Ember.onerror` is used as Backburner's error handler. + + @event onerror + @for Ember + @param {Exception} error the error object + */ + Ember['default'].onerror = null; + // END EXPORTS + + // do this for side-effects of updating Ember.assert, warn, etc when + // ember-debug is present + if (Ember['default'].__loader.registry["ember-debug"]) { + requireModule("ember-debug"); + } + + exports['default'] = Ember['default']; + +}); +enifed('ember-metal/alias', ['exports', 'ember-metal/property_get', 'ember-metal/property_set', 'ember-metal/core', 'ember-metal/error', 'ember-metal/properties', 'ember-metal/computed', 'ember-metal/platform/create', 'ember-metal/utils', 'ember-metal/dependent_keys'], function (exports, property_get, property_set, Ember, EmberError, properties, computed, create, utils, dependent_keys) { + + 'use strict'; + + exports.AliasedProperty = AliasedProperty; + + exports['default'] = alias; + + function alias(altKey) { + return new AliasedProperty(altKey); + } + + function AliasedProperty(altKey) { + this.isDescriptor = true; + this.altKey = altKey; + this._dependentKeys = [altKey]; + } + + AliasedProperty.prototype = create['default'](properties.Descriptor.prototype); + + AliasedProperty.prototype.get = function AliasedProperty_get(obj, keyName) { + return property_get.get(obj, this.altKey); + }; + + AliasedProperty.prototype.set = function AliasedProperty_set(obj, keyName, value) { + return property_set.set(obj, this.altKey, value); + }; + + AliasedProperty.prototype.willWatch = function (obj, keyName) { + dependent_keys.addDependentKeys(this, obj, keyName, utils.meta(obj)); + }; + + AliasedProperty.prototype.didUnwatch = function (obj, keyName) { + dependent_keys.removeDependentKeys(this, obj, keyName, utils.meta(obj)); + }; + + AliasedProperty.prototype.setup = function (obj, keyName) { + Ember['default'].assert("Setting alias '" + keyName + "' on self", this.altKey !== keyName); + var m = utils.meta(obj); + if (m.watching[keyName]) { + dependent_keys.addDependentKeys(this, obj, keyName, m); + } + }; + + AliasedProperty.prototype.teardown = function (obj, keyName) { + var m = utils.meta(obj); + if (m.watching[keyName]) { + dependent_keys.removeDependentKeys(this, obj, keyName, m); + } + }; + + AliasedProperty.prototype.readOnly = function () { + this.set = AliasedProperty_readOnlySet; + return this; + }; + + function AliasedProperty_readOnlySet(obj, keyName, value) { + throw new EmberError['default']("Cannot set read-only property '" + keyName + "' on object: " + utils.inspect(obj)); + } + + AliasedProperty.prototype.oneWay = function () { + this.set = AliasedProperty_oneWaySet; + return this; + }; + + function AliasedProperty_oneWaySet(obj, keyName, value) { + properties.defineProperty(obj, keyName, null); + return property_set.set(obj, keyName, value); + } + + // Backwards compatibility with Ember Data + AliasedProperty.prototype._meta = undefined; + AliasedProperty.prototype.meta = computed.ComputedProperty.prototype.meta; + +}); +enifed('ember-metal/array', ['exports'], function (exports) { + + 'use strict'; + + /** + @module ember-metal + */ + + var ArrayPrototype = Array.prototype; + + // Testing this is not ideal, but we want to use native functions + // if available, but not to use versions created by libraries like Prototype + var isNativeFunc = function (func) { + // This should probably work in all browsers likely to have ES5 array methods + return func && Function.prototype.toString.call(func).indexOf("[native code]") > -1; + }; + + var defineNativeShim = function (nativeFunc, shim) { + if (isNativeFunc(nativeFunc)) { + return nativeFunc; + } + return shim; + }; + + // From: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/array/map + var map = defineNativeShim(ArrayPrototype.map, function (fun) { + //"use strict"; + + if (this === void 0 || this === null || typeof fun !== "function") { + throw new TypeError(); + } + + var t = Object(this); + var len = t.length >>> 0; + var res = new Array(len); + + for (var i = 0; i < len; i++) { + if (i in t) { + res[i] = fun.call(arguments[1], t[i], i, t); + } + } + + return res; + }); + + // From: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/array/foreach + var forEach = defineNativeShim(ArrayPrototype.forEach, function (fun) { + //"use strict"; + + if (this === void 0 || this === null || typeof fun !== "function") { + throw new TypeError(); + } + + var t = Object(this); + var len = t.length >>> 0; + + for (var i = 0; i < len; i++) { + if (i in t) { + fun.call(arguments[1], t[i], i, t); + } + } + }); + + var indexOf = defineNativeShim(ArrayPrototype.indexOf, function (obj, fromIndex) { + if (fromIndex === null || fromIndex === undefined) { + fromIndex = 0; + } else if (fromIndex < 0) { + fromIndex = Math.max(0, this.length + fromIndex); + } + + for (var i = fromIndex, j = this.length; i < j; i++) { + if (this[i] === obj) { + return i; + } + } + return -1; + }); + + var lastIndexOf = defineNativeShim(ArrayPrototype.lastIndexOf, function (obj, fromIndex) { + var len = this.length; + var idx; + + if (fromIndex === undefined) { + fromIndex = len - 1; + } else { + fromIndex = fromIndex < 0 ? Math.ceil(fromIndex) : Math.floor(fromIndex); + } + + if (fromIndex < 0) { + fromIndex += len; + } + + for (idx = fromIndex; idx >= 0; idx--) { + if (this[idx] === obj) { + return idx; + } + } + return -1; + }); + + var filter = defineNativeShim(ArrayPrototype.filter, function (fn, context) { + var i, value; + var result = []; + var length = this.length; + + for (i = 0; i < length; i++) { + if (this.hasOwnProperty(i)) { + value = this[i]; + if (fn.call(context, value, i, this)) { + result.push(value); + } + } + } + return result; + }); + + if (Ember.SHIM_ES5) { + ArrayPrototype.map = ArrayPrototype.map || map; + ArrayPrototype.forEach = ArrayPrototype.forEach || forEach; + ArrayPrototype.filter = ArrayPrototype.filter || filter; + ArrayPrototype.indexOf = ArrayPrototype.indexOf || indexOf; + ArrayPrototype.lastIndexOf = ArrayPrototype.lastIndexOf || lastIndexOf; + } + + /** + Array polyfills to support ES5 features in older browsers. + + @namespace Ember + @property ArrayPolyfills + */ + + exports.map = map; + exports.forEach = forEach; + exports.filter = filter; + exports.indexOf = indexOf; + exports.lastIndexOf = lastIndexOf; + +}); +enifed('ember-metal/binding', ['exports', 'ember-metal/core', 'ember-metal/property_get', 'ember-metal/property_set', 'ember-metal/utils', 'ember-metal/observer', 'ember-metal/run_loop', 'ember-metal/path_cache'], function (exports, Ember, property_get, property_set, utils, observer, run, path_cache) { + + 'use strict'; + + exports.bind = bind; + exports.oneWay = oneWay; + exports.Binding = Binding; + + Ember['default'].LOG_BINDINGS = false || !!Ember['default'].ENV.LOG_BINDINGS; + + /** + Returns true if the provided path is global (e.g., `MyApp.fooController.bar`) + instead of local (`foo.bar.baz`). + + @method isGlobalPath + @for Ember + @private + @param {String} path + @return Boolean + */ + + function getWithGlobals(obj, path) { + return property_get.get(path_cache.isGlobal(path) ? Ember['default'].lookup : obj, path); + } + + // .......................................................... + // BINDING + // + + function Binding(toPath, fromPath) { + this._direction = undefined; + this._from = fromPath; + this._to = toPath; + this._readyToSync = undefined; + this._oneWay = undefined; + } + + /** + @class Binding + @namespace Ember + */ + + Binding.prototype = { + /** + This copies the Binding so it can be connected to another object. + @method copy + @return {Ember.Binding} `this` + */ + copy: function () { + var copy = new Binding(this._to, this._from); + if (this._oneWay) { + copy._oneWay = true; + } + return copy; + }, + + // .......................................................... + // CONFIG + // + + /** + This will set `from` property path to the specified value. It will not + attempt to resolve this property path to an actual object until you + connect the binding. + The binding will search for the property path starting at the root object + you pass when you `connect()` the binding. It follows the same rules as + `get()` - see that method for more information. + @method from + @param {String} path the property path to connect to + @return {Ember.Binding} `this` + */ + from: function (path) { + this._from = path; + return this; + }, + + /** + This will set the `to` property path to the specified value. It will not + attempt to resolve this property path to an actual object until you + connect the binding. + The binding will search for the property path starting at the root object + you pass when you `connect()` the binding. It follows the same rules as + `get()` - see that method for more information. + @method to + @param {String|Tuple} path A property path or tuple + @return {Ember.Binding} `this` + */ + to: function (path) { + this._to = path; + return this; + }, + + /** + Configures the binding as one way. A one-way binding will relay changes + on the `from` side to the `to` side, but not the other way around. This + means that if you change the `to` side directly, the `from` side may have + a different value. + @method oneWay + @return {Ember.Binding} `this` + */ + oneWay: function () { + this._oneWay = true; + return this; + }, + + /** + @method toString + @return {String} string representation of binding + */ + toString: function () { + var oneWay = this._oneWay ? "[oneWay]" : ""; + return "Ember.Binding<" + utils.guidFor(this) + ">(" + this._from + " -> " + this._to + ")" + oneWay; + }, + + // .......................................................... + // CONNECT AND SYNC + // + + /** + Attempts to connect this binding instance so that it can receive and relay + changes. This method will raise an exception if you have not set the + from/to properties yet. + @method connect + @param {Object} obj The root object for this binding. + @return {Ember.Binding} `this` + */ + connect: function (obj) { + Ember['default'].assert("Must pass a valid object to Ember.Binding.connect()", !!obj); + + var fromPath = this._from; + var toPath = this._to; + property_set.trySet(obj, toPath, getWithGlobals(obj, fromPath)); + + // add an observer on the object to be notified when the binding should be updated + observer.addObserver(obj, fromPath, this, this.fromDidChange); + + // if the binding is a two-way binding, also set up an observer on the target + if (!this._oneWay) { + observer.addObserver(obj, toPath, this, this.toDidChange); + } + + this._readyToSync = true; + + return this; + }, + + /** + Disconnects the binding instance. Changes will no longer be relayed. You + will not usually need to call this method. + @method disconnect + @param {Object} obj The root object you passed when connecting the binding. + @return {Ember.Binding} `this` + */ + disconnect: function (obj) { + Ember['default'].assert("Must pass a valid object to Ember.Binding.disconnect()", !!obj); + + var twoWay = !this._oneWay; + + // remove an observer on the object so we're no longer notified of + // changes that should update bindings. + observer.removeObserver(obj, this._from, this, this.fromDidChange); + + // if the binding is two-way, remove the observer from the target as well + if (twoWay) { + observer.removeObserver(obj, this._to, this, this.toDidChange); + } + + this._readyToSync = false; // disable scheduled syncs... + return this; + }, + + // .......................................................... + // PRIVATE + // + + /* called when the from side changes */ + fromDidChange: function (target) { + this._scheduleSync(target, "fwd"); + }, + + /* called when the to side changes */ + toDidChange: function (target) { + this._scheduleSync(target, "back"); + }, + + _scheduleSync: function (obj, dir) { + var existingDir = this._direction; + + // if we haven't scheduled the binding yet, schedule it + if (existingDir === undefined) { + run['default'].schedule("sync", this, this._sync, obj); + this._direction = dir; + } + + // If both a 'back' and 'fwd' sync have been scheduled on the same object, + // default to a 'fwd' sync so that it remains deterministic. + if (existingDir === "back" && dir === "fwd") { + this._direction = "fwd"; + } + }, + + _sync: function (obj) { + var log = Ember['default'].LOG_BINDINGS; + + // don't synchronize destroyed objects or disconnected bindings + if (obj.isDestroyed || !this._readyToSync) { + return; + } + + // get the direction of the binding for the object we are + // synchronizing from + var direction = this._direction; + + var fromPath = this._from; + var toPath = this._to; + + this._direction = undefined; + + // if we're synchronizing from the remote object... + if (direction === "fwd") { + var fromValue = getWithGlobals(obj, this._from); + if (log) { + Ember['default'].Logger.log(" ", this.toString(), "->", fromValue, obj); + } + if (this._oneWay) { + property_set.trySet(obj, toPath, fromValue); + } else { + observer._suspendObserver(obj, toPath, this, this.toDidChange, function () { + property_set.trySet(obj, toPath, fromValue); + }); + } + // if we're synchronizing *to* the remote object + } else if (direction === "back") { + var toValue = property_get.get(obj, this._to); + if (log) { + Ember['default'].Logger.log(" ", this.toString(), "<-", toValue, obj); + } + observer._suspendObserver(obj, fromPath, this, this.fromDidChange, function () { + property_set.trySet(path_cache.isGlobal(fromPath) ? Ember['default'].lookup : obj, fromPath, toValue); + }); + } + } + + }; + + function mixinProperties(to, from) { + for (var key in from) { + if (from.hasOwnProperty(key)) { + to[key] = from[key]; + } + } + } + + mixinProperties(Binding, { + + /* + See `Ember.Binding.from`. + @method from + @static + */ + from: function (from) { + var C = this; + return new C(undefined, from); + }, + + /* + See `Ember.Binding.to`. + @method to + @static + */ + to: function (to) { + var C = this; + return new C(to, undefined); + }, + + /** + Creates a new Binding instance and makes it apply in a single direction. + A one-way binding will relay changes on the `from` side object (supplied + as the `from` argument) the `to` side, but not the other way around. + This means that if you change the "to" side directly, the "from" side may have + a different value. + See `Binding.oneWay`. + @method oneWay + @param {String} from from path. + @param {Boolean} [flag] (Optional) passing nothing here will make the + binding `oneWay`. You can instead pass `false` to disable `oneWay`, making the + binding two way again. + @return {Ember.Binding} `this` + */ + oneWay: function (from, flag) { + var C = this; + return new C(undefined, from).oneWay(flag); + } + + }); + /** + An `Ember.Binding` connects the properties of two objects so that whenever + the value of one property changes, the other property will be changed also. + + ## Automatic Creation of Bindings with `/^*Binding/`-named Properties + + You do not usually create Binding objects directly but instead describe + bindings in your class or object definition using automatic binding + detection. + + Properties ending in a `Binding` suffix will be converted to `Ember.Binding` + instances. The value of this property should be a string representing a path + to another object or a custom binding instance created using Binding helpers + (see "One Way Bindings"): + + ``` + valueBinding: "MyApp.someController.title" + ``` + + This will create a binding from `MyApp.someController.title` to the `value` + property of your object instance automatically. Now the two values will be + kept in sync. + + ## One Way Bindings + + One especially useful binding customization you can use is the `oneWay()` + helper. This helper tells Ember that you are only interested in + receiving changes on the object you are binding from. For example, if you + are binding to a preference and you want to be notified if the preference + has changed, but your object will not be changing the preference itself, you + could do: + + ``` + bigTitlesBinding: Ember.Binding.oneWay("MyApp.preferencesController.bigTitles") + ``` + + This way if the value of `MyApp.preferencesController.bigTitles` changes the + `bigTitles` property of your object will change also. However, if you + change the value of your `bigTitles` property, it will not update the + `preferencesController`. + + One way bindings are almost twice as fast to setup and twice as fast to + execute because the binding only has to worry about changes to one side. + + You should consider using one way bindings anytime you have an object that + may be created frequently and you do not intend to change a property; only + to monitor it for changes (such as in the example above). + + ## Adding Bindings Manually + + All of the examples above show you how to configure a custom binding, but the + result of these customizations will be a binding template, not a fully active + Binding instance. The binding will actually become active only when you + instantiate the object the binding belongs to. It is useful however, to + understand what actually happens when the binding is activated. + + For a binding to function it must have at least a `from` property and a `to` + property. The `from` property path points to the object/key that you want to + bind from while the `to` path points to the object/key you want to bind to. + + When you define a custom binding, you are usually describing the property + you want to bind from (such as `MyApp.someController.value` in the examples + above). When your object is created, it will automatically assign the value + you want to bind `to` based on the name of your binding key. In the + examples above, during init, Ember objects will effectively call + something like this on your binding: + + ```javascript + binding = Ember.Binding.from("valueBinding").to("value"); + ``` + + This creates a new binding instance based on the template you provide, and + sets the to path to the `value` property of the new object. Now that the + binding is fully configured with a `from` and a `to`, it simply needs to be + connected to become active. This is done through the `connect()` method: + + ```javascript + binding.connect(this); + ``` + + Note that when you connect a binding you pass the object you want it to be + connected to. This object will be used as the root for both the from and + to side of the binding when inspecting relative paths. This allows the + binding to be automatically inherited by subclassed objects as well. + + This also allows you to bind between objects using the paths you declare in + `from` and `to`: + + ```javascript + // Example 1 + binding = Ember.Binding.from("App.someObject.value").to("value"); + binding.connect(this); + + // Example 2 + binding = Ember.Binding.from("parentView.value").to("App.someObject.value"); + binding.connect(this); + ``` + + Now that the binding is connected, it will observe both the from and to side + and relay changes. + + If you ever needed to do so (you almost never will, but it is useful to + understand this anyway), you could manually create an active binding by + using the `Ember.bind()` helper method. (This is the same method used by + to setup your bindings on objects): + + ```javascript + Ember.bind(MyApp.anotherObject, "value", "MyApp.someController.value"); + ``` + + Both of these code fragments have the same effect as doing the most friendly + form of binding creation like so: + + ```javascript + MyApp.anotherObject = Ember.Object.create({ + valueBinding: "MyApp.someController.value", + + // OTHER CODE FOR THIS OBJECT... + }); + ``` + + Ember's built in binding creation method makes it easy to automatically + create bindings for you. You should always use the highest-level APIs + available, even if you understand how it works underneath. + + @class Binding + @namespace Ember + @since Ember 0.9 + */ + // Ember.Binding = Binding; ES6TODO: where to put this? + + /** + Global helper method to create a new binding. Just pass the root object + along with a `to` and `from` path to create and connect the binding. + + @method bind + @for Ember + @param {Object} obj The root object of the transform. + @param {String} to The path to the 'to' side of the binding. + Must be relative to obj. + @param {String} from The path to the 'from' side of the binding. + Must be relative to obj or a global path. + @return {Ember.Binding} binding instance + */ + function bind(obj, to, from) { + return new Binding(to, from).connect(obj); + } + + /** + @method oneWay + @for Ember + @param {Object} obj The root object of the transform. + @param {String} to The path to the 'to' side of the binding. + Must be relative to obj. + @param {String} from The path to the 'from' side of the binding. + Must be relative to obj or a global path. + @return {Ember.Binding} binding instance + */ + function oneWay(obj, to, from) { + return new Binding(to, from).oneWay().connect(obj); + } + + exports.isGlobalPath = path_cache.isGlobal; + +}); +enifed('ember-metal/cache', ['exports', 'ember-metal/dictionary'], function (exports, dictionary) { + + 'use strict'; + + exports['default'] = Cache; + + function Cache(limit, func) { + this.store = dictionary['default'](null); + this.size = 0; + this.misses = 0; + this.hits = 0; + this.limit = limit; + this.func = func; + } + + var UNDEFINED = function () {}; + + Cache.prototype = { + set: function (key, value) { + if (this.limit > this.size) { + this.size++; + if (value === undefined) { + this.store[key] = UNDEFINED; + } else { + this.store[key] = value; + } + } + + return value; + }, + + get: function (key) { + var value = this.store[key]; + + if (value === undefined) { + this.misses++; + value = this.set(key, this.func(key)); + } else if (value === UNDEFINED) { + this.hits++; + value = undefined; + } else { + this.hits++; + // nothing to translate + } + + return value; + }, + + purge: function () { + this.store = dictionary['default'](null); + this.size = 0; + this.hits = 0; + this.misses = 0; + } + }; + +}); +enifed('ember-metal/chains', ['exports', 'ember-metal/core', 'ember-metal/property_get', 'ember-metal/utils', 'ember-metal/array', 'ember-metal/watch_key'], function (exports, Ember, property_get, utils, array, watch_key) { + + 'use strict'; + + exports.flushPendingChains = flushPendingChains; + exports.finishChains = finishChains; + exports.removeChainWatcher = removeChainWatcher; + exports.ChainNode = ChainNode; + + var warn = Ember['default'].warn; + var FIRST_KEY = /^([^\.]+)/; + + function firstKey(path) { + return path.match(FIRST_KEY)[0]; + } + + function isObject(obj) { + return obj && typeof obj === "object"; + } + + var pendingQueue = []; + + // attempts to add the pendingQueue chains again. If some of them end up + // back in the queue and reschedule is true, schedules a timeout to try + // again. + + function flushPendingChains() { + if (pendingQueue.length === 0) { + return; + } + + var queue = pendingQueue; + pendingQueue = []; + + array.forEach.call(queue, function (q) { + q[0].add(q[1]); + }); + + warn("Watching an undefined global, Ember expects watched globals to be" + " setup by the time the run loop is flushed, check for typos", pendingQueue.length === 0); + } + + function addChainWatcher(obj, keyName, node) { + if (!isObject(obj)) { + return; + } + + var m = utils.meta(obj); + var nodes = m.chainWatchers; + + if (!m.hasOwnProperty("chainWatchers")) { + // FIXME?! + nodes = m.chainWatchers = {}; + } + + if (!nodes[keyName]) { + nodes[keyName] = []; + } + nodes[keyName].push(node); + watch_key.watchKey(obj, keyName, m); + } + + function removeChainWatcher(obj, keyName, node) { + if (!isObject(obj)) { + return; + } + + var m = obj["__ember_meta__"]; + if (m && !m.hasOwnProperty("chainWatchers")) { + return; + } + + var nodes = m && m.chainWatchers; + + if (nodes && nodes[keyName]) { + nodes = nodes[keyName]; + for (var i = 0, l = nodes.length; i < l; i++) { + if (nodes[i] === node) { + nodes.splice(i, 1); + break; + } + } + } + watch_key.unwatchKey(obj, keyName, m); + } + + // A ChainNode watches a single key on an object. If you provide a starting + // value for the key then the node won't actually watch it. For a root node + // pass null for parent and key and object for value. + function ChainNode(parent, key, value) { + this._parent = parent; + this._key = key; + + // _watching is true when calling get(this._parent, this._key) will + // return the value of this node. + // + // It is false for the root of a chain (because we have no parent) + // and for global paths (because the parent node is the object with + // the observer on it) + this._watching = value === undefined; + + this._value = value; + this._paths = {}; + if (this._watching) { + this._object = parent.value(); + if (this._object) { + addChainWatcher(this._object, this._key, this); + } + } + + // Special-case: the EachProxy relies on immediate evaluation to + // establish its observers. + // + // TODO: Replace this with an efficient callback that the EachProxy + // can implement. + if (this._parent && this._parent._key === "@each") { + this.value(); + } + } + + function lazyGet(obj, key) { + if (!obj) { + return; + } + + var meta = obj["__ember_meta__"]; + // check if object meant only to be a prototype + if (meta && meta.proto === obj) { + return; + } + + if (key === "@each") { + return property_get.get(obj, key); + } + + // if a CP only return cached value + var possibleDesc = obj[key]; + var desc = possibleDesc !== null && typeof possibleDesc === "object" && possibleDesc.isDescriptor ? possibleDesc : undefined; + if (desc && desc._cacheable) { + if (meta.cache && key in meta.cache) { + return meta.cache[key]; + } else { + return; + } + } + + return property_get.get(obj, key); + } + + ChainNode.prototype = { + value: function () { + if (this._value === undefined && this._watching) { + var obj = this._parent.value(); + this._value = lazyGet(obj, this._key); + } + return this._value; + }, + + destroy: function () { + if (this._watching) { + var obj = this._object; + if (obj) { + removeChainWatcher(obj, this._key, this); + } + this._watching = false; // so future calls do nothing + } + }, + + // copies a top level object only + copy: function (obj) { + var ret = new ChainNode(null, null, obj); + var paths = this._paths; + var path; + + for (path in paths) { + // this check will also catch non-number vals. + if (paths[path] <= 0) { + continue; + } + ret.add(path); + } + return ret; + }, + + // called on the root node of a chain to setup watchers on the specified + // path. + add: function (path) { + var obj, tuple, key, src, paths; + + paths = this._paths; + paths[path] = (paths[path] || 0) + 1; + + obj = this.value(); + tuple = property_get.normalizeTuple(obj, path); + + // the path was a local path + if (tuple[0] && tuple[0] === obj) { + path = tuple[1]; + key = firstKey(path); + path = path.slice(key.length + 1); + + // global path, but object does not exist yet. + // put into a queue and try to connect later. + } else if (!tuple[0]) { + pendingQueue.push([this, path]); + tuple.length = 0; + return; + + // global path, and object already exists + } else { + src = tuple[0]; + key = path.slice(0, 0 - (tuple[1].length + 1)); + path = tuple[1]; + } + + tuple.length = 0; + this.chain(key, path, src); + }, + + // called on the root node of a chain to teardown watcher on the specified + // path + remove: function (path) { + var obj, tuple, key, src, paths; + + paths = this._paths; + if (paths[path] > 0) { + paths[path]--; + } + + obj = this.value(); + tuple = property_get.normalizeTuple(obj, path); + if (tuple[0] === obj) { + path = tuple[1]; + key = firstKey(path); + path = path.slice(key.length + 1); + } else { + src = tuple[0]; + key = path.slice(0, 0 - (tuple[1].length + 1)); + path = tuple[1]; + } + + tuple.length = 0; + this.unchain(key, path); + }, + + count: 0, + + chain: function (key, path, src) { + var chains = this._chains; + var node; + if (!chains) { + chains = this._chains = {}; + } + + node = chains[key]; + if (!node) { + node = chains[key] = new ChainNode(this, key, src); + } + node.count++; // count chains... + + // chain rest of path if there is one + if (path) { + key = firstKey(path); + path = path.slice(key.length + 1); + node.chain(key, path); // NOTE: no src means it will observe changes... + } + }, + + unchain: function (key, path) { + var chains = this._chains; + var node = chains[key]; + + // unchain rest of path first... + if (path && path.length > 1) { + var nextKey = firstKey(path); + var nextPath = path.slice(nextKey.length + 1); + node.unchain(nextKey, nextPath); + } + + // delete node if needed. + node.count--; + if (node.count <= 0) { + delete chains[node._key]; + node.destroy(); + } + }, + + willChange: function (events) { + var chains = this._chains; + if (chains) { + for (var key in chains) { + if (!chains.hasOwnProperty(key)) { + continue; + } + chains[key].willChange(events); + } + } + + if (this._parent) { + this._parent.chainWillChange(this, this._key, 1, events); + } + }, + + chainWillChange: function (chain, path, depth, events) { + if (this._key) { + path = this._key + "." + path; + } + + if (this._parent) { + this._parent.chainWillChange(this, path, depth + 1, events); + } else { + if (depth > 1) { + events.push(this.value(), path); + } + path = "this." + path; + if (this._paths[path] > 0) { + events.push(this.value(), path); + } + } + }, + + chainDidChange: function (chain, path, depth, events) { + if (this._key) { + path = this._key + "." + path; + } + + if (this._parent) { + this._parent.chainDidChange(this, path, depth + 1, events); + } else { + if (depth > 1) { + events.push(this.value(), path); + } + path = "this." + path; + if (this._paths[path] > 0) { + events.push(this.value(), path); + } + } + }, + + didChange: function (events) { + // invalidate my own value first. + if (this._watching) { + var obj = this._parent.value(); + if (obj !== this._object) { + removeChainWatcher(this._object, this._key, this); + this._object = obj; + addChainWatcher(obj, this._key, this); + } + this._value = undefined; + + // Special-case: the EachProxy relies on immediate evaluation to + // establish its observers. + if (this._parent && this._parent._key === "@each") { + this.value(); + } + } + + // then notify chains... + var chains = this._chains; + if (chains) { + for (var key in chains) { + if (!chains.hasOwnProperty(key)) { + continue; + } + chains[key].didChange(events); + } + } + + // if no events are passed in then we only care about the above wiring update + if (events === null) { + return; + } + + // and finally tell parent about my path changing... + if (this._parent) { + this._parent.chainDidChange(this, this._key, 1, events); + } + } + }; + function finishChains(obj) { + // We only create meta if we really have to + var m = obj["__ember_meta__"]; + var chains, chainWatchers, chainNodes; + + if (m) { + // finish any current chains node watchers that reference obj + chainWatchers = m.chainWatchers; + if (chainWatchers) { + for (var key in chainWatchers) { + if (!chainWatchers.hasOwnProperty(key)) { + continue; + } + + chainNodes = chainWatchers[key]; + if (chainNodes) { + for (var i = 0, l = chainNodes.length; i < l; i++) { + chainNodes[i].didChange(null); + } + } + } + } + // copy chains from prototype + chains = m.chains; + if (chains && chains.value() !== obj) { + utils.meta(obj).chains = chains = chains.copy(obj); + } + } + } + +}); +enifed('ember-metal/computed', ['exports', 'ember-metal/property_set', 'ember-metal/utils', 'ember-metal/expand_properties', 'ember-metal/error', 'ember-metal/properties', 'ember-metal/property_events', 'ember-metal/dependent_keys'], function (exports, property_set, utils, expandProperties, EmberError, properties, property_events, dependent_keys) { + + 'use strict'; + + exports.ComputedProperty = ComputedProperty; + exports.computed = computed; + exports.cacheFor = cacheFor; + + var metaFor = utils.meta; + + function UNDEFINED() {} + + // .......................................................... + // COMPUTED PROPERTY + // + + /** + A computed property transforms an object's function into a property. + + By default the function backing the computed property will only be called + once and the result will be cached. You can specify various properties + that your computed property depends on. This will force the cached + result to be recomputed if the dependencies are modified. + + In the following example we declare a computed property (by calling + `.property()` on the fullName function) and setup the property + dependencies (depending on firstName and lastName). The fullName function + will be called once (regardless of how many times it is accessed) as long + as its dependencies have not changed. Once firstName or lastName are updated + any future calls (or anything bound) to fullName will incorporate the new + values. + + ```javascript + var Person = Ember.Object.extend({ + // these will be supplied by `create` + firstName: null, + lastName: null, + + fullName: function() { + var firstName = this.get('firstName'); + var lastName = this.get('lastName'); + + return firstName + ' ' + lastName; + }.property('firstName', 'lastName') + }); + + var tom = Person.create({ + firstName: 'Tom', + lastName: 'Dale' + }); + + tom.get('fullName') // 'Tom Dale' + ``` + + You can also define what Ember should do when setting a computed property. + If you try to set a computed property, it will be invoked with the key and + value you want to set it to. You can also accept the previous value as the + third parameter. + + ```javascript + var Person = Ember.Object.extend({ + // these will be supplied by `create` + firstName: null, + lastName: null, + + fullName: function(key, value, oldValue) { + // getter + if (arguments.length === 1) { + var firstName = this.get('firstName'); + var lastName = this.get('lastName'); + + return firstName + ' ' + lastName; + + // setter + } else { + var name = value.split(' '); + + this.set('firstName', name[0]); + this.set('lastName', name[1]); + + return value; + } + }.property('firstName', 'lastName') + }); + + var person = Person.create(); + + person.set('fullName', 'Peter Wagenet'); + person.get('firstName'); // 'Peter' + person.get('lastName'); // 'Wagenet' + ``` + + @class ComputedProperty + @namespace Ember + @constructor + */ + function ComputedProperty(config, opts) { + this.isDescriptor = true; + + if (typeof config === "function") { + config.__ember_arity = config.length; + this._getter = config; + if (config.__ember_arity > 1) { + Ember.deprecate("Using the same function as getter and setter is deprecated.", false, { + url: "http://emberjs.com/deprecations/v1.x/#toc_computed-properties-with-a-shared-getter-and-setter" + }); + this._setter = config; + } + } else { + this._getter = config.get; + this._setter = config.set; + if (this._setter && this._setter.__ember_arity === undefined) { + this._setter.__ember_arity = this._setter.length; + } + } + + this._dependentKeys = undefined; + this._suspended = undefined; + this._meta = undefined; + + Ember.deprecate("Passing opts.cacheable to the CP constructor is deprecated. Invoke `volatile()` on the CP instead.", !opts || !opts.hasOwnProperty("cacheable")); + this._cacheable = opts && opts.cacheable !== undefined ? opts.cacheable : true; // TODO: Set always to `true` once this deprecation is gone. + this._dependentKeys = opts && opts.dependentKeys; + Ember.deprecate("Passing opts.readOnly to the CP constructor is deprecated. All CPs are writable by default. You can invoke `readOnly()` on the CP to change this.", !opts || !opts.hasOwnProperty("readOnly")); + this._readOnly = opts && (opts.readOnly !== undefined || !!opts.readOnly) || false; // TODO: Set always to `false` once this deprecation is gone. + } + + ComputedProperty.prototype = new properties.Descriptor(); + + var ComputedPropertyPrototype = ComputedProperty.prototype; + + /** + Properties are cacheable by default. Computed property will automatically + cache the return value of your function until one of the dependent keys changes. + + Call `volatile()` to set it into non-cached mode. When in this mode + the computed property will not automatically cache the return value. + + However, if a property is properly observable, there is no reason to disable + caching. + + @method cacheable + @param {Boolean} aFlag optional set to `false` to disable caching + @return {Ember.ComputedProperty} this + @chainable + @deprecated All computed properties are cacheble by default. Use `volatile()` instead to opt-out to caching. + */ + ComputedPropertyPrototype.cacheable = function (aFlag) { + Ember.deprecate("ComputedProperty.cacheable() is deprecated. All computed properties are cacheable by default."); + this._cacheable = aFlag !== false; + return this; + }; + + /** + Call on a computed property to set it into non-cached mode. When in this + mode the computed property will not automatically cache the return value. + + ```javascript + var outsideService = Ember.Object.extend({ + value: function() { + return OutsideService.getValue(); + }.property().volatile() + }).create(); + ``` + + @method volatile + @return {Ember.ComputedProperty} this + @chainable + */ + ComputedPropertyPrototype["volatile"] = function () { + this._cacheable = false; + return this; + }; + + /** + Call on a computed property to set it into read-only mode. When in this + mode the computed property will throw an error when set. + + ```javascript + var Person = Ember.Object.extend({ + guid: function() { + return 'guid-guid-guid'; + }.property().readOnly() + }); + + var person = Person.create(); + + person.set('guid', 'new-guid'); // will throw an exception + ``` + + @method readOnly + @return {Ember.ComputedProperty} this + @chainable + */ + ComputedPropertyPrototype.readOnly = function (readOnly) { + Ember.deprecate("Passing arguments to ComputedProperty.readOnly() is deprecated.", arguments.length === 0); + this._readOnly = readOnly === undefined || !!readOnly; // Force to true once this deprecation is gone + Ember.assert("Computed properties that define a setter using the new syntax cannot be read-only", !(this._readOnly && this._setter && this._setter !== this._getter)); + + return this; + }; + + /** + Sets the dependent keys on this computed property. Pass any number of + arguments containing key paths that this computed property depends on. + + ```javascript + var President = Ember.Object.extend({ + fullName: computed(function() { + return this.get('firstName') + ' ' + this.get('lastName'); + + // Tell Ember that this computed property depends on firstName + // and lastName + }).property('firstName', 'lastName') + }); + + var president = President.create({ + firstName: 'Barack', + lastName: 'Obama' + }); + + president.get('fullName'); // 'Barack Obama' + ``` + + @method property + @param {String} path* zero or more property paths + @return {Ember.ComputedProperty} this + @chainable + */ + ComputedPropertyPrototype.property = function () { + var args; + + var addArg = function (property) { + args.push(property); + }; + + args = []; + for (var i = 0, l = arguments.length; i < l; i++) { + expandProperties['default'](arguments[i], addArg); + } + + this._dependentKeys = args; + return this; + }; + + /** + In some cases, you may want to annotate computed properties with additional + metadata about how they function or what values they operate on. For example, + computed property functions may close over variables that are then no longer + available for introspection. + + You can pass a hash of these values to a computed property like this: + + ``` + person: function() { + var personId = this.get('personId'); + return App.Person.create({ id: personId }); + }.property().meta({ type: App.Person }) + ``` + + The hash that you pass to the `meta()` function will be saved on the + computed property descriptor under the `_meta` key. Ember runtime + exposes a public API for retrieving these values from classes, + via the `metaForProperty()` function. + + @method meta + @param {Hash} meta + @chainable + */ + + ComputedPropertyPrototype.meta = function (meta) { + if (arguments.length === 0) { + return this._meta || {}; + } else { + this._meta = meta; + return this; + } + }; + + /* impl descriptor API */ + ComputedPropertyPrototype.didChange = function (obj, keyName) { + // _suspended is set via a CP.set to ensure we don't clear + // the cached value set by the setter + if (this._cacheable && this._suspended !== obj) { + var meta = metaFor(obj); + if (meta.cache && meta.cache[keyName] !== undefined) { + meta.cache[keyName] = undefined; + dependent_keys.removeDependentKeys(this, obj, keyName, meta); + } + } + }; + + function finishChains(chainNodes) { + for (var i = 0, l = chainNodes.length; i < l; i++) { + chainNodes[i].didChange(null); + } + } + + /** + Access the value of the function backing the computed property. + If this property has already been cached, return the cached result. + Otherwise, call the function passing the property name as an argument. + + ```javascript + var Person = Ember.Object.extend({ + fullName: function(keyName) { + // the keyName parameter is 'fullName' in this case. + return this.get('firstName') + ' ' + this.get('lastName'); + }.property('firstName', 'lastName') + }); + + + var tom = Person.create({ + firstName: 'Tom', + lastName: 'Dale' + }); + + tom.get('fullName') // 'Tom Dale' + ``` + + @method get + @param {String} keyName The key being accessed. + @return {Object} The return value of the function backing the CP. + */ + ComputedPropertyPrototype.get = function (obj, keyName) { + var ret, cache, meta, chainNodes; + if (this._cacheable) { + meta = metaFor(obj); + cache = meta.cache; + + var result = cache && cache[keyName]; + + if (result === UNDEFINED) { + return undefined; + } else if (result !== undefined) { + return result; + } + + ret = this._getter.call(obj, keyName); + cache = meta.cache; + if (!cache) { + cache = meta.cache = {}; + } + if (ret === undefined) { + cache[keyName] = UNDEFINED; + } else { + cache[keyName] = ret; + } + + chainNodes = meta.chainWatchers && meta.chainWatchers[keyName]; + if (chainNodes) { + finishChains(chainNodes); + } + dependent_keys.addDependentKeys(this, obj, keyName, meta); + } else { + ret = this._getter.call(obj, keyName); + } + return ret; + }; + + /** + Set the value of a computed property. If the function that backs your + computed property does not accept arguments then the default action for + setting would be to define the property on the current object, and set + the value of the property to the value being set. + + Generally speaking if you intend for your computed property to be set + your backing function should accept either two or three arguments. + + ```javascript + var Person = Ember.Object.extend({ + // these will be supplied by `create` + firstName: null, + lastName: null, + + fullName: function(key, value, oldValue) { + // getter + if (arguments.length === 1) { + var firstName = this.get('firstName'); + var lastName = this.get('lastName'); + + return firstName + ' ' + lastName; + + // setter + } else { + var name = value.split(' '); + + this.set('firstName', name[0]); + this.set('lastName', name[1]); + + return value; + } + }.property('firstName', 'lastName') + }); + + var person = Person.create(); + + person.set('fullName', 'Peter Wagenet'); + person.get('firstName'); // 'Peter' + person.get('lastName'); // 'Wagenet' + ``` + + @method set + @param {String} keyName The key being accessed. + @param {Object} newValue The new value being assigned. + @param {String} oldValue The old value being replaced. + @return {Object} The return value of the function backing the CP. + */ + ComputedPropertyPrototype.set = function computedPropertySetWithSuspend(obj, keyName, value) { + var oldSuspended = this._suspended; + + this._suspended = obj; + + try { + this._set(obj, keyName, value); + } finally { + this._suspended = oldSuspended; + } + }; + + ComputedPropertyPrototype._set = function computedPropertySet(obj, keyName, value) { + var cacheable = this._cacheable; + var setter = this._setter; + var meta = metaFor(obj, cacheable); + var cache = meta.cache; + var hadCachedValue = false; + + var cachedValue, ret; + + if (this._readOnly) { + throw new EmberError['default']("Cannot set read-only property \"" + keyName + "\" on object: " + utils.inspect(obj)); + } + + if (cacheable && cache && cache[keyName] !== undefined) { + if (cache[keyName] !== UNDEFINED) { + cachedValue = cache[keyName]; + } + + hadCachedValue = true; + } + + if (!setter) { + properties.defineProperty(obj, keyName, null, cachedValue); + property_set.set(obj, keyName, value); + return; + } else if (setter.__ember_arity === 2) { + // Is there any way of deprecate this in a sensitive way? + // Maybe now that getters and setters are the prefered options we can.... + ret = setter.call(obj, keyName, value); + } else { + ret = setter.call(obj, keyName, value, cachedValue); + } + + if (hadCachedValue && cachedValue === ret) { + return; + } + + var watched = meta.watching[keyName]; + if (watched) { + property_events.propertyWillChange(obj, keyName); + } + + if (hadCachedValue) { + cache[keyName] = undefined; + } + + if (cacheable) { + if (!hadCachedValue) { + dependent_keys.addDependentKeys(this, obj, keyName, meta); + } + if (!cache) { + cache = meta.cache = {}; + } + if (ret === undefined) { + cache[keyName] = UNDEFINED; + } else { + cache[keyName] = ret; + } + } + + if (watched) { + property_events.propertyDidChange(obj, keyName); + } + + return ret; + }; + + /* called before property is overridden */ + ComputedPropertyPrototype.teardown = function (obj, keyName) { + var meta = metaFor(obj); + + if (meta.cache) { + if (keyName in meta.cache) { + dependent_keys.removeDependentKeys(this, obj, keyName, meta); + } + + if (this._cacheable) { + delete meta.cache[keyName]; + } + } + + return null; // no value to restore + }; + + /** + This helper returns a new property descriptor that wraps the passed + computed property function. You can use this helper to define properties + with mixins or via `Ember.defineProperty()`. + + The function you pass will be used to both get and set property values. + The function should accept two parameters, key and value. If value is not + undefined you should set the value first. In either case return the + current value of the property. + + A computed property defined in this way might look like this: + + ```js + var Person = Ember.Object.extend({ + firstName: 'Betty', + lastName: 'Jones', + + fullName: Ember.computed('firstName', 'lastName', function(key, value) { + return this.get('firstName') + ' ' + this.get('lastName'); + }) + }); + + var client = Person.create(); + + client.get('fullName'); // 'Betty Jones' + + client.set('lastName', 'Fuller'); + client.get('fullName'); // 'Betty Fuller' + ``` + + _Note: This is the preferred way to define computed properties when writing third-party + libraries that depend on or use Ember, since there is no guarantee that the user + will have prototype extensions enabled._ + + You might use this method if you disabled + [Prototype Extensions](http://emberjs.com/guides/configuring-ember/disabling-prototype-extensions/). + The alternative syntax might look like this + (if prototype extensions are enabled, which is the default behavior): + + ```js + fullName: function () { + return this.get('firstName') + ' ' + this.get('lastName'); + }.property('firstName', 'lastName') + ``` + + @class computed + @namespace Ember + @constructor + @static + @param {String} [dependentKeys*] Optional dependent keys that trigger this computed property. + @param {Function} func The computed property function. + @return {Ember.ComputedProperty} property descriptor instance + */ + function computed(func) { + var args; + + if (arguments.length > 1) { + args = [].slice.call(arguments); + func = args.pop(); + } + + var cp = new ComputedProperty(func); + // jscs:disable + + if (args) { + cp.property.apply(cp, args); + } + + return cp; + } + + /** + Returns the cached value for a property, if one exists. + This can be useful for peeking at the value of a computed + property that is generated lazily, without accidentally causing + it to be created. + + @method cacheFor + @for Ember + @param {Object} obj the object whose property you want to check + @param {String} key the name of the property whose cached value you want + to return + @return {Object} the cached value + */ + function cacheFor(obj, key) { + var meta = obj["__ember_meta__"]; + var cache = meta && meta.cache; + var ret = cache && cache[key]; + + if (ret === UNDEFINED) { + return undefined; + } + return ret; + } + + cacheFor.set = function (cache, key, value) { + if (value === undefined) { + cache[key] = UNDEFINED; + } else { + cache[key] = value; + } + }; + + cacheFor.get = function (cache, key) { + var ret = cache[key]; + if (ret === UNDEFINED) { + return undefined; + } + return ret; + }; + + cacheFor.remove = function (cache, key) { + cache[key] = undefined; + }; + +}); +enifed('ember-metal/computed_macros', ['exports', 'ember-metal/core', 'ember-metal/property_get', 'ember-metal/property_set', 'ember-metal/computed', 'ember-metal/is_empty', 'ember-metal/is_none', 'ember-metal/alias'], function (exports, Ember, property_get, property_set, computed, isEmpty, isNone, alias) { + + 'use strict'; + + exports.empty = empty; + exports.notEmpty = notEmpty; + exports.none = none; + exports.not = not; + exports.bool = bool; + exports.match = match; + exports.equal = equal; + exports.gt = gt; + exports.gte = gte; + exports.lt = lt; + exports.lte = lte; + exports.oneWay = oneWay; + exports.readOnly = readOnly; + exports.defaultTo = defaultTo; + exports.deprecatingAlias = deprecatingAlias; + + function getProperties(self, propertyNames) { + var ret = {}; + for (var i = 0; i < propertyNames.length; i++) { + ret[propertyNames[i]] = property_get.get(self, propertyNames[i]); + } + return ret; + } + + function generateComputedWithProperties(macro) { + return function () { + for (var _len = arguments.length, properties = Array(_len), _key = 0; _key < _len; _key++) { + properties[_key] = arguments[_key]; + } + + var computedFunc = computed.computed(function () { + return macro.apply(this, [getProperties(this, properties)]); + }); + + return computedFunc.property.apply(computedFunc, properties); + }; + } + + /** + A computed property that returns true if the value of the dependent + property is null, an empty string, empty array, or empty function. + + Example + + ```javascript + var ToDoList = Ember.Object.extend({ + isDone: Ember.computed.empty('todos') + }); + + var todoList = ToDoList.create({ + todos: ['Unit Test', 'Documentation', 'Release'] + }); + + todoList.get('isDone'); // false + todoList.get('todos').clear(); + todoList.get('isDone'); // true + ``` + + @since 1.6.0 + @method empty + @for Ember.computed + @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which negate + the original value for property + */ + function empty(dependentKey) { + return computed.computed(dependentKey + ".length", function () { + return isEmpty['default'](property_get.get(this, dependentKey)); + }); + } + + /** + A computed property that returns true if the value of the dependent + property is NOT null, an empty string, empty array, or empty function. + + Example + + ```javascript + var Hamster = Ember.Object.extend({ + hasStuff: Ember.computed.notEmpty('backpack') + }); + + var hamster = Hamster.create({ backpack: ['Food', 'Sleeping Bag', 'Tent'] }); + + hamster.get('hasStuff'); // true + hamster.get('backpack').clear(); // [] + hamster.get('hasStuff'); // false + ``` + + @method notEmpty + @for Ember.computed + @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which returns true if + original value for property is not empty. + */ + function notEmpty(dependentKey) { + return computed.computed(dependentKey + ".length", function () { + return !isEmpty['default'](property_get.get(this, dependentKey)); + }); + } + + /** + A computed property that returns true if the value of the dependent + property is null or undefined. This avoids errors from JSLint complaining + about use of ==, which can be technically confusing. + + Example + + ```javascript + var Hamster = Ember.Object.extend({ + isHungry: Ember.computed.none('food') + }); + + var hamster = Hamster.create(); + + hamster.get('isHungry'); // true + hamster.set('food', 'Banana'); + hamster.get('isHungry'); // false + hamster.set('food', null); + hamster.get('isHungry'); // true + ``` + + @method none + @for Ember.computed + @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which + returns true if original value for property is null or undefined. + */ + function none(dependentKey) { + return computed.computed(dependentKey, function () { + return isNone['default'](property_get.get(this, dependentKey)); + }); + } + + /** + A computed property that returns the inverse boolean value + of the original value for the dependent property. + + Example + + ```javascript + var User = Ember.Object.extend({ + isAnonymous: Ember.computed.not('loggedIn') + }); + + var user = User.create({loggedIn: false}); + + user.get('isAnonymous'); // true + user.set('loggedIn', true); + user.get('isAnonymous'); // false + ``` + + @method not + @for Ember.computed + @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which returns + inverse of the original value for property + */ + function not(dependentKey) { + return computed.computed(dependentKey, function () { + return !property_get.get(this, dependentKey); + }); + } + + /** + A computed property that converts the provided dependent property + into a boolean value. + + ```javascript + var Hamster = Ember.Object.extend({ + hasBananas: Ember.computed.bool('numBananas') + }); + + var hamster = Hamster.create(); + + hamster.get('hasBananas'); // false + hamster.set('numBananas', 0); + hamster.get('hasBananas'); // false + hamster.set('numBananas', 1); + hamster.get('hasBananas'); // true + hamster.set('numBananas', null); + hamster.get('hasBananas'); // false + ``` + + @method bool + @for Ember.computed + @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which converts + to boolean the original value for property + */ + function bool(dependentKey) { + return computed.computed(dependentKey, function () { + return !!property_get.get(this, dependentKey); + }); + } + + /** + A computed property which matches the original value for the + dependent property against a given RegExp, returning `true` + if they values matches the RegExp and `false` if it does not. + + Example + + ```javascript + var User = Ember.Object.extend({ + hasValidEmail: Ember.computed.match('email', /^.+@.+\..+$/) + }); + + var user = User.create({loggedIn: false}); + + user.get('hasValidEmail'); // false + user.set('email', ''); + user.get('hasValidEmail'); // false + user.set('email', 'ember_hamster@example.com'); + user.get('hasValidEmail'); // true + ``` + + @method match + @for Ember.computed + @param {String} dependentKey + @param {RegExp} regexp + @return {Ember.ComputedProperty} computed property which match + the original value for property against a given RegExp + */ + function match(dependentKey, regexp) { + return computed.computed(dependentKey, function () { + var value = property_get.get(this, dependentKey); + + return typeof value === "string" ? regexp.test(value) : false; + }); + } + + /** + A computed property that returns true if the provided dependent property + is equal to the given value. + + Example + + ```javascript + var Hamster = Ember.Object.extend({ + napTime: Ember.computed.equal('state', 'sleepy') + }); + + var hamster = Hamster.create(); + + hamster.get('napTime'); // false + hamster.set('state', 'sleepy'); + hamster.get('napTime'); // true + hamster.set('state', 'hungry'); + hamster.get('napTime'); // false + ``` + + @method equal + @for Ember.computed + @param {String} dependentKey + @param {String|Number|Object} value + @return {Ember.ComputedProperty} computed property which returns true if + the original value for property is equal to the given value. + */ + function equal(dependentKey, value) { + return computed.computed(dependentKey, function () { + return property_get.get(this, dependentKey) === value; + }); + } + + /** + A computed property that returns true if the provided dependent property + is greater than the provided value. + + Example + + ```javascript + var Hamster = Ember.Object.extend({ + hasTooManyBananas: Ember.computed.gt('numBananas', 10) + }); + + var hamster = Hamster.create(); + + hamster.get('hasTooManyBananas'); // false + hamster.set('numBananas', 3); + hamster.get('hasTooManyBananas'); // false + hamster.set('numBananas', 11); + hamster.get('hasTooManyBananas'); // true + ``` + + @method gt + @for Ember.computed + @param {String} dependentKey + @param {Number} value + @return {Ember.ComputedProperty} computed property which returns true if + the original value for property is greater than given value. + */ + function gt(dependentKey, value) { + return computed.computed(dependentKey, function () { + return property_get.get(this, dependentKey) > value; + }); + } + + /** + A computed property that returns true if the provided dependent property + is greater than or equal to the provided value. + + Example + + ```javascript + var Hamster = Ember.Object.extend({ + hasTooManyBananas: Ember.computed.gte('numBananas', 10) + }); + + var hamster = Hamster.create(); + + hamster.get('hasTooManyBananas'); // false + hamster.set('numBananas', 3); + hamster.get('hasTooManyBananas'); // false + hamster.set('numBananas', 10); + hamster.get('hasTooManyBananas'); // true + ``` + + @method gte + @for Ember.computed + @param {String} dependentKey + @param {Number} value + @return {Ember.ComputedProperty} computed property which returns true if + the original value for property is greater or equal then given value. + */ + function gte(dependentKey, value) { + return computed.computed(dependentKey, function () { + return property_get.get(this, dependentKey) >= value; + }); + } + + /** + A computed property that returns true if the provided dependent property + is less than the provided value. + + Example + + ```javascript + var Hamster = Ember.Object.extend({ + needsMoreBananas: Ember.computed.lt('numBananas', 3) + }); + + var hamster = Hamster.create(); + + hamster.get('needsMoreBananas'); // true + hamster.set('numBananas', 3); + hamster.get('needsMoreBananas'); // false + hamster.set('numBananas', 2); + hamster.get('needsMoreBananas'); // true + ``` + + @method lt + @for Ember.computed + @param {String} dependentKey + @param {Number} value + @return {Ember.ComputedProperty} computed property which returns true if + the original value for property is less then given value. + */ + function lt(dependentKey, value) { + return computed.computed(dependentKey, function () { + return property_get.get(this, dependentKey) < value; + }); + } + + /** + A computed property that returns true if the provided dependent property + is less than or equal to the provided value. + + Example + + ```javascript + var Hamster = Ember.Object.extend({ + needsMoreBananas: Ember.computed.lte('numBananas', 3) + }); + + var hamster = Hamster.create(); + + hamster.get('needsMoreBananas'); // true + hamster.set('numBananas', 5); + hamster.get('needsMoreBananas'); // false + hamster.set('numBananas', 3); + hamster.get('needsMoreBananas'); // true + ``` + + @method lte + @for Ember.computed + @param {String} dependentKey + @param {Number} value + @return {Ember.ComputedProperty} computed property which returns true if + the original value for property is less or equal than given value. + */ + function lte(dependentKey, value) { + return computed.computed(dependentKey, function () { + return property_get.get(this, dependentKey) <= value; + }); + } + + /** + A computed property that performs a logical `and` on the + original values for the provided dependent properties. + + Example + + ```javascript + var Hamster = Ember.Object.extend({ + readyForCamp: Ember.computed.and('hasTent', 'hasBackpack') + }); + + var hamster = Hamster.create(); + + hamster.get('readyForCamp'); // false + hamster.set('hasTent', true); + hamster.get('readyForCamp'); // false + hamster.set('hasBackpack', true); + hamster.get('readyForCamp'); // true + hamster.set('hasBackpack', 'Yes'); + hamster.get('readyForCamp'); // 'Yes' + ``` + + @method and + @for Ember.computed + @param {String} dependentKey* + @return {Ember.ComputedProperty} computed property which performs + a logical `and` on the values of all the original values for properties. + */ + var and = generateComputedWithProperties(function (properties) { + var value; + for (var key in properties) { + value = properties[key]; + if (properties.hasOwnProperty(key) && !value) { + return false; + } + } + return value; + }); + + var or = generateComputedWithProperties(function (properties) { + for (var key in properties) { + if (properties.hasOwnProperty(key) && properties[key]) { + return properties[key]; + } + } + return false; + }); + + var any = generateComputedWithProperties(function (properties) { + for (var key in properties) { + if (properties.hasOwnProperty(key) && properties[key]) { + return properties[key]; + } + } + return null; + }); + + var collect = generateComputedWithProperties(function (properties) { + var res = Ember['default'].A(); + for (var key in properties) { + if (properties.hasOwnProperty(key)) { + if (isNone['default'](properties[key])) { + res.push(null); + } else { + res.push(properties[key]); + } + } + } + return res; + }); + + function oneWay(dependentKey) { + return alias['default'](dependentKey).oneWay(); + } + + /** + This is a more semantically meaningful alias of `computed.oneWay`, + whose name is somewhat ambiguous as to which direction the data flows. + + @method reads + @for Ember.computed + @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which creates a + one way computed property to the original value for property. + */ + + /** + Where `computed.oneWay` provides oneWay bindings, `computed.readOnly` provides + a readOnly one way binding. Very often when using `computed.oneWay` one does + not also want changes to propagate back up, as they will replace the value. + + This prevents the reverse flow, and also throws an exception when it occurs. + + Example + + ```javascript + var User = Ember.Object.extend({ + firstName: null, + lastName: null, + nickName: Ember.computed.readOnly('firstName') + }); + + var teddy = User.create({ + firstName: 'Teddy', + lastName: 'Zeenny' + }); + + teddy.get('nickName'); // 'Teddy' + teddy.set('nickName', 'TeddyBear'); // throws Exception + // throw new Ember.Error('Cannot Set: nickName on: ' );` + teddy.get('firstName'); // 'Teddy' + ``` + + @method readOnly + @for Ember.computed + @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which creates a + one way computed property to the original value for property. + @since 1.5.0 + */ + function readOnly(dependentKey) { + return alias['default'](dependentKey).readOnly(); + } + + /** + A computed property that acts like a standard getter and setter, + but returns the value at the provided `defaultPath` if the + property itself has not been set to a value + + Example + + ```javascript + var Hamster = Ember.Object.extend({ + wishList: Ember.computed.defaultTo('favoriteFood') + }); + + var hamster = Hamster.create({ favoriteFood: 'Banana' }); + + hamster.get('wishList'); // 'Banana' + hamster.set('wishList', 'More Unit Tests'); + hamster.get('wishList'); // 'More Unit Tests' + hamster.get('favoriteFood'); // 'Banana' + ``` + + @method defaultTo + @for Ember.computed + @param {String} defaultPath + @return {Ember.ComputedProperty} computed property which acts like + a standard getter and setter, but defaults to the value from `defaultPath`. + @deprecated Use `Ember.computed.oneWay` or custom CP with default instead. + */ + function defaultTo(defaultPath) { + return computed.computed({ + get: function (key) { + Ember['default'].deprecate("Usage of Ember.computed.defaultTo is deprecated, use `Ember.computed.oneWay` instead."); + return property_get.get(this, defaultPath); + }, + + set: function (key, newValue, cachedValue) { + Ember['default'].deprecate("Usage of Ember.computed.defaultTo is deprecated, use `Ember.computed.oneWay` instead."); + return newValue != null ? newValue : property_get.get(this, defaultPath); + } + }); + } + + /** + Creates a new property that is an alias for another property + on an object. Calls to `get` or `set` this property behave as + though they were called on the original property, but also + print a deprecation warning. + + @method deprecatingAlias + @for Ember.computed + @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which creates an + alias with a deprecation to the original value for property. + @since 1.7.0 + */ + function deprecatingAlias(dependentKey) { + return computed.computed(dependentKey, { + get: function (key) { + Ember['default'].deprecate("Usage of `" + key + "` is deprecated, use `" + dependentKey + "` instead."); + return property_get.get(this, dependentKey); + }, + set: function (key, value) { + Ember['default'].deprecate("Usage of `" + key + "` is deprecated, use `" + dependentKey + "` instead."); + property_set.set(this, dependentKey, value); + return value; + } + }); + } + + exports.and = and; + exports.or = or; + exports.any = any; + exports.collect = collect; + +}); +enifed('ember-metal/core', ['exports'], function (exports) { + + 'use strict'; + + exports.K = K; + + /*globals Ember:true,ENV,EmberENV */ + + /** + @module ember + @submodule ember-metal + */ + + /** + All Ember methods and functions are defined inside of this namespace. You + generally should not add new properties to this namespace as it may be + overwritten by future versions of Ember. + + You can also use the shorthand `Em` instead of `Ember`. + + Ember-Runtime is a framework that provides core functions for Ember including + cross-platform functions, support for property observing and objects. Its + focus is on small size and performance. You can use this in place of or + along-side other cross-platform libraries such as jQuery. + + The core Runtime framework is based on the jQuery API with a number of + performance optimizations. + + @class Ember + @static + @version 1.12.1 + */ + + if ('undefined' === typeof Ember) { + // Create core object. Make it act like an instance of Ember.Namespace so that + // objects assigned to it are given a sane string representation. + Ember = {}; + } + + // Default imports, exports and lookup to the global object; + var global = mainContext || {}; // jshint ignore:line + Ember.imports = Ember.imports || global; + Ember.lookup = Ember.lookup || global; + var emExports = Ember.exports = Ember.exports || global; + + // aliases needed to keep minifiers from removing the global context + emExports.Em = emExports.Ember = Ember; + + // Make sure these are set whether Ember was already defined or not + + Ember.isNamespace = true; + + Ember.toString = function () { + return 'Ember'; + }; + + /** + @property VERSION + @type String + @default '1.12.1' + @static + */ + Ember.VERSION = '1.12.1'; + + /** + Standard environmental variables. You can define these in a global `EmberENV` + variable before loading Ember to control various configuration settings. + + For backwards compatibility with earlier versions of Ember the global `ENV` + variable will be used if `EmberENV` is not defined. + + @property ENV + @type Hash + */ + + if (Ember.ENV) { + // do nothing if Ember.ENV is already setup + Ember.assert('Ember.ENV should be an object.', 'object' !== typeof Ember.ENV); + } else if ('undefined' !== typeof EmberENV) { + Ember.ENV = EmberENV; + } else if ('undefined' !== typeof ENV) { + Ember.ENV = ENV; + } else { + Ember.ENV = {}; + } + + Ember.config = Ember.config || {}; + + // We disable the RANGE API by default for performance reasons + if ('undefined' === typeof Ember.ENV.DISABLE_RANGE_API) { + Ember.ENV.DISABLE_RANGE_API = true; + } + + /** + Hash of enabled Canary features. Add to this before creating your application. + + You can also define `EmberENV.FEATURES` if you need to enable features flagged at runtime. + + @class FEATURES + @namespace Ember + @static + @since 1.1.0 + */ + Ember.FEATURES = { 'features-stripped-test': false, 'ember-routing-named-substates': true, 'mandatory-setter': true, 'ember-htmlbars-component-generation': false, 'ember-htmlbars-component-helper': true, 'ember-htmlbars-inline-if-helper': true, 'ember-htmlbars-attribute-syntax': true, 'ember-routing-transitioning-classes': true, 'new-computed-syntax': true, 'ember-testing-checkbox-helpers': false, 'ember-metal-stream': false, 'ember-application-instance-initializers': true, 'ember-application-initializer-context': true, 'ember-router-willtransition': true, 'ember-application-visit': false, 'ember-views-component-block-info': false, 'ember-routing-core-outlet': false, 'ember-libraries-isregistered': false }; //jshint ignore:line + + if (Ember.ENV.FEATURES) { + for (var feature in Ember.ENV.FEATURES) { + if (Ember.ENV.FEATURES.hasOwnProperty(feature)) { + Ember.FEATURES[feature] = Ember.ENV.FEATURES[feature]; + } + } + } + + /** + Test that a feature is enabled. Parsed by Ember's build tools to leave + experimental features out of beta/stable builds. + + You can define the following configuration options: + + * `EmberENV.ENABLE_ALL_FEATURES` - force all features to be enabled. + * `EmberENV.ENABLE_OPTIONAL_FEATURES` - enable any features that have not been explicitly + enabled/disabled. + + @method isEnabled + @param {String} feature + @return {Boolean} + @for Ember.FEATURES + @since 1.1.0 + */ + + Ember.FEATURES.isEnabled = function (feature) { + var featureValue = Ember.FEATURES[feature]; + + if (Ember.ENV.ENABLE_ALL_FEATURES) { + return true; + } else if (featureValue === true || featureValue === false || featureValue === undefined) { + return featureValue; + } else if (Ember.ENV.ENABLE_OPTIONAL_FEATURES) { + return true; + } else { + return false; + } + }; + + // .......................................................... + // BOOTSTRAP + // + + /** + Determines whether Ember should enhance some built-in object prototypes to + provide a more friendly API. If enabled, a few methods will be added to + `Function`, `String`, and `Array`. `Object.prototype` will not be enhanced, + which is the one that causes most trouble for people. + + In general we recommend leaving this option set to true since it rarely + conflicts with other code. If you need to turn it off however, you can + define an `EmberENV.EXTEND_PROTOTYPES` config to disable it. + + @property EXTEND_PROTOTYPES + @type Boolean + @default true + @for Ember + */ + Ember.EXTEND_PROTOTYPES = Ember.ENV.EXTEND_PROTOTYPES; + + if (typeof Ember.EXTEND_PROTOTYPES === 'undefined') { + Ember.EXTEND_PROTOTYPES = true; + } + + /** + Determines whether Ember logs a full stack trace during deprecation warnings + + @property LOG_STACKTRACE_ON_DEPRECATION + @type Boolean + @default true + */ + Ember.LOG_STACKTRACE_ON_DEPRECATION = Ember.ENV.LOG_STACKTRACE_ON_DEPRECATION !== false; + + /** + Determines whether Ember should add ECMAScript 5 Array shims to older browsers. + + @property SHIM_ES5 + @type Boolean + @default Ember.EXTEND_PROTOTYPES + */ + Ember.SHIM_ES5 = Ember.ENV.SHIM_ES5 === false ? false : Ember.EXTEND_PROTOTYPES; + + /** + Determines whether Ember logs info about version of used libraries + + @property LOG_VERSION + @type Boolean + @default true + */ + Ember.LOG_VERSION = Ember.ENV.LOG_VERSION === false ? false : true; + + /** + Empty function. Useful for some operations. Always returns `this`. + + @method K + @private + @return {Object} + */ + function K() { + return this; + } + Ember.K = K; + //TODO: ES6 GLOBAL TODO + + // Stub out the methods defined by the ember-debug package in case it's not loaded + + if ('undefined' === typeof Ember.assert) { + Ember.assert = K; + } + if ('undefined' === typeof Ember.warn) { + Ember.warn = K; + } + if ('undefined' === typeof Ember.debug) { + Ember.debug = K; + } + if ('undefined' === typeof Ember.runInDebug) { + Ember.runInDebug = K; + } + if ('undefined' === typeof Ember.deprecate) { + Ember.deprecate = K; + } + if ('undefined' === typeof Ember.deprecateFunc) { + Ember.deprecateFunc = function (_, func) { + return func; + }; + } + + exports['default'] = Ember; + +}); +enifed('ember-metal/dependent_keys', ['exports', 'ember-metal/platform/create', 'ember-metal/watching'], function (exports, o_create, watching) { + + + exports.addDependentKeys = addDependentKeys; + exports.removeDependentKeys = removeDependentKeys; + + "REMOVE_USE_STRICT: true"; /** + @module ember-metal + */ + + // .......................................................... + // DEPENDENT KEYS + // + + // data structure: + // meta.deps = { + // 'depKey': { + // 'keyName': count, + // } + // } + + /* + This function returns a map of unique dependencies for a + given object and key. + */ + function keysForDep(depsMeta, depKey) { + var keys = depsMeta[depKey]; + if (!keys) { + // if there are no dependencies yet for a the given key + // create a new empty list of dependencies for the key + keys = depsMeta[depKey] = {}; + } else if (!depsMeta.hasOwnProperty(depKey)) { + // otherwise if the dependency list is inherited from + // a superclass, clone the hash + keys = depsMeta[depKey] = o_create['default'](keys); + } + return keys; + } + + function metaForDeps(meta) { + return keysForDep(meta, "deps"); + } + function addDependentKeys(desc, obj, keyName, meta) { + // the descriptor has a list of dependent keys, so + // add all of its dependent keys. + var depsMeta, idx, len, depKey, keys; + var depKeys = desc._dependentKeys; + if (!depKeys) { + return; + } + + depsMeta = metaForDeps(meta); + + for (idx = 0, len = depKeys.length; idx < len; idx++) { + depKey = depKeys[idx]; + // Lookup keys meta for depKey + keys = keysForDep(depsMeta, depKey); + // Increment the number of times depKey depends on keyName. + keys[keyName] = (keys[keyName] || 0) + 1; + // Watch the depKey + watching.watch(obj, depKey, meta); + } + } + + function removeDependentKeys(desc, obj, keyName, meta) { + // the descriptor has a list of dependent keys, so + // remove all of its dependent keys. + var depKeys = desc._dependentKeys; + var depsMeta, idx, len, depKey, keys; + if (!depKeys) { + return; + } + + depsMeta = metaForDeps(meta); + + for (idx = 0, len = depKeys.length; idx < len; idx++) { + depKey = depKeys[idx]; + // Lookup keys meta for depKey + keys = keysForDep(depsMeta, depKey); + // Decrement the number of times depKey depends on keyName. + keys[keyName] = (keys[keyName] || 0) - 1; + // Unwatch the depKey + watching.unwatch(obj, depKey, meta); + } + } + +}); +enifed('ember-metal/deprecate_property', ['exports', 'ember-metal/core', 'ember-metal/platform/define_property', 'ember-metal/properties', 'ember-metal/property_get', 'ember-metal/property_set'], function (exports, Ember, define_property, properties, property_get, property_set) { + + 'use strict'; + + exports.deprecateProperty = deprecateProperty; + + function deprecateProperty(object, deprecatedKey, newKey) { + function deprecate() { + Ember['default'].deprecate("Usage of `" + deprecatedKey + "` is deprecated, use `" + newKey + "` instead."); + } + + if (define_property.hasPropertyAccessors) { + properties.defineProperty(object, deprecatedKey, { + configurable: true, + enumerable: false, + set: function (value) { + deprecate(); + property_set.set(this, newKey, value); + }, + get: function () { + deprecate(); + return property_get.get(this, newKey); + } + }); + } + } + +}); +enifed('ember-metal/dictionary', ['exports', 'ember-metal/platform/create'], function (exports, create) { + + 'use strict'; + + + exports['default'] = makeDictionary; + function makeDictionary(parent) { + var dict = create['default'](parent); + dict['_dict'] = null; + delete dict['_dict']; + return dict; + } + +}); +enifed('ember-metal/enumerable_utils', ['exports', 'ember-metal/array'], function (exports, ember_metal__array) { + + 'use strict'; + + exports.map = map; + exports.forEach = forEach; + exports.filter = filter; + exports.indexOf = indexOf; + exports.indexesOf = indexesOf; + exports.addObject = addObject; + exports.removeObject = removeObject; + exports._replace = _replace; + exports.replace = replace; + exports.intersection = intersection; + + var splice = Array.prototype.splice; + + /** + * Defines some convenience methods for working with Enumerables. + * `Ember.EnumerableUtils` uses `Ember.ArrayPolyfills` when necessary. + * + * @class EnumerableUtils + * @namespace Ember + * @static + * */ + + /** + * Calls the map function on the passed object with a specified callback. This + * uses `Ember.ArrayPolyfill`'s-map method when necessary. + * + * @method map + * @param {Object} obj The object that should be mapped + * @param {Function} callback The callback to execute + * @param {Object} thisArg Value to use as this when executing *callback* + * + * @return {Array} An array of mapped values. + */ + function map(obj, callback, thisArg) { + return obj.map ? obj.map(callback, thisArg) : ember_metal__array.map.call(obj, callback, thisArg); + } + + /** + * Calls the forEach function on the passed object with a specified callback. This + * uses `Ember.ArrayPolyfill`'s-forEach method when necessary. + * + * @method forEach + * @param {Object} obj The object to call forEach on + * @param {Function} callback The callback to execute + * @param {Object} thisArg Value to use as this when executing *callback* + * + */ + function forEach(obj, callback, thisArg) { + return obj.forEach ? obj.forEach(callback, thisArg) : ember_metal__array.forEach.call(obj, callback, thisArg); + } + + /** + * Calls the filter function on the passed object with a specified callback. This + * uses `Ember.ArrayPolyfill`'s-filter method when necessary. + * + * @method filter + * @param {Object} obj The object to call filter on + * @param {Function} callback The callback to execute + * @param {Object} thisArg Value to use as this when executing *callback* + * + * @return {Array} An array containing the filtered values + * @since 1.4.0 + */ + function filter(obj, callback, thisArg) { + return obj.filter ? obj.filter(callback, thisArg) : ember_metal__array.filter.call(obj, callback, thisArg); + } + + /** + * Calls the indexOf function on the passed object with a specified callback. This + * uses `Ember.ArrayPolyfill`'s-indexOf method when necessary. + * + * @method indexOf + * @param {Object} obj The object to call indexOn on + * @param {Function} callback The callback to execute + * @param {Object} index The index to start searching from + * + */ + function indexOf(obj, element, index) { + return obj.indexOf ? obj.indexOf(element, index) : ember_metal__array.indexOf.call(obj, element, index); + } + + /** + * Returns an array of indexes of the first occurrences of the passed elements + * on the passed object. + * + * ```javascript + * var array = [1, 2, 3, 4, 5]; + * Ember.EnumerableUtils.indexesOf(array, [2, 5]); // [1, 4] + * + * var fubar = "Fubarr"; + * Ember.EnumerableUtils.indexesOf(fubar, ['b', 'r']); // [2, 4] + * ``` + * + * @method indexesOf + * @param {Object} obj The object to check for element indexes + * @param {Array} elements The elements to search for on *obj* + * + * @return {Array} An array of indexes. + * + */ + function indexesOf(obj, elements) { + return elements === undefined ? [] : map(elements, function (item) { + return indexOf(obj, item); + }); + } + + /** + * Adds an object to an array. If the array already includes the object this + * method has no effect. + * + * @method addObject + * @param {Array} array The array the passed item should be added to + * @param {Object} item The item to add to the passed array + * + * @return 'undefined' + */ + function addObject(array, item) { + var index = indexOf(array, item); + if (index === -1) { + array.push(item); + } + } + + /** + * Removes an object from an array. If the array does not contain the passed + * object this method has no effect. + * + * @method removeObject + * @param {Array} array The array to remove the item from. + * @param {Object} item The item to remove from the passed array. + * + * @return 'undefined' + */ + function removeObject(array, item) { + var index = indexOf(array, item); + if (index !== -1) { + array.splice(index, 1); + } + } + + function _replace(array, idx, amt, objects) { + var args = [].concat(objects); + var ret = []; + // https://code.google.com/p/chromium/issues/detail?id=56588 + var size = 60000; + var start = idx; + var ends = amt; + var count, chunk; + + while (args.length) { + count = ends > size ? size : ends; + if (count <= 0) { + count = 0; + } + + chunk = args.splice(0, size); + chunk = [start, count].concat(chunk); + + start += size; + ends -= count; + + ret = ret.concat(splice.apply(array, chunk)); + } + return ret; + } + + /** + * Replaces objects in an array with the passed objects. + * + * ```javascript + * var array = [1,2,3]; + * Ember.EnumerableUtils.replace(array, 1, 2, [4, 5]); // [1, 4, 5] + * + * var array = [1,2,3]; + * Ember.EnumerableUtils.replace(array, 1, 1, [4, 5]); // [1, 4, 5, 3] + * + * var array = [1,2,3]; + * Ember.EnumerableUtils.replace(array, 10, 1, [4, 5]); // [1, 2, 3, 4, 5] + * ``` + * + * @method replace + * @param {Array} array The array the objects should be inserted into. + * @param {Number} idx Starting index in the array to replace. If *idx* >= + * length, then append to the end of the array. + * @param {Number} amt Number of elements that should be removed from the array, + * starting at *idx* + * @param {Array} objects An array of zero or more objects that should be + * inserted into the array at *idx* + * + * @return {Array} The modified array. + */ + function replace(array, idx, amt, objects) { + if (array.replace) { + return array.replace(idx, amt, objects); + } else { + return _replace(array, idx, amt, objects); + } + } + + /** + * Calculates the intersection of two arrays. This method returns a new array + * filled with the records that the two passed arrays share with each other. + * If there is no intersection, an empty array will be returned. + * + * ```javascript + * var array1 = [1, 2, 3, 4, 5]; + * var array2 = [1, 3, 5, 6, 7]; + * + * Ember.EnumerableUtils.intersection(array1, array2); // [1, 3, 5] + * + * var array1 = [1, 2, 3]; + * var array2 = [4, 5, 6]; + * + * Ember.EnumerableUtils.intersection(array1, array2); // [] + * ``` + * + * @method intersection + * @param {Array} array1 The first array + * @param {Array} array2 The second array + * + * @return {Array} The intersection of the two passed arrays. + */ + function intersection(array1, array2) { + var result = []; + forEach(array1, function (element) { + if (indexOf(array2, element) >= 0) { + result.push(element); + } + }); + + return result; + } + + // TODO: this only exists to maintain the existing api, as we move forward it + // should only be part of the "global build" via some shim + exports['default'] = { + _replace: _replace, + addObject: addObject, + filter: filter, + forEach: forEach, + indexOf: indexOf, + indexesOf: indexesOf, + intersection: intersection, + map: map, + removeObject: removeObject, + replace: replace + }; + +}); +enifed('ember-metal/environment', ['exports', 'ember-metal/core'], function (exports, Ember) { + + 'use strict'; + + var environment; + + // This code attempts to automatically detect an environment with DOM + // by searching for window and document.createElement. An environment + // with DOM may disable the DOM functionality of Ember explicitly by + // defining a `disableBrowserEnvironment` ENV. + var hasDOM = typeof window !== 'undefined' && typeof document !== 'undefined' && typeof document.createElement !== 'undefined' && !Ember['default'].ENV.disableBrowserEnvironment; + + if (hasDOM) { + environment = { + hasDOM: true, + isChrome: !!window.chrome && !window.opera, + location: window.location, + history: window.history, + userAgent: window.navigator.userAgent, + global: window + }; + } else { + environment = { + hasDOM: false, + isChrome: false, + location: null, + history: null, + userAgent: 'Lynx (textmode)', + global: null + }; + } + + exports['default'] = environment; + +}); +enifed('ember-metal/error', ['exports', 'ember-metal/platform/create'], function (exports, create) { + + 'use strict'; + + var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack']; + + /** + A subclass of the JavaScript Error object for use in Ember. + + @class Error + @namespace Ember + @extends Error + @constructor + */ + function EmberError() { + var tmp = Error.apply(this, arguments); + + // Adds a `stack` property to the given error object that will yield the + // stack trace at the time captureStackTrace was called. + // When collecting the stack trace all frames above the topmost call + // to this function, including that call, will be left out of the + // stack trace. + // This is useful because we can hide Ember implementation details + // that are not very helpful for the user. + if (Error.captureStackTrace) { + Error.captureStackTrace(this, Ember.Error); + } + // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work. + for (var idx = 0; idx < errorProps.length; idx++) { + this[errorProps[idx]] = tmp[errorProps[idx]]; + } + } + + EmberError.prototype = create['default'](Error.prototype); + + exports['default'] = EmberError; + +}); +enifed('ember-metal/events', ['exports', 'ember-metal/core', 'ember-metal/utils', 'ember-metal/platform/create'], function (exports, Ember, utils, create) { + + + exports.accumulateListeners = accumulateListeners; + exports.addListener = addListener; + exports.suspendListener = suspendListener; + exports.suspendListeners = suspendListeners; + exports.watchedEvents = watchedEvents; + exports.sendEvent = sendEvent; + exports.hasListeners = hasListeners; + exports.listenersFor = listenersFor; + exports.on = on; + exports.removeListener = removeListener; + + "REMOVE_USE_STRICT: true"; /* listener flags */ + var ONCE = 1; + var SUSPENDED = 2; + + /* + The event system uses a series of nested hashes to store listeners on an + object. When a listener is registered, or when an event arrives, these + hashes are consulted to determine which target and action pair to invoke. + + The hashes are stored in the object's meta hash, and look like this: + + // Object's meta hash + { + listeners: { // variable name: `listenerSet` + "foo:changed": [ // variable name: `actions` + target, method, flags + ] + } + } + + */ + + function indexOf(array, target, method) { + var index = -1; + // hashes are added to the end of the event array + // so it makes sense to start searching at the end + // of the array and search in reverse + for (var i = array.length - 3; i >= 0; i -= 3) { + if (target === array[i] && method === array[i + 1]) { + index = i; + break; + } + } + return index; + } + + function actionsFor(obj, eventName) { + var meta = utils.meta(obj, true); + var actions; + var listeners = meta.listeners; + + if (!listeners) { + listeners = meta.listeners = create['default'](null); + listeners.__source__ = obj; + } else if (listeners.__source__ !== obj) { + // setup inherited copy of the listeners object + listeners = meta.listeners = create['default'](listeners); + listeners.__source__ = obj; + } + + actions = listeners[eventName]; + + // if there are actions, but the eventName doesn't exist in our listeners, then copy them from the prototype + if (actions && actions.__source__ !== obj) { + actions = listeners[eventName] = listeners[eventName].slice(); + actions.__source__ = obj; + } else if (!actions) { + actions = listeners[eventName] = []; + actions.__source__ = obj; + } + + return actions; + } + function accumulateListeners(obj, eventName, otherActions) { + var meta = obj["__ember_meta__"]; + var actions = meta && meta.listeners && meta.listeners[eventName]; + + if (!actions) { + return; + } + + var newActions = []; + + for (var i = actions.length - 3; i >= 0; i -= 3) { + var target = actions[i]; + var method = actions[i + 1]; + var flags = actions[i + 2]; + var actionIndex = indexOf(otherActions, target, method); + + if (actionIndex === -1) { + otherActions.push(target, method, flags); + newActions.push(target, method, flags); + } + } + + return newActions; + } + + /** + Add an event listener + + @method addListener + @for Ember + @param obj + @param {String} eventName + @param {Object|Function} target A target object or a function + @param {Function|String} method A function or the name of a function to be called on `target` + @param {Boolean} once A flag whether a function should only be called once + */ + function addListener(obj, eventName, target, method, once) { + Ember['default'].assert("You must pass at least an object and event name to Ember.addListener", !!obj && !!eventName); + + if (!method && "function" === typeof target) { + method = target; + target = null; + } + + var actions = actionsFor(obj, eventName); + var actionIndex = indexOf(actions, target, method); + var flags = 0; + + if (once) { + flags |= ONCE; + } + + if (actionIndex !== -1) { + return; + } + + actions.push(target, method, flags); + + if ("function" === typeof obj.didAddListener) { + obj.didAddListener(eventName, target, method); + } + } + + /** + Remove an event listener + + Arguments should match those passed to `Ember.addListener`. + + @method removeListener + @for Ember + @param obj + @param {String} eventName + @param {Object|Function} target A target object or a function + @param {Function|String} method A function or the name of a function to be called on `target` + */ + function removeListener(obj, eventName, target, method) { + Ember['default'].assert("You must pass at least an object and event name to Ember.removeListener", !!obj && !!eventName); + + if (!method && "function" === typeof target) { + method = target; + target = null; + } + + function _removeListener(target, method) { + var actions = actionsFor(obj, eventName); + var actionIndex = indexOf(actions, target, method); + + // action doesn't exist, give up silently + if (actionIndex === -1) { + return; + } + + actions.splice(actionIndex, 3); + + if ("function" === typeof obj.didRemoveListener) { + obj.didRemoveListener(eventName, target, method); + } + } + + if (method) { + _removeListener(target, method); + } else { + var meta = obj["__ember_meta__"]; + var actions = meta && meta.listeners && meta.listeners[eventName]; + + if (!actions) { + return; + } + for (var i = actions.length - 3; i >= 0; i -= 3) { + _removeListener(actions[i], actions[i + 1]); + } + } + } + + /** + Suspend listener during callback. + + This should only be used by the target of the event listener + when it is taking an action that would cause the event, e.g. + an object might suspend its property change listener while it is + setting that property. + + @method suspendListener + @for Ember + + @private + @param obj + @param {String} eventName + @param {Object|Function} target A target object or a function + @param {Function|String} method A function or the name of a function to be called on `target` + @param {Function} callback + */ + function suspendListener(obj, eventName, target, method, callback) { + if (!method && "function" === typeof target) { + method = target; + target = null; + } + + var actions = actionsFor(obj, eventName); + var actionIndex = indexOf(actions, target, method); + + if (actionIndex !== -1) { + actions[actionIndex + 2] |= SUSPENDED; // mark the action as suspended + } + + function tryable() { + return callback.call(target); + } + function finalizer() { + if (actionIndex !== -1) { + actions[actionIndex + 2] &= ~SUSPENDED; + } + } + + return utils.tryFinally(tryable, finalizer); + } + + /** + Suspends multiple listeners during a callback. + + @method suspendListeners + @for Ember + + @private + @param obj + @param {Array} eventNames Array of event names + @param {Object|Function} target A target object or a function + @param {Function|String} method A function or the name of a function to be called on `target` + @param {Function} callback + */ + function suspendListeners(obj, eventNames, target, method, callback) { + if (!method && "function" === typeof target) { + method = target; + target = null; + } + + var suspendedActions = []; + var actionsList = []; + var eventName, actions, i, l; + + for (i = 0, l = eventNames.length; i < l; i++) { + eventName = eventNames[i]; + actions = actionsFor(obj, eventName); + var actionIndex = indexOf(actions, target, method); + + if (actionIndex !== -1) { + actions[actionIndex + 2] |= SUSPENDED; + suspendedActions.push(actionIndex); + actionsList.push(actions); + } + } + + function tryable() { + return callback.call(target); + } + + function finalizer() { + for (var i = 0, l = suspendedActions.length; i < l; i++) { + var actionIndex = suspendedActions[i]; + actionsList[i][actionIndex + 2] &= ~SUSPENDED; + } + } + + return utils.tryFinally(tryable, finalizer); + } + + /** + Return a list of currently watched events + + @private + @method watchedEvents + @for Ember + @param obj + */ + function watchedEvents(obj) { + var listeners = obj["__ember_meta__"].listeners; + var ret = []; + + if (listeners) { + for (var eventName in listeners) { + if (eventName !== "__source__" && listeners[eventName]) { + ret.push(eventName); + } + } + } + return ret; + } + + /** + Send an event. The execution of suspended listeners + is skipped, and once listeners are removed. A listener without + a target is executed on the passed object. If an array of actions + is not passed, the actions stored on the passed object are invoked. + + @method sendEvent + @for Ember + @param obj + @param {String} eventName + @param {Array} params Optional parameters for each listener. + @param {Array} actions Optional array of actions (listeners). + @return true + */ + function sendEvent(obj, eventName, params, actions) { + // first give object a chance to handle it + if (obj !== Ember['default'] && "function" === typeof obj.sendEvent) { + obj.sendEvent(eventName, params); + } + + if (!actions) { + var meta = obj["__ember_meta__"]; + actions = meta && meta.listeners && meta.listeners[eventName]; + } + + if (!actions) { + return; + } + + for (var i = actions.length - 3; i >= 0; i -= 3) { + // looping in reverse for once listeners + var target = actions[i]; + var method = actions[i + 1]; + var flags = actions[i + 2]; + + if (!method) { + continue; + } + if (flags & SUSPENDED) { + continue; + } + if (flags & ONCE) { + removeListener(obj, eventName, target, method); + } + if (!target) { + target = obj; + } + if ("string" === typeof method) { + if (params) { + utils.applyStr(target, method, params); + } else { + target[method](); + } + } else { + if (params) { + utils.apply(target, method, params); + } else { + method.call(target); + } + } + } + return true; + } + + /** + @private + @method hasListeners + @for Ember + @param obj + @param {String} eventName + */ + function hasListeners(obj, eventName) { + var meta = obj["__ember_meta__"]; + var actions = meta && meta.listeners && meta.listeners[eventName]; + + return !!(actions && actions.length); + } + + /** + @private + @method listenersFor + @for Ember + @param obj + @param {String} eventName + */ + function listenersFor(obj, eventName) { + var ret = []; + var meta = obj["__ember_meta__"]; + var actions = meta && meta.listeners && meta.listeners[eventName]; + + if (!actions) { + return ret; + } + + for (var i = 0, l = actions.length; i < l; i += 3) { + var target = actions[i]; + var method = actions[i + 1]; + ret.push([target, method]); + } + + return ret; + } + + /** + Define a property as a function that should be executed when + a specified event or events are triggered. + + + ``` javascript + var Job = Ember.Object.extend({ + logCompleted: Ember.on('completed', function() { + console.log('Job completed!'); + }) + }); + + var job = Job.create(); + + Ember.sendEvent(job, 'completed'); // Logs 'Job completed!' + ``` + + @method on + @for Ember + @param {String} eventNames* + @param {Function} func + @return func + */ + function on() { + for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + var func = args.pop(); + var events = args; + func.__ember_listens__ = events; + return func; + } + +}); +enifed('ember-metal/expand_properties', ['exports', 'ember-metal/error', 'ember-metal/enumerable_utils', 'ember-metal/utils'], function (exports, EmberError, enumerable_utils, utils) { + + 'use strict'; + + + exports['default'] = expandProperties; + + var SPLIT_REGEX = /\{|\}/; + + /** + Expands `pattern`, invoking `callback` for each expansion. + + The only pattern supported is brace-expansion, anything else will be passed + once to `callback` directly. + + Example + + ```js + function echo(arg){ console.log(arg); } + + Ember.expandProperties('foo.bar', echo); //=> 'foo.bar' + Ember.expandProperties('{foo,bar}', echo); //=> 'foo', 'bar' + Ember.expandProperties('foo.{bar,baz}', echo); //=> 'foo.bar', 'foo.baz' + Ember.expandProperties('{foo,bar}.baz', echo); //=> 'foo.baz', 'bar.baz' + Ember.expandProperties('foo.{bar,baz}.@each', echo) //=> 'foo.bar.@each', 'foo.baz.@each' + Ember.expandProperties('{foo,bar}.{spam,eggs}', echo) //=> 'foo.spam', 'foo.eggs', 'bar.spam', 'bar.eggs' + Ember.expandProperties('{foo}.bar.{baz}') //=> 'foo.bar.baz' + ``` + + @method + @private + @param {String} pattern The property pattern to expand. + @param {Function} callback The callback to invoke. It is invoked once per + expansion, and is passed the expansion. + */ + function expandProperties(pattern, callback) { + if (pattern.indexOf(' ') > -1) { + throw new EmberError['default']('Brace expanded properties cannot contain spaces, e.g. \'user.{firstName, lastName}\' should be \'user.{firstName,lastName}\''); + } + + if ('string' === utils.typeOf(pattern)) { + var parts = pattern.split(SPLIT_REGEX); + var properties = [parts]; + + enumerable_utils.forEach(parts, function (part, index) { + if (part.indexOf(',') >= 0) { + properties = duplicateAndReplace(properties, part.split(','), index); + } + }); + + enumerable_utils.forEach(properties, function (property) { + callback(property.join('')); + }); + } else { + callback(pattern); + } + } + + function duplicateAndReplace(properties, currentParts, index) { + var all = []; + + enumerable_utils.forEach(properties, function (property) { + enumerable_utils.forEach(currentParts, function (part) { + var current = property.slice(0); + current[index] = part; + all.push(current); + }); + }); + + return all; + } + +}); +enifed('ember-metal/get_properties', ['exports', 'ember-metal/property_get', 'ember-metal/utils'], function (exports, property_get, utils) { + + 'use strict'; + + + exports['default'] = getProperties; + function getProperties(obj) { + var ret = {}; + var propertyNames = arguments; + var i = 1; + + if (arguments.length === 2 && utils.typeOf(arguments[1]) === "array") { + i = 0; + propertyNames = arguments[1]; + } + for (var len = propertyNames.length; i < len; i++) { + ret[propertyNames[i]] = property_get.get(obj, propertyNames[i]); + } + return ret; + } + +}); +enifed('ember-metal/injected_property', ['exports', 'ember-metal/core', 'ember-metal/computed', 'ember-metal/alias', 'ember-metal/properties', 'ember-metal/platform/create'], function (exports, Ember, computed, alias, properties, create) { + + 'use strict'; + + function InjectedProperty(type, name) { + this.type = type; + this.name = name; + + this._super$Constructor(injectedPropertyGet); + AliasedPropertyPrototype.oneWay.call(this); + } + + function injectedPropertyGet(keyName) { + var possibleDesc = this[keyName]; + var desc = possibleDesc !== null && typeof possibleDesc === "object" && possibleDesc.isDescriptor ? possibleDesc : undefined; + + Ember['default'].assert("Attempting to lookup an injected property on an object without a container, ensure that the object was instantiated via a container.", this.container); + + return this.container.lookup(desc.type + ":" + (desc.name || keyName)); + } + + InjectedProperty.prototype = create['default'](properties.Descriptor.prototype); + + var InjectedPropertyPrototype = InjectedProperty.prototype; + var ComputedPropertyPrototype = computed.ComputedProperty.prototype; + var AliasedPropertyPrototype = alias.AliasedProperty.prototype; + + InjectedPropertyPrototype._super$Constructor = computed.ComputedProperty; + + InjectedPropertyPrototype.get = ComputedPropertyPrototype.get; + InjectedPropertyPrototype.readOnly = ComputedPropertyPrototype.readOnly; + + InjectedPropertyPrototype.teardown = ComputedPropertyPrototype.teardown; + + exports['default'] = InjectedProperty; + +}); +enifed('ember-metal/instrumentation', ['exports', 'ember-metal/core', 'ember-metal/utils'], function (exports, Ember, utils) { + + 'use strict'; + + exports.instrument = instrument; + exports._instrumentStart = _instrumentStart; + exports.subscribe = subscribe; + exports.unsubscribe = unsubscribe; + exports.reset = reset; + + var subscribers = []; + var cache = {}; + + var populateListeners = function (name) { + var listeners = []; + var subscriber; + + for (var i = 0, l = subscribers.length; i < l; i++) { + subscriber = subscribers[i]; + if (subscriber.regex.test(name)) { + listeners.push(subscriber.object); + } + } + + cache[name] = listeners; + return listeners; + }; + + var time = (function () { + var perf = "undefined" !== typeof window ? window.performance || {} : {}; + var fn = perf.now || perf.mozNow || perf.webkitNow || perf.msNow || perf.oNow; + // fn.bind will be available in all the browsers that support the advanced window.performance... ;-) + return fn ? fn.bind(perf) : function () { + return +new Date(); + }; + })(); + + /** + Notifies event's subscribers, calls `before` and `after` hooks. + + @method instrument + @namespace Ember.Instrumentation + + @param {String} [name] Namespaced event name. + @param {Object} payload + @param {Function} callback Function that you're instrumenting. + @param {Object} binding Context that instrument function is called with. + */ + function instrument(name, _payload, callback, binding) { + if (arguments.length <= 3 && typeof _payload === "function") { + binding = callback; + callback = _payload; + _payload = undefined; + } + if (subscribers.length === 0) { + return callback.call(binding); + } + var payload = _payload || {}; + var finalizer = _instrumentStart(name, function () { + return payload; + }); + if (finalizer) { + var tryable = function _instrumenTryable() { + return callback.call(binding); + }; + var catchable = function _instrumentCatchable(e) { + payload.exception = e; + }; + return utils.tryCatchFinally(tryable, catchable, finalizer); + } else { + return callback.call(binding); + } + } + + // private for now + + function _instrumentStart(name, _payload) { + var listeners = cache[name]; + + if (!listeners) { + listeners = populateListeners(name); + } + + if (listeners.length === 0) { + return; + } + + var payload = _payload(); + + var STRUCTURED_PROFILE = Ember['default'].STRUCTURED_PROFILE; + var timeName; + if (STRUCTURED_PROFILE) { + timeName = name + ": " + payload.object; + console.time(timeName); + } + + var l = listeners.length; + var beforeValues = new Array(l); + var i, listener; + var timestamp = time(); + for (i = 0; i < l; i++) { + listener = listeners[i]; + beforeValues[i] = listener.before(name, timestamp, payload); + } + + return function _instrumentEnd() { + var i, l, listener; + var timestamp = time(); + for (i = 0, l = listeners.length; i < l; i++) { + listener = listeners[i]; + listener.after(name, timestamp, payload, beforeValues[i]); + } + + if (STRUCTURED_PROFILE) { + console.timeEnd(timeName); + } + }; + } + + /** + Subscribes to a particular event or instrumented block of code. + + @method subscribe + @namespace Ember.Instrumentation + + @param {String} [pattern] Namespaced event name. + @param {Object} [object] Before and After hooks. + + @return {Subscriber} + */ + function subscribe(pattern, object) { + var paths = pattern.split("."); + var path; + var regex = []; + + for (var i = 0, l = paths.length; i < l; i++) { + path = paths[i]; + if (path === "*") { + regex.push("[^\\.]*"); + } else { + regex.push(path); + } + } + + regex = regex.join("\\."); + regex = regex + "(\\..*)?"; + + var subscriber = { + pattern: pattern, + regex: new RegExp("^" + regex + "$"), + object: object + }; + + subscribers.push(subscriber); + cache = {}; + + return subscriber; + } + + /** + Unsubscribes from a particular event or instrumented block of code. + + @method unsubscribe + @namespace Ember.Instrumentation + + @param {Object} [subscriber] + */ + function unsubscribe(subscriber) { + var index; + + for (var i = 0, l = subscribers.length; i < l; i++) { + if (subscribers[i] === subscriber) { + index = i; + } + } + + subscribers.splice(index, 1); + cache = {}; + } + + /** + Resets `Ember.Instrumentation` by flushing list of subscribers. + + @method reset + @namespace Ember.Instrumentation + */ + function reset() { + subscribers.length = 0; + cache = {}; + } + + exports.subscribers = subscribers; + +}); +enifed('ember-metal/is_blank', ['exports', 'ember-metal/is_empty'], function (exports, isEmpty) { + + 'use strict'; + + + exports['default'] = isBlank; + function isBlank(obj) { + return isEmpty['default'](obj) || typeof obj === 'string' && obj.match(/\S/) === null; + } + +}); +enifed('ember-metal/is_empty', ['exports', 'ember-metal/property_get', 'ember-metal/is_none'], function (exports, property_get, isNone) { + + 'use strict'; + + function isEmpty(obj) { + var none = isNone['default'](obj); + if (none) { + return none; + } + + if (typeof obj.size === 'number') { + return !obj.size; + } + + var objectType = typeof obj; + + if (objectType === 'object') { + var size = property_get.get(obj, 'size'); + if (typeof size === 'number') { + return !size; + } + } + + if (typeof obj.length === 'number' && objectType !== 'function') { + return !obj.length; + } + + if (objectType === 'object') { + var length = property_get.get(obj, 'length'); + if (typeof length === 'number') { + return !length; + } + } + + return false; + } + + exports['default'] = isEmpty; + +}); +enifed('ember-metal/is_none', ['exports'], function (exports) { + + 'use strict'; + + /** + Returns true if the passed value is null or undefined. This avoids errors + from JSLint complaining about use of ==, which can be technically + confusing. + + ```javascript + Ember.isNone(); // true + Ember.isNone(null); // true + Ember.isNone(undefined); // true + Ember.isNone(''); // false + Ember.isNone([]); // false + Ember.isNone(function() {}); // false + ``` + + @method isNone + @for Ember + @param {Object} obj Value to test + @return {Boolean} + */ + function isNone(obj) { + return obj === null || obj === undefined; + } + + exports['default'] = isNone; + +}); +enifed('ember-metal/is_present', ['exports', 'ember-metal/is_blank'], function (exports, isBlank) { + + 'use strict'; + + + exports['default'] = isPresent; + function isPresent(obj) { + return !isBlank['default'](obj); + } + +}); +enifed('ember-metal/keys', ['exports', 'ember-metal/platform/define_property'], function (exports, define_property) { + + 'use strict'; + + var keys = Object.keys; + + if (!keys || !define_property.canDefineNonEnumerableProperties) { + // modified from + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys + keys = (function () { + var hasOwnProperty = Object.prototype.hasOwnProperty; + var hasDontEnumBug = !({ toString: null }).propertyIsEnumerable('toString'); + var dontEnums = ['toString', 'toLocaleString', 'valueOf', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'constructor']; + var dontEnumsLength = dontEnums.length; + + return function keys(obj) { + if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) { + throw new TypeError('Object.keys called on non-object'); + } + + var result = []; + var prop, i; + + for (prop in obj) { + if (prop !== '_super' && prop.lastIndexOf('__', 0) !== 0 && hasOwnProperty.call(obj, prop)) { + result.push(prop); + } + } + + if (hasDontEnumBug) { + for (i = 0; i < dontEnumsLength; i++) { + if (hasOwnProperty.call(obj, dontEnums[i])) { + result.push(dontEnums[i]); + } + } + } + return result; + }; + })(); + } + + exports['default'] = keys; + +}); +enifed('ember-metal/libraries', ['exports', 'ember-metal/core', 'ember-metal/enumerable_utils'], function (exports, Ember, enumerable_utils) { + + 'use strict'; + + function Libraries() { + this._registry = []; + this._coreLibIndex = 0; + } + + Libraries.prototype = { + constructor: Libraries, + + _getLibraryByName: function (name) { + var libs = this._registry; + var count = libs.length; + + for (var i = 0; i < count; i++) { + if (libs[i].name === name) { + return libs[i]; + } + } + }, + + register: function (name, version, isCoreLibrary) { + var index = this._registry.length; + + if (!this._getLibraryByName(name)) { + if (isCoreLibrary) { + index = this._coreLibIndex++; + } + this._registry.splice(index, 0, { name: name, version: version }); + } else { + Ember['default'].warn("Library \"" + name + "\" is already registered with Ember."); + } + }, + + registerCoreLibrary: function (name, version) { + this.register(name, version, true); + }, + + deRegister: function (name) { + var lib = this._getLibraryByName(name); + var index; + + if (lib) { + index = enumerable_utils.indexOf(this._registry, lib); + this._registry.splice(index, 1); + } + }, + + each: function (callback) { + Ember['default'].deprecate("Using Ember.libraries.each() is deprecated. Access to a list of registered libraries is currently a private API. If you are not knowingly accessing this method, your out-of-date Ember Inspector may be doing so."); + enumerable_utils.forEach(this._registry, function (lib) { + callback(lib.name, lib.version); + }); + } + }; + + + exports['default'] = Libraries; + +}); +enifed('ember-metal/logger', ['exports', 'ember-metal/core', 'ember-metal/error'], function (exports, Ember, EmberError) { + + 'use strict'; + + function K() { + return this; + } + + function consoleMethod(name) { + var consoleObj, logToConsole; + if (Ember['default'].imports.console) { + consoleObj = Ember['default'].imports.console; + } else if (typeof console !== "undefined") { + consoleObj = console; + } + + var method = typeof consoleObj === "object" ? consoleObj[name] : null; + + if (method) { + // Older IE doesn't support bind, but Chrome needs it + if (typeof method.bind === "function") { + logToConsole = method.bind(consoleObj); + logToConsole.displayName = "console." + name; + return logToConsole; + } else if (typeof method.apply === "function") { + logToConsole = function () { + method.apply(consoleObj, arguments); + }; + logToConsole.displayName = "console." + name; + return logToConsole; + } else { + return function () { + var message = Array.prototype.join.call(arguments, ", "); + method(message); + }; + } + } + } + + function assertPolyfill(test, message) { + if (!test) { + try { + // attempt to preserve the stack + throw new EmberError['default']("assertion failed: " + message); + } catch (error) { + setTimeout(function () { + throw error; + }, 0); + } + } + } + + /** + Inside Ember-Metal, simply uses the methods from `imports.console`. + Override this to provide more robust logging functionality. + + @class Logger + @namespace Ember + */ + exports['default'] = { + /** + Logs the arguments to the console. + You can pass as many arguments as you want and they will be joined together with a space. + ```javascript + var foo = 1; + Ember.Logger.log('log value of foo:', foo); + // "log value of foo: 1" will be printed to the console + ``` + @method log + @for Ember.Logger + @param {*} arguments + */ + log: consoleMethod("log") || K, + + /** + Prints the arguments to the console with a warning icon. + You can pass as many arguments as you want and they will be joined together with a space. + ```javascript + Ember.Logger.warn('Something happened!'); + // "Something happened!" will be printed to the console with a warning icon. + ``` + @method warn + @for Ember.Logger + @param {*} arguments + */ + warn: consoleMethod("warn") || K, + + /** + Prints the arguments to the console with an error icon, red text and a stack trace. + You can pass as many arguments as you want and they will be joined together with a space. + ```javascript + Ember.Logger.error('Danger! Danger!'); + // "Danger! Danger!" will be printed to the console in red text. + ``` + @method error + @for Ember.Logger + @param {*} arguments + */ + error: consoleMethod("error") || K, + + /** + Logs the arguments to the console. + You can pass as many arguments as you want and they will be joined together with a space. + ```javascript + var foo = 1; + Ember.Logger.info('log value of foo:', foo); + // "log value of foo: 1" will be printed to the console + ``` + @method info + @for Ember.Logger + @param {*} arguments + */ + info: consoleMethod("info") || K, + + /** + Logs the arguments to the console in blue text. + You can pass as many arguments as you want and they will be joined together with a space. + ```javascript + var foo = 1; + Ember.Logger.debug('log value of foo:', foo); + // "log value of foo: 1" will be printed to the console + ``` + @method debug + @for Ember.Logger + @param {*} arguments + */ + debug: consoleMethod("debug") || consoleMethod("info") || K, + + /** + If the value passed into `Ember.Logger.assert` is not truthy it will throw an error with a stack trace. + ```javascript + Ember.Logger.assert(true); // undefined + Ember.Logger.assert(true === false); // Throws an Assertion failed error. + ``` + @method assert + @for Ember.Logger + @param {Boolean} bool Value to test + */ + assert: consoleMethod("assert") || assertPolyfill + }; + +}); +enifed('ember-metal/map', ['exports', 'ember-metal/utils', 'ember-metal/array', 'ember-metal/platform/create', 'ember-metal/deprecate_property'], function (exports, utils, array, create, deprecate_property) { + + 'use strict'; + + exports.OrderedSet = OrderedSet; + exports.Map = Map; + exports.MapWithDefault = MapWithDefault; + + /** + @module ember-metal + */ + + /* + JavaScript (before ES6) does not have a Map implementation. Objects, + which are often used as dictionaries, may only have Strings as keys. + + Because Ember has a way to get a unique identifier for every object + via `Ember.guidFor`, we can implement a performant Map with arbitrary + keys. Because it is commonly used in low-level bookkeeping, Map is + implemented as a pure JavaScript object for performance. + + This implementation follows the current iteration of the ES6 proposal for + maps (http://wiki.ecmascript.org/doku.php?id=harmony:simple_maps_and_sets), + with one exception: as we do not have the luxury of in-VM iteration, we implement a + forEach method for iteration. + + Map is mocked out to look like an Ember object, so you can do + `Ember.Map.create()` for symmetry with other Ember classes. + */ + + function missingFunction(fn) { + throw new TypeError("" + Object.prototype.toString.call(fn) + " is not a function"); + } + + function missingNew(name) { + throw new TypeError("Constructor " + name + " requires 'new'"); + } + + function copyNull(obj) { + var output = create['default'](null); + + for (var prop in obj) { + // hasOwnPropery is not needed because obj is Object.create(null); + output[prop] = obj[prop]; + } + + return output; + } + + function copyMap(original, newObject) { + var keys = original._keys.copy(); + var values = copyNull(original._values); + + newObject._keys = keys; + newObject._values = values; + newObject.size = original.size; + + return newObject; + } + + /** + This class is used internally by Ember and Ember Data. + Please do not use it at this time. We plan to clean it up + and add many tests soon. + + @class OrderedSet + @namespace Ember + @constructor + @private + */ + function OrderedSet() { + + if (this instanceof OrderedSet) { + this.clear(); + this._silenceRemoveDeprecation = false; + } else { + missingNew("OrderedSet"); + } + } + + /** + @method create + @static + @return {Ember.OrderedSet} + */ + OrderedSet.create = function () { + var Constructor = this; + + return new Constructor(); + }; + + OrderedSet.prototype = { + constructor: OrderedSet, + /** + @method clear + */ + clear: function () { + this.presenceSet = create['default'](null); + this.list = []; + this.size = 0; + }, + + /** + @method add + @param obj + @param guid (optional, and for internal use) + @return {Ember.OrderedSet} + */ + add: function (obj, _guid) { + var guid = _guid || utils.guidFor(obj); + var presenceSet = this.presenceSet; + var list = this.list; + + if (presenceSet[guid] !== true) { + presenceSet[guid] = true; + this.size = list.push(obj); + } + + return this; + }, + + /** + @deprecated + @method remove + @param obj + @param _guid (optional and for internal use only) + @return {Boolean} + */ + remove: function (obj, _guid) { + Ember.deprecate("Calling `OrderedSet.prototype.remove` has been deprecated, please use `OrderedSet.prototype.delete` instead.", this._silenceRemoveDeprecation); + + return this["delete"](obj, _guid); + }, + + /** + @since 1.8.0 + @method delete + @param obj + @param _guid (optional and for internal use only) + @return {Boolean} + */ + "delete": function (obj, _guid) { + var guid = _guid || utils.guidFor(obj); + var presenceSet = this.presenceSet; + var list = this.list; + + if (presenceSet[guid] === true) { + delete presenceSet[guid]; + var index = array.indexOf.call(list, obj); + if (index > -1) { + list.splice(index, 1); + } + this.size = list.length; + return true; + } else { + return false; + } + }, + + /** + @method isEmpty + @return {Boolean} + */ + isEmpty: function () { + return this.size === 0; + }, + + /** + @method has + @param obj + @return {Boolean} + */ + has: function (obj) { + if (this.size === 0) { + return false; + } + + var guid = utils.guidFor(obj); + var presenceSet = this.presenceSet; + + return presenceSet[guid] === true; + }, + + /** + @method forEach + @param {Function} fn + @param self + */ + forEach: function (fn /*, ...thisArg*/) { + if (typeof fn !== "function") { + missingFunction(fn); + } + + if (this.size === 0) { + return; + } + + var list = this.list; + var length = arguments.length; + var i; + + if (length === 2) { + for (i = 0; i < list.length; i++) { + fn.call(arguments[1], list[i]); + } + } else { + for (i = 0; i < list.length; i++) { + fn(list[i]); + } + } + }, + + /** + @method toArray + @return {Array} + */ + toArray: function () { + return this.list.slice(); + }, + + /** + @method copy + @return {Ember.OrderedSet} + */ + copy: function () { + var Constructor = this.constructor; + var set = new Constructor(); + + set._silenceRemoveDeprecation = this._silenceRemoveDeprecation; + set.presenceSet = copyNull(this.presenceSet); + set.list = this.toArray(); + set.size = this.size; + + return set; + } + }; + + deprecate_property.deprecateProperty(OrderedSet.prototype, "length", "size"); + + /** + A Map stores values indexed by keys. Unlike JavaScript's + default Objects, the keys of a Map can be any JavaScript + object. + + Internally, a Map has two data structures: + + 1. `keys`: an OrderedSet of all of the existing keys + 2. `values`: a JavaScript Object indexed by the `Ember.guidFor(key)` + + When a key/value pair is added for the first time, we + add the key to the `keys` OrderedSet, and create or + replace an entry in `values`. When an entry is deleted, + we delete its entry in `keys` and `values`. + + @class Map + @namespace Ember + @private + @constructor + */ + function Map() { + if (this instanceof this.constructor) { + this._keys = OrderedSet.create(); + this._keys._silenceRemoveDeprecation = true; + this._values = create['default'](null); + this.size = 0; + } else { + missingNew("OrderedSet"); + } + } + + Ember.Map = Map; + + /** + @method create + @static + */ + Map.create = function () { + var Constructor = this; + return new Constructor(); + }; + + Map.prototype = { + constructor: Map, + + /** + This property will change as the number of objects in the map changes. + @since 1.8.0 + @property size + @type number + @default 0 + */ + size: 0, + + /** + Retrieve the value associated with a given key. + @method get + @param {*} key + @return {*} the value associated with the key, or `undefined` + */ + get: function (key) { + if (this.size === 0) { + return; + } + + var values = this._values; + var guid = utils.guidFor(key); + + return values[guid]; + }, + + /** + Adds a value to the map. If a value for the given key has already been + provided, the new value will replace the old value. + @method set + @param {*} key + @param {*} value + @return {Ember.Map} + */ + set: function (key, value) { + var keys = this._keys; + var values = this._values; + var guid = utils.guidFor(key); + + // ensure we don't store -0 + var k = key === -0 ? 0 : key; + + keys.add(k, guid); + + values[guid] = value; + + this.size = keys.size; + + return this; + }, + + /** + @deprecated see delete + Removes a value from the map for an associated key. + @method remove + @param {*} key + @return {Boolean} true if an item was removed, false otherwise + */ + remove: function (key) { + Ember.deprecate("Calling `Map.prototype.remove` has been deprecated, please use `Map.prototype.delete` instead."); + + return this["delete"](key); + }, + + /** + Removes a value from the map for an associated key. + @since 1.8.0 + @method delete + @param {*} key + @return {Boolean} true if an item was removed, false otherwise + */ + "delete": function (key) { + if (this.size === 0) { + return false; + } + // don't use ES6 "delete" because it will be annoying + // to use in browsers that are not ES6 friendly; + var keys = this._keys; + var values = this._values; + var guid = utils.guidFor(key); + + if (keys["delete"](key, guid)) { + delete values[guid]; + this.size = keys.size; + return true; + } else { + return false; + } + }, + + /** + Check whether a key is present. + @method has + @param {*} key + @return {Boolean} true if the item was present, false otherwise + */ + has: function (key) { + return this._keys.has(key); + }, + + /** + Iterate over all the keys and values. Calls the function once + for each key, passing in value, key, and the map being iterated over, + in that order. + The keys are guaranteed to be iterated over in insertion order. + @method forEach + @param {Function} callback + @param {*} self if passed, the `this` value inside the + callback. By default, `this` is the map. + */ + forEach: function (callback /*, ...thisArg*/) { + if (typeof callback !== "function") { + missingFunction(callback); + } + + if (this.size === 0) { + return; + } + + var length = arguments.length; + var map = this; + var cb, thisArg; + + if (length === 2) { + thisArg = arguments[1]; + cb = function (key) { + callback.call(thisArg, map.get(key), key, map); + }; + } else { + cb = function (key) { + callback(map.get(key), key, map); + }; + } + + this._keys.forEach(cb); + }, + + /** + @method clear + */ + clear: function () { + this._keys.clear(); + this._values = create['default'](null); + this.size = 0; + }, + + /** + @method copy + @return {Ember.Map} + */ + copy: function () { + return copyMap(this, new Map()); + } + }; + + deprecate_property.deprecateProperty(Map.prototype, "length", "size"); + + /** + @class MapWithDefault + @namespace Ember + @extends Ember.Map + @private + @constructor + @param [options] + @param {*} [options.defaultValue] + */ + function MapWithDefault(options) { + this._super$constructor(); + this.defaultValue = options.defaultValue; + } + + /** + @method create + @static + @param [options] + @param {*} [options.defaultValue] + @return {Ember.MapWithDefault|Ember.Map} If options are passed, returns + `Ember.MapWithDefault` otherwise returns `Ember.Map` + */ + MapWithDefault.create = function (options) { + if (options) { + return new MapWithDefault(options); + } else { + return new Map(); + } + }; + + MapWithDefault.prototype = create['default'](Map.prototype); + MapWithDefault.prototype.constructor = MapWithDefault; + MapWithDefault.prototype._super$constructor = Map; + MapWithDefault.prototype._super$get = Map.prototype.get; + + /** + Retrieve the value associated with a given key. + + @method get + @param {*} key + @return {*} the value associated with the key, or the default value + */ + MapWithDefault.prototype.get = function (key) { + var hasValue = this.has(key); + + if (hasValue) { + return this._super$get(key); + } else { + var defaultValue = this.defaultValue(key); + this.set(key, defaultValue); + return defaultValue; + } + }; + + /** + @method copy + @return {Ember.MapWithDefault} + */ + MapWithDefault.prototype.copy = function () { + var Constructor = this.constructor; + return copyMap(this, new Constructor({ + defaultValue: this.defaultValue + })); + }; + + exports['default'] = Map; + +}); +enifed('ember-metal/merge', ['exports', 'ember-metal/keys'], function (exports, keys) { + + 'use strict'; + + + exports['default'] = merge; + function merge(original, updates) { + if (!updates || typeof updates !== 'object') { + return original; + } + + var props = keys['default'](updates); + var prop; + var length = props.length; + + for (var i = 0; i < length; i++) { + prop = props[i]; + original[prop] = updates[prop]; + } + + return original; + } + +}); +enifed('ember-metal/mixin', ['exports', 'ember-metal/core', 'ember-metal/merge', 'ember-metal/array', 'ember-metal/platform/create', 'ember-metal/property_get', 'ember-metal/property_set', 'ember-metal/utils', 'ember-metal/expand_properties', 'ember-metal/properties', 'ember-metal/computed', 'ember-metal/binding', 'ember-metal/observer', 'ember-metal/events', 'ember-metal/streams/utils'], function (exports, Ember, merge, array, o_create, property_get, property_set, utils, expandProperties, ember_metal__properties, computed, ember_metal__binding, ember_metal__observer, events, streams__utils) { + + + exports.mixin = mixin; + exports.required = required; + exports.aliasMethod = aliasMethod; + exports.observer = observer; + exports.immediateObserver = immediateObserver; + exports.beforeObserver = beforeObserver; + exports.Mixin = Mixin; + + "REMOVE_USE_STRICT: true";var REQUIRED; + var a_slice = [].slice; + + function superFunction() { + var func = this.__nextSuper; + var ret; + + if (func) { + var length = arguments.length; + this.__nextSuper = null; + if (length === 0) { + ret = func.call(this); + } else if (length === 1) { + ret = func.call(this, arguments[0]); + } else if (length === 2) { + ret = func.call(this, arguments[0], arguments[1]); + } else { + ret = func.apply(this, arguments); + } + this.__nextSuper = func; + return ret; + } + } + + // ensure we prime superFunction to mitigate + // v8 bug potentially incorrectly deopts this function: https://code.google.com/p/v8/issues/detail?id=3709 + var primer = { + __nextSuper: function (a, b, c, d) {} + }; + + superFunction.call(primer); + superFunction.call(primer, 1); + superFunction.call(primer, 1, 2); + superFunction.call(primer, 1, 2, 3); + + function mixinsMeta(obj) { + var m = utils.meta(obj, true); + var ret = m.mixins; + if (!ret) { + ret = m.mixins = {}; + } else if (!m.hasOwnProperty("mixins")) { + ret = m.mixins = o_create['default'](ret); + } + return ret; + } + + function isMethod(obj) { + return "function" === typeof obj && obj.isMethod !== false && obj !== Boolean && obj !== Object && obj !== Number && obj !== Array && obj !== Date && obj !== String; + } + + var CONTINUE = {}; + + function mixinProperties(mixinsMeta, mixin) { + var guid; + + if (mixin instanceof Mixin) { + guid = utils.guidFor(mixin); + if (mixinsMeta[guid]) { + return CONTINUE; + } + mixinsMeta[guid] = mixin; + return mixin.properties; + } else { + return mixin; // apply anonymous mixin properties + } + } + + function concatenatedMixinProperties(concatProp, props, values, base) { + var concats; + + // reset before adding each new mixin to pickup concats from previous + concats = values[concatProp] || base[concatProp]; + if (props[concatProp]) { + concats = concats ? concats.concat(props[concatProp]) : props[concatProp]; + } + + return concats; + } + + function giveDescriptorSuper(meta, key, property, values, descs, base) { + var superProperty; + + // Computed properties override methods, and do not call super to them + if (values[key] === undefined) { + // Find the original descriptor in a parent mixin + superProperty = descs[key]; + } + + // If we didn't find the original descriptor in a parent mixin, find + // it on the original object. + if (!superProperty) { + var possibleDesc = base[key]; + var superDesc = possibleDesc !== null && typeof possibleDesc === "object" && possibleDesc.isDescriptor ? possibleDesc : undefined; + + superProperty = superDesc; + } + + if (superProperty === undefined || !(superProperty instanceof computed.ComputedProperty)) { + return property; + } + + // Since multiple mixins may inherit from the same parent, we need + // to clone the computed property so that other mixins do not receive + // the wrapped version. + property = o_create['default'](property); + property._getter = utils.wrap(property._getter, superProperty._getter); + if (superProperty._setter) { + if (property._setter) { + property._setter = utils.wrap(property._setter, superProperty._setter); + } else { + property._setter = superProperty._setter; + } + } + + return property; + } + + var sourceAvailable = (function () { + return this; + }).toString().indexOf("return this;") > -1; + + function giveMethodSuper(obj, key, method, values, descs) { + var superMethod; + + // Methods overwrite computed properties, and do not call super to them. + if (descs[key] === undefined) { + // Find the original method in a parent mixin + superMethod = values[key]; + } + + // If we didn't find the original value in a parent mixin, find it in + // the original object + superMethod = superMethod || obj[key]; + + // Only wrap the new method if the original method was a function + if (superMethod === undefined || "function" !== typeof superMethod) { + return method; + } + + var hasSuper; + if (sourceAvailable) { + hasSuper = method.__hasSuper; + + if (hasSuper === undefined) { + hasSuper = method.toString().indexOf("_super") > -1; + method.__hasSuper = hasSuper; + } + } + + if (sourceAvailable === false || hasSuper) { + return utils.wrap(method, superMethod); + } else { + return method; + } + } + + function applyConcatenatedProperties(obj, key, value, values) { + var baseValue = values[key] || obj[key]; + + if (baseValue) { + if ("function" === typeof baseValue.concat) { + if (value === null || value === undefined) { + return baseValue; + } else { + return baseValue.concat(value); + } + } else { + return utils.makeArray(baseValue).concat(value); + } + } else { + return utils.makeArray(value); + } + } + + function applyMergedProperties(obj, key, value, values) { + var baseValue = values[key] || obj[key]; + + Ember['default'].assert("You passed in `" + JSON.stringify(value) + "` as the value for `" + key + "` but `" + key + "` cannot be an Array", !utils.isArray(value)); + + if (!baseValue) { + return value; + } + + var newBase = merge['default']({}, baseValue); + var hasFunction = false; + + for (var prop in value) { + if (!value.hasOwnProperty(prop)) { + continue; + } + + var propValue = value[prop]; + if (isMethod(propValue)) { + // TODO: support for Computed Properties, etc? + hasFunction = true; + newBase[prop] = giveMethodSuper(obj, prop, propValue, baseValue, {}); + } else { + newBase[prop] = propValue; + } + } + + if (hasFunction) { + newBase._super = superFunction; + } + + return newBase; + } + + function addNormalizedProperty(base, key, value, meta, descs, values, concats, mergings) { + if (value instanceof ember_metal__properties.Descriptor) { + if (value === REQUIRED && descs[key]) { + return CONTINUE; + } + + // Wrap descriptor function to implement + // __nextSuper() if needed + if (value._getter) { + value = giveDescriptorSuper(meta, key, value, values, descs, base); + } + + descs[key] = value; + values[key] = undefined; + } else { + if (concats && array.indexOf.call(concats, key) >= 0 || key === "concatenatedProperties" || key === "mergedProperties") { + value = applyConcatenatedProperties(base, key, value, values); + } else if (mergings && array.indexOf.call(mergings, key) >= 0) { + value = applyMergedProperties(base, key, value, values); + } else if (isMethod(value)) { + value = giveMethodSuper(base, key, value, values, descs); + } + + descs[key] = undefined; + values[key] = value; + } + } + + function mergeMixins(mixins, m, descs, values, base, keys) { + var currentMixin, props, key, concats, mergings, meta; + + function removeKeys(keyName) { + delete descs[keyName]; + delete values[keyName]; + } + + for (var i = 0, l = mixins.length; i < l; i++) { + currentMixin = mixins[i]; + Ember['default'].assert("Expected hash or Mixin instance, got " + Object.prototype.toString.call(currentMixin), typeof currentMixin === "object" && currentMixin !== null && Object.prototype.toString.call(currentMixin) !== "[object Array]"); + + props = mixinProperties(m, currentMixin); + if (props === CONTINUE) { + continue; + } + + if (props) { + meta = utils.meta(base); + if (base.willMergeMixin) { + base.willMergeMixin(props); + } + concats = concatenatedMixinProperties("concatenatedProperties", props, values, base); + mergings = concatenatedMixinProperties("mergedProperties", props, values, base); + + for (key in props) { + if (!props.hasOwnProperty(key)) { + continue; + } + keys.push(key); + addNormalizedProperty(base, key, props[key], meta, descs, values, concats, mergings); + } + + // manually copy toString() because some JS engines do not enumerate it + if (props.hasOwnProperty("toString")) { + base.toString = props.toString; + } + } else if (currentMixin.mixins) { + mergeMixins(currentMixin.mixins, m, descs, values, base, keys); + if (currentMixin._without) { + array.forEach.call(currentMixin._without, removeKeys); + } + } + } + } + + var IS_BINDING = /^.+Binding$/; + + function detectBinding(obj, key, value, m) { + if (IS_BINDING.test(key)) { + var bindings = m.bindings; + if (!bindings) { + bindings = m.bindings = {}; + } else if (!m.hasOwnProperty("bindings")) { + bindings = m.bindings = o_create['default'](m.bindings); + } + bindings[key] = value; + } + } + + function connectStreamBinding(obj, key, stream) { + var onNotify = function (stream) { + ember_metal__observer._suspendObserver(obj, key, null, didChange, function () { + property_set.trySet(obj, key, stream.value()); + }); + }; + + var didChange = function () { + stream.setValue(property_get.get(obj, key), onNotify); + }; + + // Initialize value + property_set.set(obj, key, stream.value()); + + ember_metal__observer.addObserver(obj, key, null, didChange); + + stream.subscribe(onNotify); + + if (obj._streamBindingSubscriptions === undefined) { + obj._streamBindingSubscriptions = o_create['default'](null); + } + + obj._streamBindingSubscriptions[key] = onNotify; + } + + function connectBindings(obj, m) { + // TODO Mixin.apply(instance) should disconnect binding if exists + var bindings = m.bindings; + var key, binding, to; + if (bindings) { + for (key in bindings) { + binding = bindings[key]; + if (binding) { + to = key.slice(0, -7); // strip Binding off end + if (streams__utils.isStream(binding)) { + connectStreamBinding(obj, to, binding); + continue; + } else if (binding instanceof ember_metal__binding.Binding) { + binding = binding.copy(); // copy prototypes' instance + binding.to(to); + } else { + // binding is string path + binding = new ember_metal__binding.Binding(to, binding); + } + binding.connect(obj); + obj[key] = binding; + } + } + // mark as applied + m.bindings = {}; + } + } + + function finishPartial(obj, m) { + connectBindings(obj, m || utils.meta(obj)); + return obj; + } + + function followAlias(obj, desc, m, descs, values) { + var altKey = desc.methodName; + var value; + var possibleDesc; + if (descs[altKey] || values[altKey]) { + value = values[altKey]; + desc = descs[altKey]; + } else if ((possibleDesc = obj[altKey]) && possibleDesc !== null && typeof possibleDesc === "object" && possibleDesc.isDescriptor) { + desc = possibleDesc; + value = undefined; + } else { + desc = undefined; + value = obj[altKey]; + } + + return { desc: desc, value: value }; + } + + function updateObserversAndListeners(obj, key, observerOrListener, pathsKey, updateMethod) { + var paths = observerOrListener[pathsKey]; + + if (paths) { + for (var i = 0, l = paths.length; i < l; i++) { + updateMethod(obj, paths[i], null, key); + } + } + } + + function replaceObserversAndListeners(obj, key, observerOrListener) { + var prev = obj[key]; + + if ("function" === typeof prev) { + updateObserversAndListeners(obj, key, prev, "__ember_observesBefore__", ember_metal__observer.removeBeforeObserver); + updateObserversAndListeners(obj, key, prev, "__ember_observes__", ember_metal__observer.removeObserver); + updateObserversAndListeners(obj, key, prev, "__ember_listens__", events.removeListener); + } + + if ("function" === typeof observerOrListener) { + updateObserversAndListeners(obj, key, observerOrListener, "__ember_observesBefore__", ember_metal__observer.addBeforeObserver); + updateObserversAndListeners(obj, key, observerOrListener, "__ember_observes__", ember_metal__observer.addObserver); + updateObserversAndListeners(obj, key, observerOrListener, "__ember_listens__", events.addListener); + } + } + + function applyMixin(obj, mixins, partial) { + var descs = {}; + var values = {}; + var m = utils.meta(obj); + var keys = []; + var key, value, desc; + + obj._super = superFunction; + + // Go through all mixins and hashes passed in, and: + // + // * Handle concatenated properties + // * Handle merged properties + // * Set up _super wrapping if necessary + // * Set up computed property descriptors + // * Copying `toString` in broken browsers + mergeMixins(mixins, mixinsMeta(obj), descs, values, obj, keys); + + for (var i = 0, l = keys.length; i < l; i++) { + key = keys[i]; + if (key === "constructor" || !values.hasOwnProperty(key)) { + continue; + } + + desc = descs[key]; + value = values[key]; + + if (desc === REQUIRED) { + continue; + } + + while (desc && desc instanceof Alias) { + var followed = followAlias(obj, desc, m, descs, values); + desc = followed.desc; + value = followed.value; + } + + if (desc === undefined && value === undefined) { + continue; + } + + replaceObserversAndListeners(obj, key, value); + detectBinding(obj, key, value, m); + ember_metal__properties.defineProperty(obj, key, desc, value, m); + } + + if (!partial) { + // don't apply to prototype + finishPartial(obj, m); + } + + return obj; + } + + /** + @method mixin + @for Ember + @param obj + @param mixins* + @return obj + */ + function mixin(obj) { + for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + args[_key - 1] = arguments[_key]; + } + + applyMixin(obj, args, false); + return obj; + } + + /** + The `Ember.Mixin` class allows you to create mixins, whose properties can be + added to other classes. For instance, + + ```javascript + App.Editable = Ember.Mixin.create({ + edit: function() { + console.log('starting to edit'); + this.set('isEditing', true); + }, + isEditing: false + }); + + // Mix mixins into classes by passing them as the first arguments to + // .extend. + App.CommentView = Ember.View.extend(App.Editable, { + template: Ember.Handlebars.compile('{{#if view.isEditing}}...{{else}}...{{/if}}') + }); + + commentView = App.CommentView.create(); + commentView.edit(); // outputs 'starting to edit' + ``` + + Note that Mixins are created with `Ember.Mixin.create`, not + `Ember.Mixin.extend`. + + Note that mixins extend a constructor's prototype so arrays and object literals + defined as properties will be shared amongst objects that implement the mixin. + If you want to define a property in a mixin that is not shared, you can define + it either as a computed property or have it be created on initialization of the object. + + ```javascript + //filters array will be shared amongst any object implementing mixin + App.Filterable = Ember.Mixin.create({ + filters: Ember.A() + }); + + //filters will be a separate array for every object implementing the mixin + App.Filterable = Ember.Mixin.create({ + filters: Ember.computed(function() {return Ember.A();}) + }); + + //filters will be created as a separate array during the object's initialization + App.Filterable = Ember.Mixin.create({ + init: function() { + this._super.apply(this, arguments); + this.set("filters", Ember.A()); + } + }); + ``` + + @class Mixin + @namespace Ember + */ + exports['default'] = Mixin; + function Mixin(args, properties) { + this.properties = properties; + + var length = args && args.length; + + if (length > 0) { + var m = new Array(length); + + for (var i = 0; i < length; i++) { + var x = args[i]; + if (x instanceof Mixin) { + m[i] = x; + } else { + m[i] = new Mixin(undefined, x); + } + } + + this.mixins = m; + } else { + this.mixins = undefined; + } + this.ownerConstructor = undefined; + } + + Mixin._apply = applyMixin; + + Mixin.applyPartial = function (obj) { + var args = a_slice.call(arguments, 1); + return applyMixin(obj, args, true); + }; + + Mixin.finishPartial = finishPartial; + + // ES6TODO: this relies on a global state? + Ember['default'].anyUnprocessedMixins = false; + + /** + @method create + @static + @param arguments* + */ + Mixin.create = function () { + for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { + args[_key2] = arguments[_key2]; + } + + // ES6TODO: this relies on a global state? + Ember['default'].anyUnprocessedMixins = true; + var M = this; + return new M(args, undefined); + }; + + var MixinPrototype = Mixin.prototype; + + /** + @method reopen + @param arguments* + */ + MixinPrototype.reopen = function () { + var currentMixin; + + if (this.properties) { + currentMixin = new Mixin(undefined, this.properties); + this.properties = undefined; + this.mixins = [currentMixin]; + } else if (!this.mixins) { + this.mixins = []; + } + + var len = arguments.length; + var mixins = this.mixins; + var idx; + + for (idx = 0; idx < len; idx++) { + currentMixin = arguments[idx]; + Ember['default'].assert("Expected hash or Mixin instance, got " + Object.prototype.toString.call(currentMixin), typeof currentMixin === "object" && currentMixin !== null && Object.prototype.toString.call(currentMixin) !== "[object Array]"); + + if (currentMixin instanceof Mixin) { + mixins.push(currentMixin); + } else { + mixins.push(new Mixin(undefined, currentMixin)); + } + } + + return this; + }; + + /** + @method apply + @param obj + @return applied object + */ + MixinPrototype.apply = function (obj) { + return applyMixin(obj, [this], false); + }; + + MixinPrototype.applyPartial = function (obj) { + return applyMixin(obj, [this], true); + }; + + function _detect(curMixin, targetMixin, seen) { + var guid = utils.guidFor(curMixin); + + if (seen[guid]) { + return false; + } + seen[guid] = true; + + if (curMixin === targetMixin) { + return true; + } + var mixins = curMixin.mixins; + var loc = mixins ? mixins.length : 0; + while (--loc >= 0) { + if (_detect(mixins[loc], targetMixin, seen)) { + return true; + } + } + return false; + } + + /** + @method detect + @param obj + @return {Boolean} + */ + MixinPrototype.detect = function (obj) { + if (!obj) { + return false; + } + if (obj instanceof Mixin) { + return _detect(obj, this, {}); + } + var m = obj["__ember_meta__"]; + var mixins = m && m.mixins; + if (mixins) { + return !!mixins[utils.guidFor(this)]; + } + return false; + }; + + MixinPrototype.without = function () { + for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { + args[_key3] = arguments[_key3]; + } + + var ret = new Mixin([this]); + ret._without = args; + return ret; + }; + + function _keys(ret, mixin, seen) { + if (seen[utils.guidFor(mixin)]) { + return; + } + seen[utils.guidFor(mixin)] = true; + + if (mixin.properties) { + var props = mixin.properties; + for (var key in props) { + if (props.hasOwnProperty(key)) { + ret[key] = true; + } + } + } else if (mixin.mixins) { + array.forEach.call(mixin.mixins, function (x) { + _keys(ret, x, seen); + }); + } + } + + MixinPrototype.keys = function () { + var keys = {}; + var seen = {}; + var ret = []; + _keys(keys, this, seen); + for (var key in keys) { + if (keys.hasOwnProperty(key)) { + ret.push(key); + } + } + return ret; + }; + + // returns the mixins currently applied to the specified object + // TODO: Make Ember.mixin + Mixin.mixins = function (obj) { + var m = obj["__ember_meta__"]; + var mixins = m && m.mixins; + var ret = []; + + if (!mixins) { + return ret; + } + + for (var key in mixins) { + var currentMixin = mixins[key]; + + // skip primitive mixins since these are always anonymous + if (!currentMixin.properties) { + ret.push(currentMixin); + } + } + + return ret; + }; + + REQUIRED = new ember_metal__properties.Descriptor(); + REQUIRED.toString = function () { + return "(Required Property)"; + }; + + /** + Denotes a required property for a mixin + + @method required + @for Ember + */ + function required() { + Ember['default'].deprecate("Ember.required is deprecated as its behavior is inconsistent and unreliable.", false); + return REQUIRED; + } + + function Alias(methodName) { + this.isDescriptor = true; + this.methodName = methodName; + } + + Alias.prototype = new ember_metal__properties.Descriptor(); + + /** + Makes a method available via an additional name. + + ```javascript + App.Person = Ember.Object.extend({ + name: function() { + return 'Tomhuda Katzdale'; + }, + moniker: Ember.aliasMethod('name') + }); + + var goodGuy = App.Person.create(); + + goodGuy.name(); // 'Tomhuda Katzdale' + goodGuy.moniker(); // 'Tomhuda Katzdale' + ``` + + @method aliasMethod + @for Ember + @param {String} methodName name of the method to alias + */ + function aliasMethod(methodName) { + return new Alias(methodName); + } + + // .......................................................... + // OBSERVER HELPER + // + + /** + Specify a method that observes property changes. + + ```javascript + Ember.Object.extend({ + valueObserver: Ember.observer('value', function() { + // Executes whenever the "value" property changes + }) + }); + ``` + + In the future this method may become asynchronous. If you want to ensure + synchronous behavior, use `immediateObserver`. + + Also available as `Function.prototype.observes` if prototype extensions are + enabled. + + @method observer + @for Ember + @param {String} propertyNames* + @param {Function} func + @return func + */ + function observer() { + for (var _len4 = arguments.length, args = Array(_len4), _key4 = 0; _key4 < _len4; _key4++) { + args[_key4] = arguments[_key4]; + } + + var func = args.slice(-1)[0]; + var paths; + + var addWatchedProperty = function (path) { + paths.push(path); + }; + var _paths = args.slice(0, -1); + + if (typeof func !== "function") { + // revert to old, soft-deprecated argument ordering + + func = args[0]; + _paths = args.slice(1); + } + + paths = []; + + for (var i = 0; i < _paths.length; ++i) { + expandProperties['default'](_paths[i], addWatchedProperty); + } + + if (typeof func !== "function") { + throw new Ember['default'].Error("Ember.observer called without a function"); + } + + func.__ember_observes__ = paths; + return func; + } + + /** + Specify a method that observes property changes. + + ```javascript + Ember.Object.extend({ + valueObserver: Ember.immediateObserver('value', function() { + // Executes whenever the "value" property changes + }) + }); + ``` + + In the future, `Ember.observer` may become asynchronous. In this event, + `Ember.immediateObserver` will maintain the synchronous behavior. + + Also available as `Function.prototype.observesImmediately` if prototype extensions are + enabled. + + @method immediateObserver + @for Ember + @param {String} propertyNames* + @param {Function} func + @return func + */ + function immediateObserver() { + for (var i = 0, l = arguments.length; i < l; i++) { + var arg = arguments[i]; + Ember['default'].assert("Immediate observers must observe internal properties only, not properties on other objects.", typeof arg !== "string" || arg.indexOf(".") === -1); + } + + return observer.apply(this, arguments); + } + + /** + When observers fire, they are called with the arguments `obj`, `keyName`. + + Note, `@each.property` observer is called per each add or replace of an element + and it's not called with a specific enumeration item. + + A `beforeObserver` fires before a property changes. + + A `beforeObserver` is an alternative form of `.observesBefore()`. + + ```javascript + App.PersonView = Ember.View.extend({ + friends: [{ name: 'Tom' }, { name: 'Stefan' }, { name: 'Kris' }], + + valueWillChange: Ember.beforeObserver('content.value', function(obj, keyName) { + this.changingFrom = obj.get(keyName); + }), + + valueDidChange: Ember.observer('content.value', function(obj, keyName) { + // only run if updating a value already in the DOM + if (this.get('state') === 'inDOM') { + var color = obj.get(keyName) > this.changingFrom ? 'green' : 'red'; + // logic + } + }), + + friendsDidChange: Ember.observer('friends.@each.name', function(obj, keyName) { + // some logic + // obj.get(keyName) returns friends array + }) + }); + ``` + + Also available as `Function.prototype.observesBefore` if prototype extensions are + enabled. + + @method beforeObserver + @for Ember + @param {String} propertyNames* + @param {Function} func + @return func + */ + function beforeObserver() { + for (var _len5 = arguments.length, args = Array(_len5), _key5 = 0; _key5 < _len5; _key5++) { + args[_key5] = arguments[_key5]; + } + + var func = args.slice(-1)[0]; + var paths; + + var addWatchedProperty = function (path) { + paths.push(path); + }; + + var _paths = args.slice(0, -1); + + if (typeof func !== "function") { + // revert to old, soft-deprecated argument ordering + + func = args[0]; + _paths = args.slice(1); + } + + paths = []; + + for (var i = 0; i < _paths.length; ++i) { + expandProperties['default'](_paths[i], addWatchedProperty); + } + + if (typeof func !== "function") { + throw new Ember['default'].Error("Ember.beforeObserver called without a function"); + } + + func.__ember_observesBefore__ = paths; + return func; + } + + exports.IS_BINDING = IS_BINDING; + exports.REQUIRED = REQUIRED; + +}); +enifed('ember-metal/observer', ['exports', 'ember-metal/watching', 'ember-metal/array', 'ember-metal/events'], function (exports, watching, array, ember_metal__events) { + + 'use strict'; + + exports.addObserver = addObserver; + exports.observersFor = observersFor; + exports.removeObserver = removeObserver; + exports.addBeforeObserver = addBeforeObserver; + exports._suspendBeforeObserver = _suspendBeforeObserver; + exports._suspendObserver = _suspendObserver; + exports._suspendBeforeObservers = _suspendBeforeObservers; + exports._suspendObservers = _suspendObservers; + exports.beforeObserversFor = beforeObserversFor; + exports.removeBeforeObserver = removeBeforeObserver; + + var AFTER_OBSERVERS = ":change"; + var BEFORE_OBSERVERS = ":before"; + + function changeEvent(keyName) { + return keyName + AFTER_OBSERVERS; + } + + function beforeEvent(keyName) { + return keyName + BEFORE_OBSERVERS; + } + + /** + @method addObserver + @for Ember + @param obj + @param {String} path + @param {Object|Function} targetOrMethod + @param {Function|String} [method] + */ + function addObserver(obj, _path, target, method) { + ember_metal__events.addListener(obj, changeEvent(_path), target, method); + watching.watch(obj, _path); + + return this; + } + + function observersFor(obj, path) { + return ember_metal__events.listenersFor(obj, changeEvent(path)); + } + + /** + @method removeObserver + @for Ember + @param obj + @param {String} path + @param {Object|Function} target + @param {Function|String} [method] + */ + function removeObserver(obj, path, target, method) { + watching.unwatch(obj, path); + ember_metal__events.removeListener(obj, changeEvent(path), target, method); + + return this; + } + + /** + @method addBeforeObserver + @for Ember + @param obj + @param {String} path + @param {Object|Function} target + @param {Function|String} [method] + */ + function addBeforeObserver(obj, path, target, method) { + ember_metal__events.addListener(obj, beforeEvent(path), target, method); + watching.watch(obj, path); + + return this; + } + + // Suspend observer during callback. + // + // This should only be used by the target of the observer + // while it is setting the observed path. + + function _suspendBeforeObserver(obj, path, target, method, callback) { + return ember_metal__events.suspendListener(obj, beforeEvent(path), target, method, callback); + } + + function _suspendObserver(obj, path, target, method, callback) { + return ember_metal__events.suspendListener(obj, changeEvent(path), target, method, callback); + } + + function _suspendBeforeObservers(obj, paths, target, method, callback) { + var events = array.map.call(paths, beforeEvent); + return ember_metal__events.suspendListeners(obj, events, target, method, callback); + } + + function _suspendObservers(obj, paths, target, method, callback) { + var events = array.map.call(paths, changeEvent); + return ember_metal__events.suspendListeners(obj, events, target, method, callback); + } + + function beforeObserversFor(obj, path) { + return ember_metal__events.listenersFor(obj, beforeEvent(path)); + } + + /** + @method removeBeforeObserver + @for Ember + @param obj + @param {String} path + @param {Object|Function} target + @param {Function|String} [method] + */ + function removeBeforeObserver(obj, path, target, method) { + watching.unwatch(obj, path); + ember_metal__events.removeListener(obj, beforeEvent(path), target, method); + + return this; + } + +}); +enifed('ember-metal/observer_set', ['exports', 'ember-metal/utils', 'ember-metal/events'], function (exports, utils, events) { + + 'use strict'; + + exports['default'] = ObserverSet; + function ObserverSet() { + this.clear(); + } + + ObserverSet.prototype.add = function (sender, keyName, eventName) { + var observerSet = this.observerSet; + var observers = this.observers; + var senderGuid = utils.guidFor(sender); + var keySet = observerSet[senderGuid]; + var index; + + if (!keySet) { + observerSet[senderGuid] = keySet = {}; + } + index = keySet[keyName]; + if (index === undefined) { + index = observers.push({ + sender: sender, + keyName: keyName, + eventName: eventName, + listeners: [] + }) - 1; + keySet[keyName] = index; + } + return observers[index].listeners; + }; + + ObserverSet.prototype.flush = function () { + var observers = this.observers; + var i, len, observer, sender; + this.clear(); + for (i = 0, len = observers.length; i < len; ++i) { + observer = observers[i]; + sender = observer.sender; + if (sender.isDestroying || sender.isDestroyed) { + continue; + } + events.sendEvent(sender, observer.eventName, [sender, observer.keyName], observer.listeners); + } + }; + + ObserverSet.prototype.clear = function () { + this.observerSet = {}; + this.observers = []; + }; + +}); +enifed('ember-metal/path_cache', ['exports', 'ember-metal/cache'], function (exports, Cache) { + + 'use strict'; + + exports.isGlobal = isGlobal; + exports.isGlobalPath = isGlobalPath; + exports.hasThis = hasThis; + exports.isPath = isPath; + exports.getFirstKey = getFirstKey; + exports.getTailPath = getTailPath; + + var IS_GLOBAL = /^[A-Z$]/; + var IS_GLOBAL_PATH = /^[A-Z$].*[\.]/; + var HAS_THIS = 'this.'; + + var isGlobalCache = new Cache['default'](1000, function (key) { + return IS_GLOBAL.test(key); + }); + + var isGlobalPathCache = new Cache['default'](1000, function (key) { + return IS_GLOBAL_PATH.test(key); + }); + + var hasThisCache = new Cache['default'](1000, function (key) { + return key.lastIndexOf(HAS_THIS, 0) === 0; + }); + + var firstDotIndexCache = new Cache['default'](1000, function (key) { + return key.indexOf('.'); + }); + + var firstKeyCache = new Cache['default'](1000, function (path) { + var index = firstDotIndexCache.get(path); + if (index === -1) { + return path; + } else { + return path.slice(0, index); + } + }); + + var tailPathCache = new Cache['default'](1000, function (path) { + var index = firstDotIndexCache.get(path); + if (index !== -1) { + return path.slice(index + 1); + } + }); + + var caches = { + isGlobalCache: isGlobalCache, + isGlobalPathCache: isGlobalPathCache, + hasThisCache: hasThisCache, + firstDotIndexCache: firstDotIndexCache, + firstKeyCache: firstKeyCache, + tailPathCache: tailPathCache + };function isGlobal(path) { + return isGlobalCache.get(path); + } + + function isGlobalPath(path) { + return isGlobalPathCache.get(path); + } + + function hasThis(path) { + return hasThisCache.get(path); + } + + function isPath(path) { + return firstDotIndexCache.get(path) !== -1; + } + + function getFirstKey(path) { + return firstKeyCache.get(path); + } + + function getTailPath(path) { + return tailPathCache.get(path); + } + + exports.caches = caches; + +}); +enifed('ember-metal/platform/create', ['exports', 'ember-metal/platform/define_properties'], function (exports, defineProperties) { + + + + + 'REMOVE_USE_STRICT: true'; /** + @class platform + @namespace Ember + @static + */ + + /** + Identical to `Object.create()`. Implements if not available natively. + + @since 1.8.0 + @method create + @for Ember + */ + var create; + // ES5 15.2.3.5 + // http://es5.github.com/#x15.2.3.5 + if (!(Object.create && !Object.create(null).hasOwnProperty)) { + /* jshint scripturl:true, proto:true */ + // Contributed by Brandon Benvie, October, 2012 + var createEmpty; + var supportsProto = !({ '__proto__': null } instanceof Object); + // the following produces false positives + // in Opera Mini => not a reliable check + // Object.prototype.__proto__ === null + if (supportsProto || typeof document === 'undefined') { + createEmpty = function () { + return { '__proto__': null }; + }; + } else { + // In old IE __proto__ can't be used to manually set `null`, nor does + // any other method exist to make an object that inherits from nothing, + // aside from Object.prototype itself. Instead, create a new global + // object and *steal* its Object.prototype and strip it bare. This is + // used as the prototype to create nullary objects. + createEmpty = function () { + var iframe = document.createElement('iframe'); + var parent = document.body || document.documentElement; + iframe.style.display = 'none'; + parent.appendChild(iframe); + iframe.src = 'javascript:'; + var empty = iframe.contentWindow.Object.prototype; + parent.removeChild(iframe); + iframe = null; + delete empty.constructor; + delete empty.hasOwnProperty; + delete empty.propertyIsEnumerable; + delete empty.isPrototypeOf; + delete empty.toLocaleString; + delete empty.toString; + delete empty.valueOf; + + function Empty() {} + Empty.prototype = empty; + // short-circuit future calls + createEmpty = function () { + return new Empty(); + }; + return new Empty(); + }; + } + + create = Object.create = function create(prototype, properties) { + + var object; + function Type() {} // An empty constructor. + + if (prototype === null) { + object = createEmpty(); + } else { + if (typeof prototype !== 'object' && typeof prototype !== 'function') { + // In the native implementation `parent` can be `null` + // OR *any* `instanceof Object` (Object|Function|Array|RegExp|etc) + // Use `typeof` tho, b/c in old IE, DOM elements are not `instanceof Object` + // like they are in modern browsers. Using `Object.create` on DOM elements + // is...err...probably inappropriate, but the native version allows for it. + throw new TypeError('Object prototype may only be an Object or null'); // same msg as Chrome + } + + Type.prototype = prototype; + + object = new Type(); + } + + if (properties !== undefined) { + defineProperties['default'](object, properties); + } + + return object; + }; + } else { + create = Object.create; + } + + exports['default'] = create; + +}); +enifed('ember-metal/platform/define_properties', ['exports', 'ember-metal/platform/define_property'], function (exports, define_property) { + + 'use strict'; + + var defineProperties = Object.defineProperties; + + // ES5 15.2.3.7 + // http://es5.github.com/#x15.2.3.7 + if (!defineProperties) { + defineProperties = function defineProperties(object, properties) { + for (var property in properties) { + if (properties.hasOwnProperty(property) && property !== "__proto__") { + define_property.defineProperty(object, property, properties[property]); + } + } + return object; + }; + + Object.defineProperties = defineProperties; + } + + exports['default'] = defineProperties; + +}); +enifed('ember-metal/platform/define_property', ['exports'], function (exports) { + + 'use strict'; + + /*globals Node */ + + /** + @class platform + @namespace Ember + @static + */ + + /** + Set to true if the platform supports native getters and setters. + + @property hasPropertyAccessors + @final + */ + + /** + Identical to `Object.defineProperty()`. Implements as much functionality + as possible if not available natively. + + @method defineProperty + @param {Object} obj The object to modify + @param {String} keyName property name to modify + @param {Object} desc descriptor hash + @return {void} + */ + var defineProperty = (function checkCompliance(defineProperty) { + if (!defineProperty) { + return; + } + + try { + var a = 5; + var obj = {}; + defineProperty(obj, 'a', { + configurable: true, + enumerable: true, + get: function () { + return a; + }, + set: function (v) { + a = v; + } + }); + if (obj.a !== 5) { + return; + } + + obj.a = 10; + if (a !== 10) { + return; + } + + // check non-enumerability + defineProperty(obj, 'a', { + configurable: true, + enumerable: false, + writable: true, + value: true + }); + for (var key in obj) { + if (key === 'a') { + return; + } + } + + // Detects a bug in Android <3.2 where you cannot redefine a property using + // Object.defineProperty once accessors have already been set. + if (obj.a !== true) { + return; + } + + // Detects a bug in Android <3 where redefining a property without a value changes the value + // Object.defineProperty once accessors have already been set. + defineProperty(obj, 'a', { + enumerable: false + }); + if (obj.a !== true) { + return; + } + + // defineProperty is compliant + return defineProperty; + } catch (e) { + // IE8 defines Object.defineProperty but calling it on an Object throws + return; + } + })(Object.defineProperty); + + var hasES5CompliantDefineProperty = !!defineProperty; + + if (hasES5CompliantDefineProperty && typeof document !== 'undefined') { + // This is for Safari 5.0, which supports Object.defineProperty, but not + // on DOM nodes. + var canDefinePropertyOnDOM = (function () { + try { + defineProperty(document.createElement('div'), 'definePropertyOnDOM', {}); + return true; + } catch (e) {} + + return false; + })(); + + if (!canDefinePropertyOnDOM) { + defineProperty = function (obj, keyName, desc) { + var isNode; + + if (typeof Node === 'object') { + isNode = obj instanceof Node; + } else { + isNode = typeof obj === 'object' && typeof obj.nodeType === 'number' && typeof obj.nodeName === 'string'; + } + + if (isNode) { + // TODO: Should we have a warning here? + return obj[keyName] = desc.value; + } else { + return Object.defineProperty(obj, keyName, desc); + } + }; + } + } + + if (!hasES5CompliantDefineProperty) { + defineProperty = function definePropertyPolyfill(obj, keyName, desc) { + if (!desc.get) { + obj[keyName] = desc.value; + } + }; + } + + var hasPropertyAccessors = hasES5CompliantDefineProperty; + var canDefineNonEnumerableProperties = hasES5CompliantDefineProperty; + + exports.hasES5CompliantDefineProperty = hasES5CompliantDefineProperty; + exports.defineProperty = defineProperty; + exports.hasPropertyAccessors = hasPropertyAccessors; + exports.canDefineNonEnumerableProperties = canDefineNonEnumerableProperties; + +}); +enifed('ember-metal/properties', ['exports', 'ember-metal/core', 'ember-metal/utils', 'ember-metal/platform/define_property', 'ember-metal/property_events'], function (exports, Ember, utils, define_property, property_events) { + + 'use strict'; + + exports.Descriptor = Descriptor; + exports.MANDATORY_SETTER_FUNCTION = MANDATORY_SETTER_FUNCTION; + exports.DEFAULT_GETTER_FUNCTION = DEFAULT_GETTER_FUNCTION; + exports.defineProperty = defineProperty; + + function Descriptor() { + this.isDescriptor = true; + } + + // .......................................................... + // DEFINING PROPERTIES API + // + + function MANDATORY_SETTER_FUNCTION(name) { + return function SETTER_FUNCTION(value) { + Ember['default'].assert("You must use Ember.set() to set the `" + name + "` property (of " + this + ") to `" + value + "`.", false); + }; + } + + function DEFAULT_GETTER_FUNCTION(name) { + return function GETTER_FUNCTION() { + var meta = this["__ember_meta__"]; + return meta && meta.values[name]; + }; + } + + /** + NOTE: This is a low-level method used by other parts of the API. You almost + never want to call this method directly. Instead you should use + `Ember.mixin()` to define new properties. + + Defines a property on an object. This method works much like the ES5 + `Object.defineProperty()` method except that it can also accept computed + properties and other special descriptors. + + Normally this method takes only three parameters. However if you pass an + instance of `Descriptor` as the third param then you can pass an + optional value as the fourth parameter. This is often more efficient than + creating new descriptor hashes for each property. + + ## Examples + + ```javascript + // ES5 compatible mode + Ember.defineProperty(contact, 'firstName', { + writable: true, + configurable: false, + enumerable: true, + value: 'Charles' + }); + + // define a simple property + Ember.defineProperty(contact, 'lastName', undefined, 'Jolley'); + + // define a computed property + Ember.defineProperty(contact, 'fullName', Ember.computed(function() { + return this.firstName+' '+this.lastName; + }).property('firstName', 'lastName')); + ``` + + @private + @method defineProperty + @for Ember + @param {Object} obj the object to define this property on. This may be a prototype. + @param {String} keyName the name of the property + @param {Descriptor} [desc] an instance of `Descriptor` (typically a + computed property) or an ES5 descriptor. + You must provide this or `data` but not both. + @param {*} [data] something other than a descriptor, that will + become the explicit value of this property. + */ + function defineProperty(obj, keyName, desc, data, meta) { + var possibleDesc, existingDesc, watching, value; + + if (!meta) { + meta = utils.meta(obj); + } + var watchEntry = meta.watching[keyName]; + possibleDesc = obj[keyName]; + existingDesc = possibleDesc !== null && typeof possibleDesc === "object" && possibleDesc.isDescriptor ? possibleDesc : undefined; + + watching = watchEntry !== undefined && watchEntry > 0; + + if (existingDesc) { + existingDesc.teardown(obj, keyName); + } + + if (desc instanceof Descriptor) { + value = desc; + + + if (watching && define_property.hasPropertyAccessors) { + define_property.defineProperty(obj, keyName, { + configurable: true, + enumerable: true, + writable: true, + value: value + }); + } else { + obj[keyName] = value; + } + if (desc.setup) { + desc.setup(obj, keyName); + } + } else { + if (desc == null) { + value = data; + + + if (watching && define_property.hasPropertyAccessors) { + meta.values[keyName] = data; + define_property.defineProperty(obj, keyName, { + configurable: true, + enumerable: true, + set: MANDATORY_SETTER_FUNCTION(keyName), + get: DEFAULT_GETTER_FUNCTION(keyName) + }); + } else { + obj[keyName] = data; + } + } else { + value = desc; + + // compatibility with ES5 + define_property.defineProperty(obj, keyName, desc); + } + } + + // if key is being watched, override chains that + // were initialized with the prototype + if (watching) { + property_events.overrideChains(obj, keyName, meta); + } + + // The `value` passed to the `didDefineProperty` hook is + // either the descriptor or data, whichever was passed. + if (obj.didDefineProperty) { + obj.didDefineProperty(obj, keyName, value); + } + + return this; + } + +}); +enifed('ember-metal/property_events', ['exports', 'ember-metal/utils', 'ember-metal/events', 'ember-metal/observer_set'], function (exports, utils, ember_metal__events, ObserverSet) { + + 'use strict'; + + exports.propertyWillChange = propertyWillChange; + exports.propertyDidChange = propertyDidChange; + exports.overrideChains = overrideChains; + exports.beginPropertyChanges = beginPropertyChanges; + exports.endPropertyChanges = endPropertyChanges; + exports.changeProperties = changeProperties; + + var beforeObserverSet = new ObserverSet['default'](); + var observerSet = new ObserverSet['default'](); + var deferred = 0; + + // .......................................................... + // PROPERTY CHANGES + // + + /** + This function is called just before an object property is about to change. + It will notify any before observers and prepare caches among other things. + + Normally you will not need to call this method directly but if for some + reason you can't directly watch a property you can invoke this method + manually along with `Ember.propertyDidChange()` which you should call just + after the property value changes. + + @method propertyWillChange + @for Ember + @param {Object} obj The object with the property that will change + @param {String} keyName The property key (or path) that will change. + @return {void} + */ + function propertyWillChange(obj, keyName) { + var m = obj["__ember_meta__"]; + var watching = m && m.watching[keyName] > 0 || keyName === "length"; + var proto = m && m.proto; + var possibleDesc = obj[keyName]; + var desc = possibleDesc !== null && typeof possibleDesc === "object" && possibleDesc.isDescriptor ? possibleDesc : undefined; + + if (!watching) { + return; + } + + if (proto === obj) { + return; + } + + if (desc && desc.willChange) { + desc.willChange(obj, keyName); + } + + dependentKeysWillChange(obj, keyName, m); + chainsWillChange(obj, keyName, m); + notifyBeforeObservers(obj, keyName); + } + + /** + This function is called just after an object property has changed. + It will notify any observers and clear caches among other things. + + Normally you will not need to call this method directly but if for some + reason you can't directly watch a property you can invoke this method + manually along with `Ember.propertyWillChange()` which you should call just + before the property value changes. + + @method propertyDidChange + @for Ember + @param {Object} obj The object with the property that will change + @param {String} keyName The property key (or path) that will change. + @return {void} + */ + function propertyDidChange(obj, keyName) { + var m = obj["__ember_meta__"]; + var watching = m && m.watching[keyName] > 0 || keyName === "length"; + var proto = m && m.proto; + var possibleDesc = obj[keyName]; + var desc = possibleDesc !== null && typeof possibleDesc === "object" && possibleDesc.isDescriptor ? possibleDesc : undefined; + + if (proto === obj) { + return; + } + + // shouldn't this mean that we're watching this key? + if (desc && desc.didChange) { + desc.didChange(obj, keyName); + } + + if (!watching && keyName !== "length") { + return; + } + + if (m && m.deps && m.deps[keyName]) { + dependentKeysDidChange(obj, keyName, m); + } + + chainsDidChange(obj, keyName, m, false); + notifyObservers(obj, keyName); + } + + var WILL_SEEN, DID_SEEN; + // called whenever a property is about to change to clear the cache of any dependent keys (and notify those properties of changes, etc...) + function dependentKeysWillChange(obj, depKey, meta) { + if (obj.isDestroying) { + return; + } + + var deps; + if (meta && meta.deps && (deps = meta.deps[depKey])) { + var seen = WILL_SEEN; + var top = !seen; + + if (top) { + seen = WILL_SEEN = {}; + } + + iterDeps(propertyWillChange, obj, deps, depKey, seen, meta); + + if (top) { + WILL_SEEN = null; + } + } + } + + // called whenever a property has just changed to update dependent keys + function dependentKeysDidChange(obj, depKey, meta) { + if (obj.isDestroying) { + return; + } + + var deps; + if (meta && meta.deps && (deps = meta.deps[depKey])) { + var seen = DID_SEEN; + var top = !seen; + + if (top) { + seen = DID_SEEN = {}; + } + + iterDeps(propertyDidChange, obj, deps, depKey, seen, meta); + + if (top) { + DID_SEEN = null; + } + } + } + + function keysOf(obj) { + var keys = []; + + for (var key in obj) { + keys.push(key); + } + + return keys; + } + + function iterDeps(method, obj, deps, depKey, seen, meta) { + var keys, key, i, possibleDesc, desc; + var guid = utils.guidFor(obj); + var current = seen[guid]; + + if (!current) { + current = seen[guid] = {}; + } + + if (current[depKey]) { + return; + } + + current[depKey] = true; + + if (deps) { + keys = keysOf(deps); + for (i = 0; i < keys.length; i++) { + key = keys[i]; + possibleDesc = obj[key]; + desc = possibleDesc !== null && typeof possibleDesc === "object" && possibleDesc.isDescriptor ? possibleDesc : undefined; + + if (desc && desc._suspended === obj) { + continue; + } + + method(obj, key); + } + } + } + + function chainsWillChange(obj, keyName, m) { + if (!(m.hasOwnProperty("chainWatchers") && m.chainWatchers[keyName])) { + return; + } + + var nodes = m.chainWatchers[keyName]; + var events = []; + var i, l; + + for (i = 0, l = nodes.length; i < l; i++) { + nodes[i].willChange(events); + } + + for (i = 0, l = events.length; i < l; i += 2) { + propertyWillChange(events[i], events[i + 1]); + } + } + + function chainsDidChange(obj, keyName, m, suppressEvents) { + if (!(m && m.hasOwnProperty("chainWatchers") && m.chainWatchers[keyName])) { + return; + } + + var nodes = m.chainWatchers[keyName]; + var events = suppressEvents ? null : []; + var i, l; + + for (i = 0, l = nodes.length; i < l; i++) { + nodes[i].didChange(events); + } + + if (suppressEvents) { + return; + } + + for (i = 0, l = events.length; i < l; i += 2) { + propertyDidChange(events[i], events[i + 1]); + } + } + + function overrideChains(obj, keyName, m) { + chainsDidChange(obj, keyName, m, true); + } + + /** + @method beginPropertyChanges + @chainable + @private + */ + function beginPropertyChanges() { + deferred++; + } + + /** + @method endPropertyChanges + @private + */ + function endPropertyChanges() { + deferred--; + if (deferred <= 0) { + beforeObserverSet.clear(); + observerSet.flush(); + } + } + + /** + Make a series of property changes together in an + exception-safe way. + + ```javascript + Ember.changeProperties(function() { + obj1.set('foo', mayBlowUpWhenSet); + obj2.set('bar', baz); + }); + ``` + + @method changeProperties + @param {Function} callback + @param [binding] + */ + function changeProperties(callback, binding) { + beginPropertyChanges(); + utils.tryFinally(callback, endPropertyChanges, binding); + } + + function notifyBeforeObservers(obj, keyName) { + if (obj.isDestroying) { + return; + } + + var eventName = keyName + ":before"; + var listeners, added; + if (deferred) { + listeners = beforeObserverSet.add(obj, keyName, eventName); + added = ember_metal__events.accumulateListeners(obj, eventName, listeners); + ember_metal__events.sendEvent(obj, eventName, [obj, keyName], added); + } else { + ember_metal__events.sendEvent(obj, eventName, [obj, keyName]); + } + } + + function notifyObservers(obj, keyName) { + if (obj.isDestroying) { + return; + } + + var eventName = keyName + ":change"; + var listeners; + if (deferred) { + listeners = observerSet.add(obj, keyName, eventName); + ember_metal__events.accumulateListeners(obj, eventName, listeners); + } else { + ember_metal__events.sendEvent(obj, eventName, [obj, keyName]); + } + } + +}); +enifed('ember-metal/property_get', ['exports', 'ember-metal/core', 'ember-metal/error', 'ember-metal/path_cache', 'ember-metal/platform/define_property', 'ember-metal/is_none'], function (exports, Ember, EmberError, path_cache, define_property, isNone) { + + 'use strict'; + + exports.get = get; + exports.normalizeTuple = normalizeTuple; + exports._getPath = _getPath; + exports.getWithDefault = getWithDefault; + + var FIRST_KEY = /^([^\.]+)/; + + // .......................................................... + // GET AND SET + // + // If we are on a platform that supports accessors we can use those. + // Otherwise simulate accessors by looking up the property directly on the + // object. + + /** + Gets the value of a property on an object. If the property is computed, + the function will be invoked. If the property is not defined but the + object implements the `unknownProperty` method then that will be invoked. + + If you plan to run on IE8 and older browsers then you should use this + method anytime you want to retrieve a property on an object that you don't + know for sure is private. (Properties beginning with an underscore '_' + are considered private.) + + On all newer browsers, you only need to use this method to retrieve + properties if the property might not be defined on the object and you want + to respect the `unknownProperty` handler. Otherwise you can ignore this + method. + + Note that if the object itself is `undefined`, this method will throw + an error. + + @method get + @for Ember + @param {Object} obj The object to retrieve from. + @param {String} keyName The property key to retrieve + @return {Object} the property value or `null`. + */ + function get(obj, keyName) { + // Helpers that operate with 'this' within an #each + if (keyName === "") { + return obj; + } + + if (!keyName && "string" === typeof obj) { + keyName = obj; + obj = Ember['default'].lookup; + } + + Ember['default'].assert("Cannot call get with " + keyName + " key.", !!keyName); + Ember['default'].assert("Cannot call get with '" + keyName + "' on an undefined object.", obj !== undefined); + + if (isNone['default'](obj)) { + return _getPath(obj, keyName); + } + + var meta = obj["__ember_meta__"]; + var possibleDesc = obj[keyName]; + var desc = possibleDesc !== null && typeof possibleDesc === "object" && possibleDesc.isDescriptor ? possibleDesc : undefined; + var ret; + + if (desc === undefined && path_cache.isPath(keyName)) { + return _getPath(obj, keyName); + } + + if (desc) { + return desc.get(obj, keyName); + } else { + + if (define_property.hasPropertyAccessors && meta && meta.watching[keyName] > 0) { + ret = meta.values[keyName]; + } else { + ret = obj[keyName]; + } + + if (ret === undefined && "object" === typeof obj && !(keyName in obj) && "function" === typeof obj.unknownProperty) { + return obj.unknownProperty(keyName); + } + + return ret; + } + } + + /** + Normalizes a target/path pair to reflect that actual target/path that should + be observed, etc. This takes into account passing in global property + paths (i.e. a path beginning with a capital letter not defined on the + target). + + @private + @method normalizeTuple + @for Ember + @param {Object} target The current target. May be `null`. + @param {String} path A path on the target or a global property path. + @return {Array} a temporary array with the normalized target/path pair. + */ + function normalizeTuple(target, path) { + var hasThis = path_cache.hasThis(path); + var isGlobal = !hasThis && path_cache.isGlobal(path); + var key; + + if (!target && !isGlobal) { + return [undefined, ""]; + } + + if (hasThis) { + path = path.slice(5); + } + + if (!target || isGlobal) { + target = Ember['default'].lookup; + } + + if (isGlobal && path_cache.isPath(path)) { + key = path.match(FIRST_KEY)[0]; + target = get(target, key); + path = path.slice(key.length + 1); + } + + // must return some kind of path to be valid else other things will break. + validateIsPath(path); + + return [target, path]; + } + + function validateIsPath(path) { + if (!path || path.length === 0) { + throw new EmberError['default']("Object in path " + path + " could not be found or was destroyed."); + } + } + function _getPath(root, path) { + var hasThis, parts, tuple, idx, len; + + // detect complicated paths and normalize them + hasThis = path_cache.hasThis(path); + + if (!root || hasThis) { + tuple = normalizeTuple(root, path); + root = tuple[0]; + path = tuple[1]; + tuple.length = 0; + } + + parts = path.split("."); + len = parts.length; + for (idx = 0; root != null && idx < len; idx++) { + root = get(root, parts[idx], true); + if (root && root.isDestroyed) { + return undefined; + } + } + return root; + } + + function getWithDefault(root, key, defaultValue) { + var value = get(root, key); + + if (value === undefined) { + return defaultValue; + } + return value; + } + + exports['default'] = get; + +}); +enifed('ember-metal/property_set', ['exports', 'ember-metal/core', 'ember-metal/property_get', 'ember-metal/property_events', 'ember-metal/properties', 'ember-metal/error', 'ember-metal/path_cache', 'ember-metal/platform/define_property'], function (exports, Ember, property_get, property_events, properties, EmberError, path_cache, define_property) { + + 'use strict'; + + exports.set = set; + exports.trySet = trySet; + + function set(obj, keyName, value, tolerant) { + if (typeof obj === "string") { + Ember['default'].assert("Path '" + obj + "' must be global if no obj is given.", path_cache.isGlobalPath(obj)); + value = keyName; + keyName = obj; + obj = Ember['default'].lookup; + } + + Ember['default'].assert("Cannot call set with '" + keyName + "' key.", !!keyName); + + if (obj === Ember['default'].lookup) { + return setPath(obj, keyName, value, tolerant); + } + + var meta, possibleDesc, desc; + if (obj) { + meta = obj["__ember_meta__"]; + possibleDesc = obj[keyName]; + desc = possibleDesc !== null && typeof possibleDesc === "object" && possibleDesc.isDescriptor ? possibleDesc : undefined; + } + + var isUnknown, currentValue; + if ((!obj || desc === undefined) && path_cache.isPath(keyName)) { + return setPath(obj, keyName, value, tolerant); + } + + Ember['default'].assert("You need to provide an object and key to `set`.", !!obj && keyName !== undefined); + Ember['default'].assert("calling set on destroyed object", !obj.isDestroyed); + + if (desc) { + desc.set(obj, keyName, value); + } else { + + if (obj !== null && value !== undefined && typeof obj === "object" && obj[keyName] === value) { + return value; + } + + isUnknown = "object" === typeof obj && !(keyName in obj); + + // setUnknownProperty is called if `obj` is an object, + // the property does not already exist, and the + // `setUnknownProperty` method exists on the object + if (isUnknown && "function" === typeof obj.setUnknownProperty) { + obj.setUnknownProperty(keyName, value); + } else if (meta && meta.watching[keyName] > 0) { + if (meta.proto !== obj) { + + if (define_property.hasPropertyAccessors) { + currentValue = meta.values[keyName]; + } else { + currentValue = obj[keyName]; + } + } + // only trigger a change if the value has changed + if (value !== currentValue) { + property_events.propertyWillChange(obj, keyName); + + if (define_property.hasPropertyAccessors) { + if (currentValue === undefined && !(keyName in obj) || !Object.prototype.propertyIsEnumerable.call(obj, keyName)) { + properties.defineProperty(obj, keyName, null, value); // setup mandatory setter + } else { + meta.values[keyName] = value; + } + } else { + obj[keyName] = value; + } + property_events.propertyDidChange(obj, keyName); + } + } else { + obj[keyName] = value; + } + } + return value; + } + + function setPath(root, path, value, tolerant) { + var keyName; + + // get the last part of the path + keyName = path.slice(path.lastIndexOf(".") + 1); + + // get the first part of the part + path = path === keyName ? keyName : path.slice(0, path.length - (keyName.length + 1)); + + // unless the path is this, look up the first part to + // get the root + if (path !== "this") { + root = property_get._getPath(root, path); + } + + if (!keyName || keyName.length === 0) { + throw new EmberError['default']("Property set failed: You passed an empty path"); + } + + if (!root) { + if (tolerant) { + return; + } else { + throw new EmberError['default']("Property set failed: object in path \"" + path + "\" could not be found or was destroyed."); + } + } + + return set(root, keyName, value); + } + + /** + Error-tolerant form of `Ember.set`. Will not blow up if any part of the + chain is `undefined`, `null`, or destroyed. + + This is primarily used when syncing bindings, which may try to update after + an object has been destroyed. + + @method trySet + @for Ember + @param {Object} obj The object to modify. + @param {String} path The property path to set + @param {Object} value The value to set + */ + function trySet(root, path, value) { + return set(root, path, value, true); + } + +}); +enifed('ember-metal/run_loop', ['exports', 'ember-metal/core', 'ember-metal/utils', 'ember-metal/array', 'ember-metal/property_events', 'backburner'], function (exports, Ember, utils, array, property_events, Backburner) { + + 'use strict'; + + function onBegin(current) { + run.currentRunLoop = current; + } + + function onEnd(current, next) { + run.currentRunLoop = next; + } + + // ES6TODO: should Backburner become es6? + var backburner = new Backburner['default'](['sync', 'actions', 'destroy'], { + GUID_KEY: utils.GUID_KEY, + sync: { + before: property_events.beginPropertyChanges, + after: property_events.endPropertyChanges + }, + defaultQueue: 'actions', + onBegin: onBegin, + onEnd: onEnd, + onErrorTarget: Ember['default'], + onErrorMethod: 'onerror' + }); + + // .......................................................... + // run - this is ideally the only public API the dev sees + // + + /** + Runs the passed target and method inside of a RunLoop, ensuring any + deferred actions including bindings and views updates are flushed at the + end. + + Normally you should not need to invoke this method yourself. However if + you are implementing raw event handlers when interfacing with other + libraries or plugins, you should probably wrap all of your code inside this + call. + + ```javascript + run(function() { + // code to be executed within a RunLoop + }); + ``` + + @class run + @namespace Ember + @static + @constructor + @param {Object} [target] target of method to call + @param {Function|String} method Method to invoke. + May be a function or a string. If you pass a string + then it will be looked up on the passed target. + @param {Object} [args*] Any additional arguments you wish to pass to the method. + @return {Object} return value from invoking the passed function. + */ + exports['default'] = run; + function run() { + return backburner.run.apply(backburner, arguments); + } + + /** + If no run-loop is present, it creates a new one. If a run loop is + present it will queue itself to run on the existing run-loops action + queue. + + Please note: This is not for normal usage, and should be used sparingly. + + If invoked when not within a run loop: + + ```javascript + run.join(function() { + // creates a new run-loop + }); + ``` + + Alternatively, if called within an existing run loop: + + ```javascript + run(function() { + // creates a new run-loop + run.join(function() { + // joins with the existing run-loop, and queues for invocation on + // the existing run-loops action queue. + }); + }); + ``` + + @method join + @namespace Ember + @param {Object} [target] target of method to call + @param {Function|String} method Method to invoke. + May be a function or a string. If you pass a string + then it will be looked up on the passed target. + @param {Object} [args*] Any additional arguments you wish to pass to the method. + @return {Object} Return value from invoking the passed function. Please note, + when called within an existing loop, no return value is possible. + */ + run.join = function () { + return backburner.join.apply(backburner, arguments); + }; + + /** + Allows you to specify which context to call the specified function in while + adding the execution of that function to the Ember run loop. This ability + makes this method a great way to asynchronously integrate third-party libraries + into your Ember application. + + `run.bind` takes two main arguments, the desired context and the function to + invoke in that context. Any additional arguments will be supplied as arguments + to the function that is passed in. + + Let's use the creation of a TinyMCE component as an example. Currently, + TinyMCE provides a setup configuration option we can use to do some processing + after the TinyMCE instance is initialized but before it is actually rendered. + We can use that setup option to do some additional setup for our component. + The component itself could look something like the following: + + ```javascript + App.RichTextEditorComponent = Ember.Component.extend({ + initializeTinyMCE: Ember.on('didInsertElement', function() { + tinymce.init({ + selector: '#' + this.$().prop('id'), + setup: Ember.run.bind(this, this.setupEditor) + }); + }), + + setupEditor: function(editor) { + this.set('editor', editor); + + editor.on('change', function() { + console.log('content changed!'); + }); + } + }); + ``` + + In this example, we use Ember.run.bind to bind the setupEditor method to the + context of the App.RichTextEditorComponent and to have the invocation of that + method be safely handled and executed by the Ember run loop. + + @method bind + @namespace Ember + @param {Object} [target] target of method to call + @param {Function|String} method Method to invoke. + May be a function or a string. If you pass a string + then it will be looked up on the passed target. + @param {Object} [args*] Any additional arguments you wish to pass to the method. + @return {Function} returns a new function that will always have a particular context + @since 1.4.0 + */ + run.bind = function () { + for (var _len = arguments.length, curried = Array(_len), _key = 0; _key < _len; _key++) { + curried[_key] = arguments[_key]; + } + + return function () { + for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { + args[_key2] = arguments[_key2]; + } + + return run.join.apply(run, curried.concat(args)); + }; + }; + + run.backburner = backburner; + run.currentRunLoop = null; + run.queues = backburner.queueNames; + + /** + Begins a new RunLoop. Any deferred actions invoked after the begin will + be buffered until you invoke a matching call to `run.end()`. This is + a lower-level way to use a RunLoop instead of using `run()`. + + ```javascript + run.begin(); + // code to be executed within a RunLoop + run.end(); + ``` + + @method begin + @return {void} + */ + run.begin = function () { + backburner.begin(); + }; + + /** + Ends a RunLoop. This must be called sometime after you call + `run.begin()` to flush any deferred actions. This is a lower-level way + to use a RunLoop instead of using `run()`. + + ```javascript + run.begin(); + // code to be executed within a RunLoop + run.end(); + ``` + + @method end + @return {void} + */ + run.end = function () { + backburner.end(); + }; + + /** + Array of named queues. This array determines the order in which queues + are flushed at the end of the RunLoop. You can define your own queues by + simply adding the queue name to this array. Normally you should not need + to inspect or modify this property. + + @property queues + @type Array + @default ['sync', 'actions', 'destroy'] + */ + + /** + Adds the passed target/method and any optional arguments to the named + queue to be executed at the end of the RunLoop. If you have not already + started a RunLoop when calling this method one will be started for you + automatically. + + At the end of a RunLoop, any methods scheduled in this way will be invoked. + Methods will be invoked in an order matching the named queues defined in + the `run.queues` property. + + ```javascript + run.schedule('sync', this, function() { + // this will be executed in the first RunLoop queue, when bindings are synced + console.log('scheduled on sync queue'); + }); + + run.schedule('actions', this, function() { + // this will be executed in the 'actions' queue, after bindings have synced. + console.log('scheduled on actions queue'); + }); + + // Note the functions will be run in order based on the run queues order. + // Output would be: + // scheduled on sync queue + // scheduled on actions queue + ``` + + @method schedule + @param {String} queue The name of the queue to schedule against. + Default queues are 'sync' and 'actions' + @param {Object} [target] target object to use as the context when invoking a method. + @param {String|Function} method The method to invoke. If you pass a string it + will be resolved on the target object at the time the scheduled item is + invoked allowing you to change the target function. + @param {Object} [arguments*] Optional arguments to be passed to the queued method. + @return {void} + */ + run.schedule = function () { + checkAutoRun(); + backburner.schedule.apply(backburner, arguments); + }; + + // Used by global test teardown + run.hasScheduledTimers = function () { + return backburner.hasTimers(); + }; + + // Used by global test teardown + run.cancelTimers = function () { + backburner.cancelTimers(); + }; + + /** + Immediately flushes any events scheduled in the 'sync' queue. Bindings + use this queue so this method is a useful way to immediately force all + bindings in the application to sync. + + You should call this method anytime you need any changed state to propagate + throughout the app immediately without repainting the UI (which happens + in the later 'render' queue added by the `ember-views` package). + + ```javascript + run.sync(); + ``` + + @method sync + @return {void} + */ + run.sync = function () { + if (backburner.currentInstance) { + backburner.currentInstance.queues.sync.flush(); + } + }; + + /** + Invokes the passed target/method and optional arguments after a specified + period of time. The last parameter of this method must always be a number + of milliseconds. + + You should use this method whenever you need to run some action after a + period of time instead of using `setTimeout()`. This method will ensure that + items that expire during the same script execution cycle all execute + together, which is often more efficient than using a real setTimeout. + + ```javascript + run.later(myContext, function() { + // code here will execute within a RunLoop in about 500ms with this == myContext + }, 500); + ``` + + @method later + @param {Object} [target] target of method to invoke + @param {Function|String} method The method to invoke. + If you pass a string it will be resolved on the + target at the time the method is invoked. + @param {Object} [args*] Optional arguments to pass to the timeout. + @param {Number} wait Number of milliseconds to wait. + @return {*} Timer information for use in cancelling, see `run.cancel`. + */ + run.later = function () { + return backburner.later.apply(backburner, arguments); + }; + + /** + Schedule a function to run one time during the current RunLoop. This is equivalent + to calling `scheduleOnce` with the "actions" queue. + + @method once + @param {Object} [target] The target of the method to invoke. + @param {Function|String} method The method to invoke. + If you pass a string it will be resolved on the + target at the time the method is invoked. + @param {Object} [args*] Optional arguments to pass to the timeout. + @return {Object} Timer information for use in cancelling, see `run.cancel`. + */ + run.once = function () { + for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { + args[_key3] = arguments[_key3]; + } + + checkAutoRun(); + args.unshift('actions'); + return backburner.scheduleOnce.apply(backburner, args); + }; + + /** + Schedules a function to run one time in a given queue of the current RunLoop. + Calling this method with the same queue/target/method combination will have + no effect (past the initial call). + + Note that although you can pass optional arguments these will not be + considered when looking for duplicates. New arguments will replace previous + calls. + + ```javascript + function sayHi() { + console.log('hi'); + } + + run(function() { + run.scheduleOnce('afterRender', myContext, sayHi); + run.scheduleOnce('afterRender', myContext, sayHi); + // sayHi will only be executed once, in the afterRender queue of the RunLoop + }); + ``` + + Also note that passing an anonymous function to `run.scheduleOnce` will + not prevent additional calls with an identical anonymous function from + scheduling the items multiple times, e.g.: + + ```javascript + function scheduleIt() { + run.scheduleOnce('actions', myContext, function() { + console.log('Closure'); + }); + } + + scheduleIt(); + scheduleIt(); + + // "Closure" will print twice, even though we're using `run.scheduleOnce`, + // because the function we pass to it is anonymous and won't match the + // previously scheduled operation. + ``` + + Available queues, and their order, can be found at `run.queues` + + @method scheduleOnce + @param {String} [queue] The name of the queue to schedule against. Default queues are 'sync' and 'actions'. + @param {Object} [target] The target of the method to invoke. + @param {Function|String} method The method to invoke. + If you pass a string it will be resolved on the + target at the time the method is invoked. + @param {Object} [args*] Optional arguments to pass to the timeout. + @return {Object} Timer information for use in cancelling, see `run.cancel`. + */ + run.scheduleOnce = function () { + checkAutoRun(); + return backburner.scheduleOnce.apply(backburner, arguments); + }; + + /** + Schedules an item to run from within a separate run loop, after + control has been returned to the system. This is equivalent to calling + `run.later` with a wait time of 1ms. + + ```javascript + run.next(myContext, function() { + // code to be executed in the next run loop, + // which will be scheduled after the current one + }); + ``` + + Multiple operations scheduled with `run.next` will coalesce + into the same later run loop, along with any other operations + scheduled by `run.later` that expire right around the same + time that `run.next` operations will fire. + + Note that there are often alternatives to using `run.next`. + For instance, if you'd like to schedule an operation to happen + after all DOM element operations have completed within the current + run loop, you can make use of the `afterRender` run loop queue (added + by the `ember-views` package, along with the preceding `render` queue + where all the DOM element operations happen). Example: + + ```javascript + App.MyCollectionView = Ember.CollectionView.extend({ + didInsertElement: function() { + run.scheduleOnce('afterRender', this, 'processChildElements'); + }, + processChildElements: function() { + // ... do something with collectionView's child view + // elements after they've finished rendering, which + // can't be done within the CollectionView's + // `didInsertElement` hook because that gets run + // before the child elements have been added to the DOM. + } + }); + ``` + + One benefit of the above approach compared to using `run.next` is + that you will be able to perform DOM/CSS operations before unprocessed + elements are rendered to the screen, which may prevent flickering or + other artifacts caused by delaying processing until after rendering. + + The other major benefit to the above approach is that `run.next` + introduces an element of non-determinism, which can make things much + harder to test, due to its reliance on `setTimeout`; it's much harder + to guarantee the order of scheduled operations when they are scheduled + outside of the current run loop, i.e. with `run.next`. + + @method next + @param {Object} [target] target of method to invoke + @param {Function|String} method The method to invoke. + If you pass a string it will be resolved on the + target at the time the method is invoked. + @param {Object} [args*] Optional arguments to pass to the timeout. + @return {Object} Timer information for use in cancelling, see `run.cancel`. + */ + run.next = function () { + for (var _len4 = arguments.length, args = Array(_len4), _key4 = 0; _key4 < _len4; _key4++) { + args[_key4] = arguments[_key4]; + } + + args.push(1); + return backburner.later.apply(backburner, args); + }; + + /** + Cancels a scheduled item. Must be a value returned by `run.later()`, + `run.once()`, `run.next()`, `run.debounce()`, or + `run.throttle()`. + + ```javascript + var runNext = run.next(myContext, function() { + // will not be executed + }); + + run.cancel(runNext); + + var runLater = run.later(myContext, function() { + // will not be executed + }, 500); + + run.cancel(runLater); + + var runOnce = run.once(myContext, function() { + // will not be executed + }); + + run.cancel(runOnce); + + var throttle = run.throttle(myContext, function() { + // will not be executed + }, 1, false); + + run.cancel(throttle); + + var debounce = run.debounce(myContext, function() { + // will not be executed + }, 1); + + run.cancel(debounce); + + var debounceImmediate = run.debounce(myContext, function() { + // will be executed since we passed in true (immediate) + }, 100, true); + + // the 100ms delay until this method can be called again will be cancelled + run.cancel(debounceImmediate); + ``` + + @method cancel + @param {Object} timer Timer object to cancel + @return {Boolean} true if cancelled or false/undefined if it wasn't found + */ + run.cancel = function (timer) { + return backburner.cancel(timer); + }; + + /** + Delay calling the target method until the debounce period has elapsed + with no additional debounce calls. If `debounce` is called again before + the specified time has elapsed, the timer is reset and the entire period + must pass again before the target method is called. + + This method should be used when an event may be called multiple times + but the action should only be called once when the event is done firing. + A common example is for scroll events where you only want updates to + happen once scrolling has ceased. + + ```javascript + function whoRan() { + console.log(this.name + ' ran.'); + } + + var myContext = { name: 'debounce' }; + + run.debounce(myContext, whoRan, 150); + + // less than 150ms passes + run.debounce(myContext, whoRan, 150); + + // 150ms passes + // whoRan is invoked with context myContext + // console logs 'debounce ran.' one time. + ``` + + Immediate allows you to run the function immediately, but debounce + other calls for this function until the wait time has elapsed. If + `debounce` is called again before the specified time has elapsed, + the timer is reset and the entire period must pass again before + the method can be called again. + + ```javascript + function whoRan() { + console.log(this.name + ' ran.'); + } + + var myContext = { name: 'debounce' }; + + run.debounce(myContext, whoRan, 150, true); + + // console logs 'debounce ran.' one time immediately. + // 100ms passes + run.debounce(myContext, whoRan, 150, true); + + // 150ms passes and nothing else is logged to the console and + // the debouncee is no longer being watched + run.debounce(myContext, whoRan, 150, true); + + // console logs 'debounce ran.' one time immediately. + // 150ms passes and nothing else is logged to the console and + // the debouncee is no longer being watched + + ``` + + @method debounce + @param {Object} [target] target of method to invoke + @param {Function|String} method The method to invoke. + May be a function or a string. If you pass a string + then it will be looked up on the passed target. + @param {Object} [args*] Optional arguments to pass to the timeout. + @param {Number} wait Number of milliseconds to wait. + @param {Boolean} immediate Trigger the function on the leading instead + of the trailing edge of the wait interval. Defaults to false. + @return {Array} Timer information for use in cancelling, see `run.cancel`. + */ + run.debounce = function () { + return backburner.debounce.apply(backburner, arguments); + }; + + /** + Ensure that the target method is never called more frequently than + the specified spacing period. The target method is called immediately. + + ```javascript + function whoRan() { + console.log(this.name + ' ran.'); + } + + var myContext = { name: 'throttle' }; + + run.throttle(myContext, whoRan, 150); + // whoRan is invoked with context myContext + // console logs 'throttle ran.' + + // 50ms passes + run.throttle(myContext, whoRan, 150); + + // 50ms passes + run.throttle(myContext, whoRan, 150); + + // 150ms passes + run.throttle(myContext, whoRan, 150); + // whoRan is invoked with context myContext + // console logs 'throttle ran.' + ``` + + @method throttle + @param {Object} [target] target of method to invoke + @param {Function|String} method The method to invoke. + May be a function or a string. If you pass a string + then it will be looked up on the passed target. + @param {Object} [args*] Optional arguments to pass to the timeout. + @param {Number} spacing Number of milliseconds to space out requests. + @param {Boolean} immediate Trigger the function on the leading instead + of the trailing edge of the wait interval. Defaults to true. + @return {Array} Timer information for use in cancelling, see `run.cancel`. + */ + run.throttle = function () { + return backburner.throttle.apply(backburner, arguments); + }; + + // Make sure it's not an autorun during testing + function checkAutoRun() { + if (!run.currentRunLoop) { + Ember['default'].assert('You have turned on testing mode, which disabled the run-loop\'s autorun.\n You will need to wrap any code with asynchronous side-effects in a run', !Ember['default'].testing); + } + } + + /** + Add a new named queue after the specified queue. + + The queue to add will only be added once. + + @method _addQueue + @param {String} name the name of the queue to add. + @param {String} after the name of the queue to add after. + @private + */ + run._addQueue = function (name, after) { + if (array.indexOf.call(run.queues, name) === -1) { + run.queues.splice(array.indexOf.call(run.queues, after) + 1, 0, name); + } + }; + /* queue, target, method */ /*target, method*/ /*queue, target, method*/ + +}); +enifed('ember-metal/set_properties', ['exports', 'ember-metal/property_events', 'ember-metal/property_set', 'ember-metal/keys'], function (exports, property_events, property_set, keys) { + + 'use strict'; + + + exports['default'] = setProperties; + function setProperties(obj, properties) { + if (!properties || typeof properties !== "object") { + return obj; + } + property_events.changeProperties(function () { + var props = keys['default'](properties); + var propertyName; + + for (var i = 0, l = props.length; i < l; i++) { + propertyName = props[i]; + + property_set.set(obj, propertyName, properties[propertyName]); + } + }); + return obj; + } + +}); +enifed('ember-metal/streams/conditional', ['exports', 'ember-metal/streams/stream', 'ember-metal/streams/utils', 'ember-metal/platform/create'], function (exports, Stream, utils, create) { + + 'use strict'; + + + + exports['default'] = conditional; + + function conditional(test, consequent, alternate) { + if (utils.isStream(test)) { + return new ConditionalStream(test, consequent, alternate); + } else { + if (test) { + return consequent; + } else { + return alternate; + } + } + } + + function ConditionalStream(test, consequent, alternate) { + this.init(); + + this.oldTestResult = undefined; + this.test = test; + this.consequent = consequent; + this.alternate = alternate; + } + + ConditionalStream.prototype = create['default'](Stream['default'].prototype); + + ConditionalStream.prototype.valueFn = function () { + var oldTestResult = this.oldTestResult; + var newTestResult = !!utils.read(this.test); + + if (newTestResult !== oldTestResult) { + switch (oldTestResult) { + case true: + utils.unsubscribe(this.consequent, this.notify, this);break; + case false: + utils.unsubscribe(this.alternate, this.notify, this);break; + case undefined: + utils.subscribe(this.test, this.notify, this); + } + + switch (newTestResult) { + case true: + utils.subscribe(this.consequent, this.notify, this);break; + case false: + utils.subscribe(this.alternate, this.notify, this); + } + + this.oldTestResult = newTestResult; + } + + return newTestResult ? utils.read(this.consequent) : utils.read(this.alternate); + }; + +}); +enifed('ember-metal/streams/simple', ['exports', 'ember-metal/merge', 'ember-metal/streams/stream', 'ember-metal/platform/create', 'ember-metal/streams/utils'], function (exports, merge, Stream, create, utils) { + + 'use strict'; + + function SimpleStream(source) { + this.init(); + this.source = source; + + if (utils.isStream(source)) { + source.subscribe(this._didChange, this); + } + } + + SimpleStream.prototype = create['default'](Stream['default'].prototype); + + merge['default'](SimpleStream.prototype, { + valueFn: function () { + return utils.read(this.source); + }, + + setValue: function (value) { + var source = this.source; + + if (utils.isStream(source)) { + source.setValue(value); + } + }, + + setSource: function (nextSource) { + var prevSource = this.source; + if (nextSource !== prevSource) { + if (utils.isStream(prevSource)) { + prevSource.unsubscribe(this._didChange, this); + } + + if (utils.isStream(nextSource)) { + nextSource.subscribe(this._didChange, this); + } + + this.source = nextSource; + this.notify(); + } + }, + + _didChange: function () { + this.notify(); + }, + + _super$destroy: Stream['default'].prototype.destroy, + + destroy: function () { + if (this._super$destroy()) { + if (utils.isStream(this.source)) { + this.source.unsubscribe(this._didChange, this); + } + this.source = undefined; + return true; + } + } + }); + + exports['default'] = SimpleStream; + +}); +enifed('ember-metal/streams/stream', ['exports', 'ember-metal/platform/create', 'ember-metal/path_cache'], function (exports, create, path_cache) { + + 'use strict'; + + function Subscriber(callback, context) { + this.next = null; + this.prev = null; + this.callback = callback; + this.context = context; + } + + Subscriber.prototype.removeFrom = function (stream) { + var next = this.next; + var prev = this.prev; + + if (prev) { + prev.next = next; + } else { + stream.subscriberHead = next; + } + + if (next) { + next.prev = prev; + } else { + stream.subscriberTail = prev; + } + }; + + /* + @public + @class Stream + @namespace Ember.stream + @constructor + */ + function Stream(fn) { + this.init(); + this.valueFn = fn; + } + + Stream.prototype = { + isStream: true, + + init: function () { + this.state = "dirty"; + this.cache = undefined; + this.subscriberHead = null; + this.subscriberTail = null; + this.children = undefined; + this._label = undefined; + }, + + get: function (path) { + var firstKey = path_cache.getFirstKey(path); + var tailPath = path_cache.getTailPath(path); + + if (this.children === undefined) { + this.children = create['default'](null); + } + + var keyStream = this.children[firstKey]; + + if (keyStream === undefined) { + keyStream = this._makeChildStream(firstKey, path); + this.children[firstKey] = keyStream; + } + + if (tailPath === undefined) { + return keyStream; + } else { + return keyStream.get(tailPath); + } + }, + + value: function () { + if (this.state === "clean") { + return this.cache; + } else if (this.state === "dirty") { + this.state = "clean"; + return this.cache = this.valueFn(); + } + // TODO: Ensure value is never called on a destroyed stream + // so that we can uncomment this assertion. + // + // Ember.assert("Stream error: value was called in an invalid state: " + this.state); + }, + + valueFn: function () { + throw new Error("Stream error: valueFn not implemented"); + }, + + setValue: function () { + throw new Error("Stream error: setValue not implemented"); + }, + + notify: function () { + this.notifyExcept(); + }, + + notifyExcept: function (callbackToSkip, contextToSkip) { + if (this.state === "clean") { + this.state = "dirty"; + this._notifySubscribers(callbackToSkip, contextToSkip); + } + }, + + subscribe: function (callback, context) { + var subscriber = new Subscriber(callback, context, this); + if (this.subscriberHead === null) { + this.subscriberHead = this.subscriberTail = subscriber; + } else { + var tail = this.subscriberTail; + tail.next = subscriber; + subscriber.prev = tail; + this.subscriberTail = subscriber; + } + + var stream = this; + return function () { + subscriber.removeFrom(stream); + }; + }, + + unsubscribe: function (callback, context) { + var subscriber = this.subscriberHead; + + while (subscriber) { + var next = subscriber.next; + if (subscriber.callback === callback && subscriber.context === context) { + subscriber.removeFrom(this); + } + subscriber = next; + } + }, + + _notifySubscribers: function (callbackToSkip, contextToSkip) { + var subscriber = this.subscriberHead; + + while (subscriber) { + var next = subscriber.next; + + var callback = subscriber.callback; + var context = subscriber.context; + + subscriber = next; + + if (callback === callbackToSkip && context === contextToSkip) { + continue; + } + + if (context === undefined) { + callback(this); + } else { + callback.call(context, this); + } + } + }, + + destroy: function () { + if (this.state !== "destroyed") { + this.state = "destroyed"; + + var children = this.children; + for (var key in children) { + children[key].destroy(); + } + + this.subscriberHead = this.subscriberTail = null; + + return true; + } + }, + + isGlobal: function () { + var stream = this; + while (stream !== undefined) { + if (stream._isRoot) { + return stream._isGlobal; + } + stream = stream.source; + } + } + }; + + exports['default'] = Stream; + +}); +enifed('ember-metal/streams/stream_binding', ['exports', 'ember-metal/platform/create', 'ember-metal/merge', 'ember-metal/run_loop', 'ember-metal/streams/stream'], function (exports, create, merge, run, Stream) { + + 'use strict'; + + function StreamBinding(stream) { + Ember.assert("StreamBinding error: tried to bind to object that is not a stream", stream && stream.isStream); + + this.init(); + this.stream = stream; + this.senderCallback = undefined; + this.senderContext = undefined; + this.senderValue = undefined; + + stream.subscribe(this._onNotify, this); + } + + StreamBinding.prototype = create['default'](Stream['default'].prototype); + + merge['default'](StreamBinding.prototype, { + valueFn: function () { + return this.stream.value(); + }, + + _onNotify: function () { + this._scheduleSync(undefined, undefined, this); + }, + + setValue: function (value, callback, context) { + this._scheduleSync(value, callback, context); + }, + + _scheduleSync: function (value, callback, context) { + if (this.senderCallback === undefined && this.senderContext === undefined) { + this.senderCallback = callback; + this.senderContext = context; + this.senderValue = value; + run['default'].schedule("sync", this, this._sync); + } else if (this.senderContext !== this) { + this.senderCallback = callback; + this.senderContext = context; + this.senderValue = value; + } + }, + + _sync: function () { + if (this.state === "destroyed") { + return; + } + + if (this.senderContext !== this) { + this.stream.setValue(this.senderValue); + } + + var senderCallback = this.senderCallback; + var senderContext = this.senderContext; + this.senderCallback = undefined; + this.senderContext = undefined; + this.senderValue = undefined; + + // Force StreamBindings to always notify + this.state = "clean"; + + this.notifyExcept(senderCallback, senderContext); + }, + + _super$destroy: Stream['default'].prototype.destroy, + + destroy: function () { + if (this._super$destroy()) { + this.stream.unsubscribe(this._onNotify, this); + return true; + } + } + }); + + exports['default'] = StreamBinding; + +}); +enifed('ember-metal/streams/utils', ['exports', './stream'], function (exports, Stream) { + + 'use strict'; + + exports.isStream = isStream; + exports.subscribe = subscribe; + exports.unsubscribe = unsubscribe; + exports.read = read; + exports.readArray = readArray; + exports.readHash = readHash; + exports.scanArray = scanArray; + exports.scanHash = scanHash; + exports.concat = concat; + exports.chain = chain; + + function isStream(object) { + return object && object.isStream; + } + + /* + A method of subscribing to a stream which is safe for use with a non-stream + object. If a non-stream object is passed, the function does nothing. + + @public + @for Ember.stream + @function subscribe + @param {Object|Stream} object object or stream to potentially subscribe to + @param {Function} callback function to run when stream value changes + @param {Object} [context] the callback will be executed with this context if it + is provided + */ + function subscribe(object, callback, context) { + if (object && object.isStream) { + object.subscribe(callback, context); + } + } + + /* + A method of unsubscribing from a stream which is safe for use with a non-stream + object. If a non-stream object is passed, the function does nothing. + + @public + @for Ember.stream + @function unsubscribe + @param {Object|Stream} object object or stream to potentially unsubscribe from + @param {Function} callback function originally passed to `subscribe()` + @param {Object} [context] object originally passed to `subscribe()` + */ + function unsubscribe(object, callback, context) { + if (object && object.isStream) { + object.unsubscribe(callback, context); + } + } + + /* + Retrieve the value of a stream, or in the case a non-stream object is passed, + return the object itself. + + @public + @for Ember.stream + @function read + @param {Object|Stream} object object to return the value of + @return the stream's current value, or the non-stream object itself + */ + function read(object) { + if (object && object.isStream) { + return object.value(); + } else { + return object; + } + } + + /* + Map an array, replacing any streams with their values. + + @public + @for Ember.stream + @function readArray + @param {Array} array The array to read values from + @return {Array} a new array of the same length with the values of non-stream + objects mapped from their original positions untouched, and + the values of stream objects retaining their original position + and replaced with the stream's current value. + */ + function readArray(array) { + var length = array.length; + var ret = new Array(length); + for (var i = 0; i < length; i++) { + ret[i] = read(array[i]); + } + return ret; + } + + /* + Map a hash, replacing any stream property values with the current value of that + stream. + + @public + @for Ember.stream + @function readHash + @param {Object} object The hash to read keys and values from + @return {Object} a new object with the same keys as the passed object. The + property values in the new object are the original values in + the case of non-stream objects, and the streams' current + values in the case of stream objects. + */ + function readHash(object) { + var ret = {}; + for (var key in object) { + ret[key] = read(object[key]); + } + return ret; + } + + /* + Check whether an array contains any stream values + + @public + @for Ember.stream + @function scanArray + @param {Array} array array given to a handlebars helper + @return {Boolean} `true` if the array contains a stream/bound value, `false` + otherwise + */ + function scanArray(array) { + var length = array.length; + var containsStream = false; + + for (var i = 0; i < length; i++) { + if (isStream(array[i])) { + containsStream = true; + break; + } + } + + return containsStream; + } + + /* + Check whether a hash has any stream property values + + @public + @for Ember.stream + @function scanHash + @param {Object} hash "hash" argument given to a handlebars helper + @return {Boolean} `true` if the object contains a stream/bound value, `false` + otherwise + */ + function scanHash(hash) { + var containsStream = false; + + for (var prop in hash) { + if (isStream(hash[prop])) { + containsStream = true; + break; + } + } + + return containsStream; + } + + /* + Join an array, with any streams replaced by their current values + + @public + @for Ember.stream + @function concat + @param {Array} array An array containing zero or more stream objects and + zero or more non-stream objects + @param {String} separator string to be used to join array elements + @return {String} String with array elements concatenated and joined by the + provided separator, and any stream array members having been + replaced by the current value of the stream + */ + function concat(array, separator) { + // TODO: Create subclass ConcatStream < Stream. Defer + // subscribing to streams until the value() is called. + var hasStream = scanArray(array); + if (hasStream) { + var i, l; + var stream = new Stream['default'](function () { + return readArray(array).join(separator); + }); + + for (i = 0, l = array.length; i < l; i++) { + subscribe(array[i], stream.notify, stream); + } + + return stream; + } else { + return array.join(separator); + } + } + + /* + Generate a new stream by providing a source stream and a function that can + be used to transform the stream's value. In the case of a non-stream object, + returns the result of the function. + + The value to transform would typically be available to the function you pass + to `chain()` via scope. For example: + + ```javascript + var source = ...; // stream returning a number + // or a numeric (non-stream) object + var result = chain(source, function() { + var currentValue = read(source); + return currentValue + 1; + }); + ``` + + In the example, result is a stream if source is a stream, or a number of + source was numeric. + + @public + @for Ember.stream + @function chain + @param {Object|Stream} value A stream or non-stream object + @param {Function} fn function to be run when the stream value changes, or to + be run once in the case of a non-stream object + @return {Object|Stream} In the case of a stream `value` parameter, a new + stream that will be updated with the return value of + the provided function `fn`. In the case of a + non-stream object, the return value of the provided + function `fn`. + */ + function chain(value, fn) { + if (isStream(value)) { + var stream = new Stream['default'](fn); + subscribe(value, stream.notify, stream); + return stream; + } else { + return fn(); + } + } + +}); +enifed('ember-metal/utils', ['exports', 'ember-metal/core', 'ember-metal/platform/create', 'ember-metal/platform/define_property', 'ember-metal/array'], function (exports, Ember, o_create, define_property, array) { + + + exports.uuid = uuid; + exports.generateGuid = generateGuid; + exports.guidFor = guidFor; + exports.getMeta = getMeta; + exports.setMeta = setMeta; + exports.metaPath = metaPath; + exports.wrap = wrap; + exports.makeArray = makeArray; + exports.tryInvoke = tryInvoke; + exports.inspect = inspect; + exports.apply = apply; + exports.applyStr = applyStr; + exports.meta = meta; + exports.typeOf = typeOf; + exports.isArray = isArray; + exports.canInvoke = canInvoke; + + "REMOVE_USE_STRICT: true"; /** + @module ember-metal + */ + + /** + Previously we used `Ember.$.uuid`, however `$.uuid` has been removed from + jQuery master. We'll just bootstrap our own uuid now. + + @private + @return {Number} the uuid + */ + var _uuid = 0; + + /** + Generates a universally unique identifier. This method + is used internally by Ember for assisting with + the generation of GUID's and other unique identifiers + such as `bind-attr` data attributes. + + @public + @return {Number} [description] + */ + function uuid() { + return ++_uuid; + } + + /** + Prefix used for guids through out Ember. + @private + @property GUID_PREFIX + @for Ember + @type String + @final + */ + var GUID_PREFIX = "ember"; + + // Used for guid generation... + var numberCache = []; + var stringCache = {}; + + /** + Strongly hint runtimes to intern the provided string. + + When do I need to use this function? + + For the most part, never. Pre-mature optimization is bad, and often the + runtime does exactly what you need it to, and more often the trade-off isn't + worth it. + + Why? + + Runtimes store strings in at least 2 different representations: + Ropes and Symbols (interned strings). The Rope provides a memory efficient + data-structure for strings created from concatenation or some other string + manipulation like splitting. + + Unfortunately checking equality of different ropes can be quite costly as + runtimes must resort to clever string comparison algorithms. These + algorithms typically cost in proportion to the length of the string. + Luckily, this is where the Symbols (interned strings) shine. As Symbols are + unique by their string content, equality checks can be done by pointer + comparison. + + How do I know if my string is a rope or symbol? + + Typically (warning general sweeping statement, but truthy in runtimes at + present) static strings created as part of the JS source are interned. + Strings often used for comparisons can be interned at runtime if some + criteria are met. One of these criteria can be the size of the entire rope. + For example, in chrome 38 a rope longer then 12 characters will not + intern, nor will segments of that rope. + + Some numbers: http://jsperf.com/eval-vs-keys/8 + + Known Trickâ„¢ + + @private + @return {String} interned version of the provided string + */ + function intern(str) { + var obj = {}; + obj[str] = 1; + for (var key in obj) { + if (key === str) { + return key; + } + } + return str; + } + + /** + A unique key used to assign guids and other private metadata to objects. + If you inspect an object in your browser debugger you will often see these. + They can be safely ignored. + + On browsers that support it, these properties are added with enumeration + disabled so they won't show up when you iterate over your properties. + + @private + @property GUID_KEY + @for Ember + @type String + @final + */ + var GUID_KEY = intern("__ember" + +new Date()); + + var GUID_DESC = { + writable: true, + configurable: true, + enumerable: false, + value: null + }; + + var undefinedDescriptor = { + configurable: true, + writable: true, + enumerable: false, + value: undefined + }; + + var nullDescriptor = { + configurable: true, + writable: true, + enumerable: false, + value: null + }; + + var META_DESC = { + writable: true, + configurable: true, + enumerable: false, + value: null + }; + + var EMBER_META_PROPERTY = { + name: "__ember_meta__", + descriptor: META_DESC + }; + + var GUID_KEY_PROPERTY = { + name: GUID_KEY, + descriptor: nullDescriptor + }; + + var NEXT_SUPER_PROPERTY = { + name: "__nextSuper", + descriptor: undefinedDescriptor + }; + + function generateGuid(obj, prefix) { + if (!prefix) { + prefix = GUID_PREFIX; + } + + var ret = prefix + uuid(); + if (obj) { + if (obj[GUID_KEY] === null) { + obj[GUID_KEY] = ret; + } else { + GUID_DESC.value = ret; + if (obj.__defineNonEnumerable) { + obj.__defineNonEnumerable(GUID_KEY_PROPERTY); + } else { + define_property.defineProperty(obj, GUID_KEY, GUID_DESC); + } + } + } + return ret; + } + + /** + Returns a unique id for the object. If the object does not yet have a guid, + one will be assigned to it. You can call this on any object, + `Ember.Object`-based or not, but be aware that it will add a `_guid` + property. + + You can also use this method on DOM Element objects. + + @private + @method guidFor + @for Ember + @param {Object} obj any object, string, number, Element, or primitive + @return {String} the unique guid for this instance. + */ + function guidFor(obj) { + + // special cases where we don't want to add a key to object + if (obj === undefined) { + return "(undefined)"; + } + + if (obj === null) { + return "(null)"; + } + + var ret; + var type = typeof obj; + + // Don't allow prototype changes to String etc. to change the guidFor + switch (type) { + case "number": + ret = numberCache[obj]; + + if (!ret) { + ret = numberCache[obj] = "nu" + obj; + } + + return ret; + + case "string": + ret = stringCache[obj]; + + if (!ret) { + ret = stringCache[obj] = "st" + uuid(); + } + + return ret; + + case "boolean": + return obj ? "(true)" : "(false)"; + + default: + if (obj[GUID_KEY]) { + return obj[GUID_KEY]; + } + + if (obj === Object) { + return "(Object)"; + } + + if (obj === Array) { + return "(Array)"; + } + + ret = GUID_PREFIX + uuid(); + + if (obj[GUID_KEY] === null) { + obj[GUID_KEY] = ret; + } else { + GUID_DESC.value = ret; + + if (obj.__defineNonEnumerable) { + obj.__defineNonEnumerable(GUID_KEY_PROPERTY); + } else { + define_property.defineProperty(obj, GUID_KEY, GUID_DESC); + } + } + return ret; + } + } + + // .......................................................... + // META + // + function Meta(obj) { + this.watching = {}; + this.cache = undefined; + this.cacheMeta = undefined; + this.source = obj; + this.deps = undefined; + this.listeners = undefined; + this.mixins = undefined; + this.bindings = undefined; + this.chains = undefined; + this.values = undefined; + this.proto = undefined; + } + + Meta.prototype = { + chainWatchers: null // FIXME + }; + + if (!define_property.canDefineNonEnumerableProperties) { + // on platforms that don't support enumerable false + // make meta fail jQuery.isPlainObject() to hide from + // jQuery.extend() by having a property that fails + // hasOwnProperty check. + Meta.prototype.__preventPlainObject__ = true; + + // Without non-enumerable properties, meta objects will be output in JSON + // unless explicitly suppressed + Meta.prototype.toJSON = function () {}; + } + + // Placeholder for non-writable metas. + var EMPTY_META = new Meta(null); + + + if (define_property.hasPropertyAccessors) { + EMPTY_META.values = {}; + } + + + /** + Retrieves the meta hash for an object. If `writable` is true ensures the + hash is writable for this object as well. + + The meta object contains information about computed property descriptors as + well as any watched properties and other information. You generally will + not access this information directly but instead work with higher level + methods that manipulate this hash indirectly. + + @method meta + @for Ember + @private + + @param {Object} obj The object to retrieve meta for + @param {Boolean} [writable=true] Pass `false` if you do not intend to modify + the meta hash, allowing the method to avoid making an unnecessary copy. + @return {Object} the meta hash for an object + */ + function meta(obj, writable) { + var ret = obj.__ember_meta__; + if (writable === false) { + return ret || EMPTY_META; + } + + if (!ret) { + if (define_property.canDefineNonEnumerableProperties) { + if (obj.__defineNonEnumerable) { + obj.__defineNonEnumerable(EMBER_META_PROPERTY); + } else { + define_property.defineProperty(obj, "__ember_meta__", META_DESC); + } + } + + ret = new Meta(obj); + + + if (define_property.hasPropertyAccessors) { + ret.values = {}; + } + + + obj.__ember_meta__ = ret; + } else if (ret.source !== obj) { + if (obj.__defineNonEnumerable) { + obj.__defineNonEnumerable(EMBER_META_PROPERTY); + } else { + define_property.defineProperty(obj, "__ember_meta__", META_DESC); + } + + ret = o_create['default'](ret); + ret.watching = o_create['default'](ret.watching); + ret.cache = undefined; + ret.cacheMeta = undefined; + ret.source = obj; + + + if (define_property.hasPropertyAccessors) { + ret.values = o_create['default'](ret.values); + } + + + obj["__ember_meta__"] = ret; + } + return ret; + } + function getMeta(obj, property) { + var _meta = meta(obj, false); + return _meta[property]; + } + + function setMeta(obj, property, value) { + var _meta = meta(obj, true); + _meta[property] = value; + return value; + } + + /** + @deprecated + @private + + In order to store defaults for a class, a prototype may need to create + a default meta object, which will be inherited by any objects instantiated + from the class's constructor. + + However, the properties of that meta object are only shallow-cloned, + so if a property is a hash (like the event system's `listeners` hash), + it will by default be shared across all instances of that class. + + This method allows extensions to deeply clone a series of nested hashes or + other complex objects. For instance, the event system might pass + `['listeners', 'foo:change', 'ember157']` to `prepareMetaPath`, which will + walk down the keys provided. + + For each key, if the key does not exist, it is created. If it already + exists and it was inherited from its constructor, the constructor's + key is cloned. + + You can also pass false for `writable`, which will simply return + undefined if `prepareMetaPath` discovers any part of the path that + shared or undefined. + + @method metaPath + @for Ember + @param {Object} obj The object whose meta we are examining + @param {Array} path An array of keys to walk down + @param {Boolean} writable whether or not to create a new meta + (or meta property) if one does not already exist or if it's + shared with its constructor + */ + function metaPath(obj, path, writable) { + Ember['default'].deprecate("Ember.metaPath is deprecated and will be removed from future releases."); + var _meta = meta(obj, writable); + var keyName, value; + + for (var i = 0, l = path.length; i < l; i++) { + keyName = path[i]; + value = _meta[keyName]; + + if (!value) { + if (!writable) { + return undefined; + } + value = _meta[keyName] = { __ember_source__: obj }; + } else if (value.__ember_source__ !== obj) { + if (!writable) { + return undefined; + } + value = _meta[keyName] = o_create['default'](value); + value.__ember_source__ = obj; + } + + _meta = value; + } + + return value; + } + + /** + Wraps the passed function so that `this._super` will point to the superFunc + when the function is invoked. This is the primitive we use to implement + calls to super. + + @private + @method wrap + @for Ember + @param {Function} func The function to call + @param {Function} superFunc The super function. + @return {Function} wrapped function. + */ + function wrap(func, superFunc) { + function superWrapper() { + var ret; + var sup = this && this.__nextSuper; + var length = arguments.length; + + if (this) { + this.__nextSuper = superFunc; + } + + if (length === 0) { + ret = func.call(this); + } else if (length === 1) { + ret = func.call(this, arguments[0]); + } else if (length === 2) { + ret = func.call(this, arguments[0], arguments[1]); + } else { + var args = new Array(length); + for (var i = 0; i < length; i++) { + args[i] = arguments[i]; + } + ret = apply(this, func, args); + } + + if (this) { + this.__nextSuper = sup; + } + + return ret; + } + + superWrapper.wrappedFunction = func; + superWrapper.__ember_observes__ = func.__ember_observes__; + superWrapper.__ember_observesBefore__ = func.__ember_observesBefore__; + superWrapper.__ember_listens__ = func.__ember_listens__; + + return superWrapper; + } + + var EmberArray; + + /** + Returns true if the passed object is an array or Array-like. + + Ember Array Protocol: + + - the object has an objectAt property + - the object is a native Array + - the object is an Object, and has a length property + + Unlike `Ember.typeOf` this method returns true even if the passed object is + not formally array but appears to be array-like (i.e. implements `Ember.Array`) + + ```javascript + Ember.isArray(); // false + Ember.isArray([]); // true + Ember.isArray(Ember.ArrayProxy.create({ content: [] })); // true + ``` + + @method isArray + @for Ember + @param {Object} obj The object to test + @return {Boolean} true if the passed object is an array or Array-like + */ + // ES6TODO: Move up to runtime? This is only use in ember-metal by concatenatedProperties + function isArray(obj) { + var modulePath, type; + + if (typeof EmberArray === "undefined") { + modulePath = "ember-runtime/mixins/array"; + if (Ember['default'].__loader.registry[modulePath]) { + EmberArray = Ember['default'].__loader.require(modulePath)["default"]; + } + } + + if (!obj || obj.setInterval) { + return false; + } + if (Array.isArray && Array.isArray(obj)) { + return true; + } + if (EmberArray && EmberArray.detect(obj)) { + return true; + } + + type = typeOf(obj); + if ("array" === type) { + return true; + } + if (obj.length !== undefined && "object" === type) { + return true; + } + return false; + } + + /** + Forces the passed object to be part of an array. If the object is already + an array or array-like, it will return the object. Otherwise, it will add the object to + an array. If obj is `null` or `undefined`, it will return an empty array. + + ```javascript + Ember.makeArray(); // [] + Ember.makeArray(null); // [] + Ember.makeArray(undefined); // [] + Ember.makeArray('lindsay'); // ['lindsay'] + Ember.makeArray([1, 2, 42]); // [1, 2, 42] + + var controller = Ember.ArrayProxy.create({ content: [] }); + + Ember.makeArray(controller) === controller; // true + ``` + + @method makeArray + @for Ember + @param {Object} obj the object + @return {Array} + */ + function makeArray(obj) { + if (obj === null || obj === undefined) { + return []; + } + return isArray(obj) ? obj : [obj]; + } + + /** + Checks to see if the `methodName` exists on the `obj`. + + ```javascript + var foo = { bar: function() { return 'bar'; }, baz: null }; + + Ember.canInvoke(foo, 'bar'); // true + Ember.canInvoke(foo, 'baz'); // false + Ember.canInvoke(foo, 'bat'); // false + ``` + + @method canInvoke + @for Ember + @param {Object} obj The object to check for the method + @param {String} methodName The method name to check for + @return {Boolean} + */ + function canInvoke(obj, methodName) { + return !!(obj && typeof obj[methodName] === "function"); + } + + /** + Checks to see if the `methodName` exists on the `obj`, + and if it does, invokes it with the arguments passed. + + ```javascript + var d = new Date('03/15/2013'); + + Ember.tryInvoke(d, 'getTime'); // 1363320000000 + Ember.tryInvoke(d, 'setFullYear', [2014]); // 1394856000000 + Ember.tryInvoke(d, 'noSuchMethod', [2014]); // undefined + ``` + + @method tryInvoke + @for Ember + @param {Object} obj The object to check for the method + @param {String} methodName The method name to check for + @param {Array} [args] The arguments to pass to the method + @return {*} the return value of the invoked method or undefined if it cannot be invoked + */ + function tryInvoke(obj, methodName, args) { + if (canInvoke(obj, methodName)) { + return args ? applyStr(obj, methodName, args) : applyStr(obj, methodName); + } + } + + // https://github.com/emberjs/ember.js/pull/1617 + var needsFinallyFix = (function () { + var count = 0; + try { + // jscs:disable + try {} finally { + count++; + throw new Error("needsFinallyFixTest"); + } + // jscs:enable + } catch (e) {} + + return count !== 1; + })(); + + /** + Provides try/finally functionality, while working + around Safari's double finally bug. + + ```javascript + var tryable = function() { + someResource.lock(); + runCallback(); // May throw error. + }; + + var finalizer = function() { + someResource.unlock(); + }; + + Ember.tryFinally(tryable, finalizer); + ``` + + @method tryFinally + @deprecated Use JavaScript's native try/finally + @for Ember + @param {Function} tryable The function to run the try callback + @param {Function} finalizer The function to run the finally callback + @param {Object} [binding] The optional calling object. Defaults to 'this' + @return {*} The return value is the that of the finalizer, + unless that value is undefined, in which case it is the return value + of the tryable + */ + + var tryFinally; + if (needsFinallyFix) { + tryFinally = function (tryable, finalizer, binding) { + var result, finalResult, finalError; + + binding = binding || this; + + try { + result = tryable.call(binding); + } finally { + try { + finalResult = finalizer.call(binding); + } catch (e) { + finalError = e; + } + } + + if (finalError) { + throw finalError; + } + + return finalResult === undefined ? result : finalResult; + }; + } else { + tryFinally = function (tryable, finalizer, binding) { + var result, finalResult; + + binding = binding || this; + + try { + result = tryable.call(binding); + } finally { + finalResult = finalizer.call(binding); + } + + return finalResult === undefined ? result : finalResult; + }; + } + + var deprecatedTryFinally = function () { + Ember['default'].deprecate("tryFinally is deprecated. Please use JavaScript's native try/finally.", false); + return tryFinally.apply(this, arguments); + }; + + /** + Provides try/catch/finally functionality, while working + around Safari's double finally bug. + + ```javascript + var tryable = function() { + for (i = 0, l = listeners.length; i < l; i++) { + listener = listeners[i]; + beforeValues[i] = listener.before(name, time(), payload); + } + + return callback.call(binding); + }; + + var catchable = function(e) { + payload = payload || {}; + payload.exception = e; + }; + + var finalizer = function() { + for (i = 0, l = listeners.length; i < l; i++) { + listener = listeners[i]; + listener.after(name, time(), payload, beforeValues[i]); + } + }; + + Ember.tryCatchFinally(tryable, catchable, finalizer); + ``` + + @method tryCatchFinally + @deprecated Use JavaScript's native try/catch/finally instead + @for Ember + @param {Function} tryable The function to run the try callback + @param {Function} catchable The function to run the catchable callback + @param {Function} finalizer The function to run the finally callback + @param {Object} [binding] The optional calling object. Defaults to 'this' + @return {*} The return value is the that of the finalizer, + unless that value is undefined, in which case it is the return value + of the tryable. + */ + var tryCatchFinally; + if (needsFinallyFix) { + tryCatchFinally = function (tryable, catchable, finalizer, binding) { + var result, finalResult, finalError; + + binding = binding || this; + + try { + result = tryable.call(binding); + } catch (error) { + result = catchable.call(binding, error); + } finally { + try { + finalResult = finalizer.call(binding); + } catch (e) { + finalError = e; + } + } + + if (finalError) { + throw finalError; + } + + return finalResult === undefined ? result : finalResult; + }; + } else { + tryCatchFinally = function (tryable, catchable, finalizer, binding) { + var result, finalResult; + + binding = binding || this; + + try { + result = tryable.call(binding); + } catch (error) { + result = catchable.call(binding, error); + } finally { + finalResult = finalizer.call(binding); + } + + return finalResult === undefined ? result : finalResult; + }; + } + + var deprecatedTryCatchFinally = function () { + Ember['default'].deprecate("tryCatchFinally is deprecated. Please use JavaScript's native try/catch/finally.", false); + return tryCatchFinally.apply(this, arguments); + }; + + // ........................................ + // TYPING & ARRAY MESSAGING + // + + var TYPE_MAP = {}; + var t = "Boolean Number String Function Array Date RegExp Object".split(" "); + array.forEach.call(t, function (name) { + TYPE_MAP["[object " + name + "]"] = name.toLowerCase(); + }); + + var toString = Object.prototype.toString; + + var EmberObject; + + /** + Returns a consistent type for the passed item. + + Use this instead of the built-in `typeof` to get the type of an item. + It will return the same result across all browsers and includes a bit + more detail. Here is what will be returned: + + | Return Value | Meaning | + |---------------|------------------------------------------------------| + | 'string' | String primitive or String object. | + | 'number' | Number primitive or Number object. | + | 'boolean' | Boolean primitive or Boolean object. | + | 'null' | Null value | + | 'undefined' | Undefined value | + | 'function' | A function | + | 'array' | An instance of Array | + | 'regexp' | An instance of RegExp | + | 'date' | An instance of Date | + | 'class' | An Ember class (created using Ember.Object.extend()) | + | 'instance' | An Ember object instance | + | 'error' | An instance of the Error object | + | 'object' | A JavaScript object not inheriting from Ember.Object | + + Examples: + + ```javascript + Ember.typeOf(); // 'undefined' + Ember.typeOf(null); // 'null' + Ember.typeOf(undefined); // 'undefined' + Ember.typeOf('michael'); // 'string' + Ember.typeOf(new String('michael')); // 'string' + Ember.typeOf(101); // 'number' + Ember.typeOf(new Number(101)); // 'number' + Ember.typeOf(true); // 'boolean' + Ember.typeOf(new Boolean(true)); // 'boolean' + Ember.typeOf(Ember.makeArray); // 'function' + Ember.typeOf([1, 2, 90]); // 'array' + Ember.typeOf(/abc/); // 'regexp' + Ember.typeOf(new Date()); // 'date' + Ember.typeOf(Ember.Object.extend()); // 'class' + Ember.typeOf(Ember.Object.create()); // 'instance' + Ember.typeOf(new Error('teamocil')); // 'error' + + // 'normal' JavaScript object + Ember.typeOf({ a: 'b' }); // 'object' + ``` + + @method typeOf + @for Ember + @param {Object} item the item to check + @return {String} the type + */ + function typeOf(item) { + var ret, modulePath; + + // ES6TODO: Depends on Ember.Object which is defined in runtime. + if (typeof EmberObject === "undefined") { + modulePath = "ember-runtime/system/object"; + if (Ember['default'].__loader.registry[modulePath]) { + EmberObject = Ember['default'].__loader.require(modulePath)["default"]; + } + } + + ret = item === null || item === undefined ? String(item) : TYPE_MAP[toString.call(item)] || "object"; + + if (ret === "function") { + if (EmberObject && EmberObject.detect(item)) { + ret = "class"; + } + } else if (ret === "object") { + if (item instanceof Error) { + ret = "error"; + } else if (EmberObject && item instanceof EmberObject) { + ret = "instance"; + } else if (item instanceof Date) { + ret = "date"; + } + } + + return ret; + } + + /** + Convenience method to inspect an object. This method will attempt to + convert the object into a useful string description. + + It is a pretty simple implementation. If you want something more robust, + use something like JSDump: https://github.com/NV/jsDump + + @method inspect + @for Ember + @param {Object} obj The object you want to inspect. + @return {String} A description of the object + @since 1.4.0 + */ + function inspect(obj) { + var type = typeOf(obj); + if (type === "array") { + return "[" + obj + "]"; + } + if (type !== "object") { + return obj + ""; + } + + var v; + var ret = []; + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + v = obj[key]; + if (v === "toString") { + continue; + } // ignore useless items + if (typeOf(v) === "function") { + v = "function() { ... }"; + } + + if (v && typeof v.toString !== "function") { + ret.push(key + ": " + toString.call(v)); + } else { + ret.push(key + ": " + v); + } + } + } + return "{" + ret.join(", ") + "}"; + } + + // The following functions are intentionally minified to keep the functions + // below Chrome's function body size inlining limit of 600 chars. + /** + @param {Object} target + @param {Function} method + @param {Array} args + */ + function apply(t, m, a) { + var l = a && a.length; + if (!a || !l) { + return m.call(t); + } + switch (l) { + case 1: + return m.call(t, a[0]); + case 2: + return m.call(t, a[0], a[1]); + case 3: + return m.call(t, a[0], a[1], a[2]); + case 4: + return m.call(t, a[0], a[1], a[2], a[3]); + case 5: + return m.call(t, a[0], a[1], a[2], a[3], a[4]); + default: + return m.apply(t, a); + } + } + + /** + @param {Object} target + @param {String} method + @param {Array} args + */ + function applyStr(t, m, a) { + var l = a && a.length; + if (!a || !l) { + return t[m](); + } + switch (l) { + case 1: + return t[m](a[0]); + case 2: + return t[m](a[0], a[1]); + case 3: + return t[m](a[0], a[1], a[2]); + case 4: + return t[m](a[0], a[1], a[2], a[3]); + case 5: + return t[m](a[0], a[1], a[2], a[3], a[4]); + default: + return t[m].apply(t, a); + } + } + + exports.GUID_DESC = GUID_DESC; + exports.EMBER_META_PROPERTY = EMBER_META_PROPERTY; + exports.GUID_KEY_PROPERTY = GUID_KEY_PROPERTY; + exports.NEXT_SUPER_PROPERTY = NEXT_SUPER_PROPERTY; + exports.GUID_KEY = GUID_KEY; + exports.META_DESC = META_DESC; + exports.EMPTY_META = EMPTY_META; + exports.tryCatchFinally = tryCatchFinally; + exports.deprecatedTryCatchFinally = deprecatedTryCatchFinally; + exports.tryFinally = tryFinally; + exports.deprecatedTryFinally = deprecatedTryFinally; + +}); +enifed('ember-metal/watch_key', ['exports', 'ember-metal/core', 'ember-metal/utils', 'ember-metal/platform/define_property', 'ember-metal/properties'], function (exports, Ember, utils, define_property, properties) { + + 'use strict'; + + exports.watchKey = watchKey; + exports.unwatchKey = unwatchKey; + + function watchKey(obj, keyName, meta) { + // can't watch length on Array - it is special... + if (keyName === "length" && utils.typeOf(obj) === "array") { + return; + } + + var m = meta || utils.meta(obj); + var watching = m.watching; + + // activate watching first time + if (!watching[keyName]) { + watching[keyName] = 1; + + var possibleDesc = obj[keyName]; + var desc = possibleDesc !== null && typeof possibleDesc === "object" && possibleDesc.isDescriptor ? possibleDesc : undefined; + if (desc && desc.willWatch) { + desc.willWatch(obj, keyName); + } + + if ("function" === typeof obj.willWatchProperty) { + obj.willWatchProperty(keyName); + } + + + if (define_property.hasPropertyAccessors) { + handleMandatorySetter(m, obj, keyName); + } + + } else { + watching[keyName] = (watching[keyName] || 0) + 1; + } + } + + + var handleMandatorySetter = function handleMandatorySetter(m, obj, keyName) { + var descriptor = Object.getOwnPropertyDescriptor && Object.getOwnPropertyDescriptor(obj, keyName); + var configurable = descriptor ? descriptor.configurable : true; + var isWritable = descriptor ? descriptor.writable : true; + var hasValue = descriptor ? "value" in descriptor : true; + var possibleDesc = descriptor && descriptor.value; + var isDescriptor = possibleDesc !== null && typeof possibleDesc === "object" && possibleDesc.isDescriptor; + + if (isDescriptor) { + return; + } + + // this x in Y deopts, so keeping it in this function is better; + if (configurable && isWritable && hasValue && keyName in obj) { + m.values[keyName] = obj[keyName]; + define_property.defineProperty(obj, keyName, { + configurable: true, + enumerable: Object.prototype.propertyIsEnumerable.call(obj, keyName), + set: properties.MANDATORY_SETTER_FUNCTION(keyName), + get: properties.DEFAULT_GETTER_FUNCTION(keyName) + }); + } + }; + + + // This is super annoying, but required until + // https://github.com/babel/babel/issues/906 is resolved + ; // jshint ignore:line + + function unwatchKey(obj, keyName, meta) { + var m = meta || utils.meta(obj); + var watching = m.watching; + + if (watching[keyName] === 1) { + watching[keyName] = 0; + + var possibleDesc = obj[keyName]; + var desc = possibleDesc !== null && typeof possibleDesc === "object" && possibleDesc.isDescriptor ? possibleDesc : undefined; + if (desc && desc.didUnwatch) { + desc.didUnwatch(obj, keyName); + } + + if ("function" === typeof obj.didUnwatchProperty) { + obj.didUnwatchProperty(keyName); + } + + + if (!desc && define_property.hasPropertyAccessors && keyName in obj) { + define_property.defineProperty(obj, keyName, { + configurable: true, + enumerable: Object.prototype.propertyIsEnumerable.call(obj, keyName), + set: function (val) { + // redefine to set as enumerable + define_property.defineProperty(obj, keyName, { + configurable: true, + writable: true, + enumerable: true, + value: val + }); + delete m.values[keyName]; + }, + get: properties.DEFAULT_GETTER_FUNCTION(keyName) + }); + } + + } else if (watching[keyName] > 1) { + watching[keyName]--; + } + } + +}); +enifed('ember-metal/watch_path', ['exports', 'ember-metal/utils', 'ember-metal/chains'], function (exports, utils, chains) { + + 'use strict'; + + exports.watchPath = watchPath; + exports.unwatchPath = unwatchPath; + + function chainsFor(obj, meta) { + var m = meta || utils.meta(obj); + var ret = m.chains; + if (!ret) { + ret = m.chains = new chains.ChainNode(null, null, obj); + } else if (ret.value() !== obj) { + ret = m.chains = ret.copy(obj); + } + return ret; + } + function watchPath(obj, keyPath, meta) { + // can't watch length on Array - it is special... + if (keyPath === "length" && utils.typeOf(obj) === "array") { + return; + } + + var m = meta || utils.meta(obj); + var watching = m.watching; + + if (!watching[keyPath]) { + // activate watching first time + watching[keyPath] = 1; + chainsFor(obj, m).add(keyPath); + } else { + watching[keyPath] = (watching[keyPath] || 0) + 1; + } + } + + function unwatchPath(obj, keyPath, meta) { + var m = meta || utils.meta(obj); + var watching = m.watching; + + if (watching[keyPath] === 1) { + watching[keyPath] = 0; + chainsFor(obj, m).remove(keyPath); + } else if (watching[keyPath] > 1) { + watching[keyPath]--; + } + } + +}); +enifed('ember-metal/watching', ['exports', 'ember-metal/utils', 'ember-metal/chains', 'ember-metal/watch_key', 'ember-metal/watch_path', 'ember-metal/path_cache'], function (exports, utils, chains, watch_key, watch_path, path_cache) { + + 'use strict'; + + exports.isWatching = isWatching; + exports.unwatch = unwatch; + exports.destroy = destroy; + exports.watch = watch; + + function watch(obj, _keyPath, m) { + // can't watch length on Array - it is special... + if (_keyPath === "length" && utils.typeOf(obj) === "array") { + return; + } + + if (!path_cache.isPath(_keyPath)) { + watch_key.watchKey(obj, _keyPath, m); + } else { + watch_path.watchPath(obj, _keyPath, m); + } + } + + function isWatching(obj, key) { + var meta = obj["__ember_meta__"]; + return (meta && meta.watching[key]) > 0; + } + + watch.flushPending = chains.flushPendingChains; + function unwatch(obj, _keyPath, m) { + // can't watch length on Array - it is special... + if (_keyPath === "length" && utils.typeOf(obj) === "array") { + return; + } + + if (!path_cache.isPath(_keyPath)) { + watch_key.unwatchKey(obj, _keyPath, m); + } else { + watch_path.unwatchPath(obj, _keyPath, m); + } + } + + var NODE_STACK = []; + + /** + Tears down the meta on an object so that it can be garbage collected. + Multiple calls will have no effect. + + @method destroy + @for Ember + @param {Object} obj the object to destroy + @return {void} + */ + function destroy(obj) { + var meta = obj["__ember_meta__"]; + var node, nodes, key, nodeObject; + + if (meta) { + obj["__ember_meta__"] = null; + // remove chainWatchers to remove circular references that would prevent GC + node = meta.chains; + if (node) { + NODE_STACK.push(node); + // process tree + while (NODE_STACK.length > 0) { + node = NODE_STACK.pop(); + // push children + nodes = node._chains; + if (nodes) { + for (key in nodes) { + if (nodes.hasOwnProperty(key)) { + NODE_STACK.push(nodes[key]); + } + } + } + // remove chainWatcher in node object + if (node._watching) { + nodeObject = node._object; + if (nodeObject) { + chains.removeChainWatcher(nodeObject, node._key, node); + } + } + } + } + } + } + +}); +enifed('ember-template-compiler', ['exports', 'ember-metal/core', 'ember-template-compiler/system/precompile', 'ember-template-compiler/system/compile', 'ember-template-compiler/system/template', 'ember-template-compiler/plugins', 'ember-template-compiler/plugins/transform-each-in-to-hash', 'ember-template-compiler/plugins/transform-with-as-to-hash', 'ember-template-compiler/compat'], function (exports, _Ember, precompile, compile, template, plugins, TransformEachInToHash, TransformWithAsToHash) { + + 'use strict'; + + plugins.registerPlugin("ast", TransformWithAsToHash['default']); + plugins.registerPlugin("ast", TransformEachInToHash['default']); + + exports._Ember = _Ember['default']; + exports.precompile = precompile['default']; + exports.compile = compile['default']; + exports.template = template['default']; + exports.registerPlugin = plugins.registerPlugin; + +}); +enifed('ember-template-compiler/compat', ['ember-metal/core', 'ember-template-compiler/compat/precompile', 'ember-template-compiler/system/compile', 'ember-template-compiler/system/template'], function (Ember, precompile, compile, template) { + + 'use strict'; + + var EmberHandlebars = Ember['default'].Handlebars = Ember['default'].Handlebars || {}; + + EmberHandlebars.precompile = precompile['default']; + EmberHandlebars.compile = compile['default']; + EmberHandlebars.template = template['default']; + +}); +enifed('ember-template-compiler/compat/precompile', ['exports', 'ember-template-compiler/system/compile_options'], function (exports, compileOptions) { + + 'use strict'; + + /** + @module ember + @submodule ember-template-compiler + */ + var compile, compileSpec; + + exports['default'] = function (string) { + if ((!compile || !compileSpec) && Ember.__loader.registry['htmlbars-compiler/compiler']) { + var Compiler = requireModule('htmlbars-compiler/compiler'); + + compile = Compiler.compile; + compileSpec = Compiler.compileSpec; + } + + if (!compile || !compileSpec) { + throw new Error('Cannot call `precompile` without the template compiler loaded. Please load `ember-template-compiler.js` prior to calling `precompile`.'); + } + + var asObject = arguments[1] === undefined ? true : arguments[1]; + var compileFunc = asObject ? compile : compileSpec; + + return compileFunc(string, compileOptions['default']()); + } + +}); +enifed('ember-template-compiler/plugins', ['exports'], function (exports) { + + 'use strict'; + + exports.registerPlugin = registerPlugin; + + var plugins = { + ast: [] + }; + + /** + Adds an AST plugin to be used by Ember.HTMLBars.compile. + + @private + @method registerASTPlugin + */ + function registerPlugin(type, Plugin) { + if (!plugins[type]) { + throw new Error('Attempting to register "' + Plugin + '" as "' + type + '" which is not a valid HTMLBars plugin type.'); + } + + plugins[type].push(Plugin); + } + + exports['default'] = plugins; + +}); +enifed('ember-template-compiler/plugins/transform-each-in-to-hash', ['exports'], function (exports) { + + 'use strict'; + + /** + @module ember + @submodule ember-htmlbars + */ + + /** + An HTMLBars AST transformation that replaces all instances of + + ```handlebars + {{#each item in items}} + {{/each}} + ``` + + with + + ```handlebars + {{#each items keyword="item"}} + {{/each}} + ``` + + @class TransformEachInToHash + @private + */ + function TransformEachInToHash(options) { + // set later within HTMLBars to the syntax package + this.syntax = null; + this.options = options || {}; + } + + /** + @private + @method transform + @param {AST} The AST to be transformed. + */ + TransformEachInToHash.prototype.transform = function TransformEachInToHash_transform(ast) { + var pluginContext = this; + var walker = new pluginContext.syntax.Walker(); + var b = pluginContext.syntax.builders; + + walker.visit(ast, function (node) { + if (pluginContext.validate(node)) { + + if (node.program && node.program.blockParams.length) { + throw new Error('You cannot use keyword (`{{each foo in bar}}`) and block params (`{{each bar as |foo|}}`) at the same time.'); + } + + var removedParams = node.sexpr.params.splice(0, 2); + var keyword = removedParams[0].original; + + // TODO: This may not be necessary. + if (!node.sexpr.hash) { + node.sexpr.hash = b.hash(); + } + + node.sexpr.hash.pairs.push(b.pair('keyword', b.string(keyword))); + } + }); + + return ast; + }; + + TransformEachInToHash.prototype.validate = function TransformEachInToHash_validate(node) { + return (node.type === 'BlockStatement' || node.type === 'MustacheStatement') && node.sexpr.path.original === 'each' && node.sexpr.params.length === 3 && node.sexpr.params[1].type === 'PathExpression' && node.sexpr.params[1].original === 'in'; + }; + + exports['default'] = TransformEachInToHash; + +}); +enifed('ember-template-compiler/plugins/transform-with-as-to-hash', ['exports'], function (exports) { + + 'use strict'; + + /** + @module ember + @submodule ember-htmlbars + */ + + /** + An HTMLBars AST transformation that replaces all instances of + + ```handlebars + {{#with foo.bar as bar}} + {{/with}} + ``` + + with + + ```handlebars + {{#with foo.bar as |bar|}} + {{/with}} + ``` + + @private + @class TransformWithAsToHash + */ + function TransformWithAsToHash(options) { + // set later within HTMLBars to the syntax package + this.syntax = null; + this.options = options; + } + + /** + @private + @method transform + @param {AST} The AST to be transformed. + */ + TransformWithAsToHash.prototype.transform = function TransformWithAsToHash_transform(ast) { + var pluginContext = this; + var walker = new pluginContext.syntax.Walker(); + var moduleName = this.options.moduleName; + + walker.visit(ast, function (node) { + if (pluginContext.validate(node)) { + + if (node.program && node.program.blockParams.length) { + throw new Error("You cannot use keyword (`{{with foo as bar}}`) and block params (`{{with foo as |bar|}}`) at the same time."); + } + + Ember.deprecate("Using {{with}} without block syntax is deprecated. " + "Please use standard block form (`{{#with foo as |bar|}}`) " + (moduleName ? " in `" + moduleName + "` " : "") + "instead.", false, { url: "http://emberjs.com/deprecations/v1.x/#toc_code-as-code-sytnax-for-code-with-code" }); + + var removedParams = node.sexpr.params.splice(1, 2); + var keyword = removedParams[1].original; + node.program.blockParams = [keyword]; + } + }); + + return ast; + }; + + TransformWithAsToHash.prototype.validate = function TransformWithAsToHash_validate(node) { + return node.type === "BlockStatement" && node.sexpr.path.original === "with" && node.sexpr.params.length === 3 && node.sexpr.params[1].type === "PathExpression" && node.sexpr.params[1].original === "as"; + }; + + exports['default'] = TransformWithAsToHash; + +}); +enifed('ember-template-compiler/system/compile', ['exports', 'ember-template-compiler/system/compile_options', 'ember-template-compiler/system/template'], function (exports, compileOptions, template) { + + 'use strict'; + + + var compile; /** + Uses HTMLBars `compile` function to process a string into a compiled template. + + This is not present in production builds. + + @private + @method compile + @param {String} templateString This is the string to be compiled by HTMLBars. + @param {Object} options This is an options hash to augment the compiler options. + */ + exports['default'] = function (templateString, options) { + if (!compile && Ember.__loader.registry["htmlbars-compiler/compiler"]) { + compile = requireModule("htmlbars-compiler/compiler").compile; + } + + if (!compile) { + throw new Error("Cannot call `compile` without the template compiler loaded. Please load `ember-template-compiler.js` prior to calling `compile`."); + } + + var templateSpec = compile(templateString, compileOptions['default'](options)); + + return template['default'](templateSpec); + } + +}); +enifed('ember-template-compiler/system/compile_options', ['exports', 'ember-metal/core', 'ember-template-compiler/plugins'], function (exports, Ember, plugins) { + + 'use strict'; + + /** + @module ember + @submodule ember-template-compiler + */ + + exports['default'] = function (_options) { + var disableComponentGeneration = true; + + var options = _options || {}; + // When calling `Ember.Handlebars.compile()` a second argument of `true` + // had a special meaning (long since lost), this just gaurds against + // `options` being true, and causing an error during compilation. + if (options === true) { + options = {}; + } + + options.revision = "Ember@1.12.1"; + options.disableComponentGeneration = disableComponentGeneration; + options.plugins = plugins['default']; + + return options; + } + +}); +enifed('ember-template-compiler/system/precompile', ['exports', 'ember-template-compiler/system/compile_options'], function (exports, compileOptions) { + + 'use strict'; + + /** + @module ember + @submodule ember-template-compiler + */ + + var compileSpec; + + /** + Uses HTMLBars `compile` function to process a string into a compiled template string. + The returned string must be passed through `Ember.HTMLBars.template`. + + This is not present in production builds. + + @private + @method precompile + @param {String} templateString This is the string to be compiled by HTMLBars. + */ + exports['default'] = function (templateString, options) { + if (!compileSpec && Ember.__loader.registry['htmlbars-compiler/compiler']) { + compileSpec = requireModule('htmlbars-compiler/compiler').compileSpec; + } + + if (!compileSpec) { + throw new Error('Cannot call `compileSpec` without the template compiler loaded. Please load `ember-template-compiler.js` prior to calling `compileSpec`.'); + } + + return compileSpec(templateString, compileOptions['default'](options)); + } + +}); +enifed('ember-template-compiler/system/template', ['exports'], function (exports) { + + 'use strict'; + + /** + @module ember + @submodule ember-template-compiler + */ + + /** + Augments the default precompiled output of an HTMLBars template with + additional information needed by Ember. + + @private + @method template + @param {Function} templateSpec This is the compiled HTMLBars template spec. + */ + + exports['default'] = function (templateSpec) { + templateSpec.isTop = true; + templateSpec.isMethod = false; + + return templateSpec; + } + +}); +enifed("htmlbars-compiler", + ["./htmlbars-compiler/compiler","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var compile = __dependency1__.compile; + var compileSpec = __dependency1__.compileSpec; + var template = __dependency1__.template; + + __exports__.compile = compile; + __exports__.compileSpec = compileSpec; + __exports__.template = template; + }); +enifed("htmlbars-compiler/compiler", + ["../htmlbars-syntax/parser","./template-compiler","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + /*jshint evil:true*/ + var preprocess = __dependency1__.preprocess; + var TemplateCompiler = __dependency2__["default"]; + + /* + * Compile a string into a template spec string. The template spec is a string + * representation of a template. Usually, you would use compileSpec for + * pre-compilation of a template on the server. + * + * Example usage: + * + * var templateSpec = compileSpec("Howdy {{name}}"); + * // This next step is basically what plain compile does + * var template = new Function("return " + templateSpec)(); + * + * @method compileSpec + * @param {String} string An HTMLBars template string + * @return {TemplateSpec} A template spec string + */ + function compileSpec(string, options) { + var ast = preprocess(string, options); + var compiler = new TemplateCompiler(options); + var program = compiler.compile(ast); + return program; + } + + __exports__.compileSpec = compileSpec;/* + * @method template + * @param {TemplateSpec} templateSpec A precompiled template + * @return {Template} A template spec string + */ + function template(templateSpec) { + return new Function("return " + templateSpec)(); + } + + __exports__.template = template;/* + * Compile a string into a template rendering function + * + * Example usage: + * + * // Template is the hydration portion of the compiled template + * var template = compile("Howdy {{name}}"); + * + * // Template accepts three arguments: + * // + * // 1. A context object + * // 2. An env object + * // 3. A contextualElement (optional, document.body is the default) + * // + * // The env object *must* have at least these two properties: + * // + * // 1. `hooks` - Basic hooks for rendering a template + * // 2. `dom` - An instance of DOMHelper + * // + * import {hooks} from 'htmlbars-runtime'; + * import {DOMHelper} from 'morph'; + * var context = {name: 'whatever'}, + * env = {hooks: hooks, dom: new DOMHelper()}, + * contextualElement = document.body; + * var domFragment = template(context, env, contextualElement); + * + * @method compile + * @param {String} string An HTMLBars template string + * @param {Object} options A set of options to provide to the compiler + * @return {Template} A function for rendering the template + */ + function compile(string, options) { + return template(compileSpec(string, options)); + } + + __exports__.compile = compile; + }); +enifed("htmlbars-compiler/fragment-javascript-compiler", + ["./utils","../htmlbars-util/quoting","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + var processOpcodes = __dependency1__.processOpcodes; + var string = __dependency2__.string; + + var svgNamespace = "http://www.w3.org/2000/svg", + // http://www.w3.org/html/wg/drafts/html/master/syntax.html#html-integration-point + svgHTMLIntegrationPoints = {'foreignObject':true, 'desc':true, 'title':true}; + + + function FragmentJavaScriptCompiler() { + this.source = []; + this.depth = -1; + } + + __exports__["default"] = FragmentJavaScriptCompiler; + + FragmentJavaScriptCompiler.prototype.compile = function(opcodes, options) { + this.source.length = 0; + this.depth = -1; + this.indent = (options && options.indent) || ""; + this.namespaceFrameStack = [{namespace: null, depth: null}]; + this.domNamespace = null; + + this.source.push('function build(dom) {\n'); + processOpcodes(this, opcodes); + this.source.push(this.indent+'}'); + + return this.source.join(''); + }; + + FragmentJavaScriptCompiler.prototype.createFragment = function() { + var el = 'el'+(++this.depth); + this.source.push(this.indent+' var '+el+' = dom.createDocumentFragment();\n'); + }; + + FragmentJavaScriptCompiler.prototype.createElement = function(tagName) { + var el = 'el'+(++this.depth); + if (tagName === 'svg') { + this.pushNamespaceFrame({namespace: svgNamespace, depth: this.depth}); + } + this.ensureNamespace(); + this.source.push(this.indent+' var '+el+' = dom.createElement('+string(tagName)+');\n'); + if (svgHTMLIntegrationPoints[tagName]) { + this.pushNamespaceFrame({namespace: null, depth: this.depth}); + } + }; + + FragmentJavaScriptCompiler.prototype.createText = function(str) { + var el = 'el'+(++this.depth); + this.source.push(this.indent+' var '+el+' = dom.createTextNode('+string(str)+');\n'); + }; + + FragmentJavaScriptCompiler.prototype.createComment = function(str) { + var el = 'el'+(++this.depth); + this.source.push(this.indent+' var '+el+' = dom.createComment('+string(str)+');\n'); + }; + + FragmentJavaScriptCompiler.prototype.returnNode = function() { + var el = 'el'+this.depth; + this.source.push(this.indent+' return '+el+';\n'); + }; + + FragmentJavaScriptCompiler.prototype.setAttribute = function(name, value, namespace) { + var el = 'el'+this.depth; + if (namespace) { + this.source.push(this.indent+' dom.setAttributeNS('+el+','+string(namespace)+','+string(name)+','+string(value)+');\n'); + } else { + this.source.push(this.indent+' dom.setAttribute('+el+','+string(name)+','+string(value)+');\n'); + } + }; + + FragmentJavaScriptCompiler.prototype.appendChild = function() { + if (this.depth === this.getCurrentNamespaceFrame().depth) { + this.popNamespaceFrame(); + } + var child = 'el'+(this.depth--); + var el = 'el'+this.depth; + this.source.push(this.indent+' dom.appendChild('+el+', '+child+');\n'); + }; + + FragmentJavaScriptCompiler.prototype.getCurrentNamespaceFrame = function() { + return this.namespaceFrameStack[this.namespaceFrameStack.length-1]; + }; + + FragmentJavaScriptCompiler.prototype.pushNamespaceFrame = function(frame) { + this.namespaceFrameStack.push(frame); + }; + + FragmentJavaScriptCompiler.prototype.popNamespaceFrame = function() { + return this.namespaceFrameStack.pop(); + }; + + FragmentJavaScriptCompiler.prototype.ensureNamespace = function() { + var correctNamespace = this.getCurrentNamespaceFrame().namespace; + if (this.domNamespace !== correctNamespace) { + this.source.push(this.indent+' dom.setNamespace('+(correctNamespace ? string(correctNamespace) : 'null')+');\n'); + this.domNamespace = correctNamespace; + } + }; + }); +enifed("htmlbars-compiler/fragment-opcode-compiler", + ["./template-visitor","./utils","../htmlbars-util","../htmlbars-util/array-utils","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { + "use strict"; + var TemplateVisitor = __dependency1__["default"]; + var processOpcodes = __dependency2__.processOpcodes; + var getAttrNamespace = __dependency3__.getAttrNamespace; + var forEach = __dependency4__.forEach; + + function FragmentOpcodeCompiler() { + this.opcodes = []; + } + + __exports__["default"] = FragmentOpcodeCompiler; + + FragmentOpcodeCompiler.prototype.compile = function(ast) { + var templateVisitor = new TemplateVisitor(); + templateVisitor.visit(ast); + + processOpcodes(this, templateVisitor.actions); + + return this.opcodes; + }; + + FragmentOpcodeCompiler.prototype.opcode = function(type, params) { + this.opcodes.push([type, params]); + }; + + FragmentOpcodeCompiler.prototype.text = function(text) { + this.opcode('createText', [text.chars]); + this.opcode('appendChild'); + }; + + FragmentOpcodeCompiler.prototype.comment = function(comment) { + this.opcode('createComment', [comment.value]); + this.opcode('appendChild'); + }; + + FragmentOpcodeCompiler.prototype.openElement = function(element) { + this.opcode('createElement', [element.tag]); + forEach(element.attributes, this.attribute, this); + }; + + FragmentOpcodeCompiler.prototype.closeElement = function() { + this.opcode('appendChild'); + }; + + FragmentOpcodeCompiler.prototype.startProgram = function() { + this.opcodes.length = 0; + this.opcode('createFragment'); + }; + + FragmentOpcodeCompiler.prototype.endProgram = function() { + this.opcode('returnNode'); + }; + + FragmentOpcodeCompiler.prototype.mustache = function() { + this.pushMorphPlaceholderNode(); + }; + + FragmentOpcodeCompiler.prototype.component = function() { + this.pushMorphPlaceholderNode(); + }; + + FragmentOpcodeCompiler.prototype.block = function() { + this.pushMorphPlaceholderNode(); + }; + + FragmentOpcodeCompiler.prototype.pushMorphPlaceholderNode = function() { + this.opcode('createComment', [""]); + this.opcode('appendChild'); + }; + + FragmentOpcodeCompiler.prototype.attribute = function(attr) { + if (attr.value.type === 'TextNode') { + var namespace = getAttrNamespace(attr.name); + this.opcode('setAttribute', [attr.name, attr.value.chars, namespace]); + } + }; + + FragmentOpcodeCompiler.prototype.setNamespace = function(namespace) { + this.opcode('setNamespace', [namespace]); + }; + }); +enifed("htmlbars-compiler/hydration-javascript-compiler", + ["./utils","../htmlbars-util/quoting","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + var processOpcodes = __dependency1__.processOpcodes; + var string = __dependency2__.string; + var array = __dependency2__.array; + + function HydrationJavaScriptCompiler() { + this.stack = []; + this.source = []; + this.mustaches = []; + this.parents = [['fragment']]; + this.parentCount = 0; + this.morphs = []; + this.fragmentProcessing = []; + this.hooks = undefined; + } + + __exports__["default"] = HydrationJavaScriptCompiler; + + var prototype = HydrationJavaScriptCompiler.prototype; + + prototype.compile = function(opcodes, options) { + this.stack.length = 0; + this.mustaches.length = 0; + this.source.length = 0; + this.parents.length = 1; + this.parents[0] = ['fragment']; + this.morphs.length = 0; + this.fragmentProcessing.length = 0; + this.parentCount = 0; + this.indent = (options && options.indent) || ""; + this.hooks = {}; + this.hasOpenBoundary = false; + this.hasCloseBoundary = false; + + processOpcodes(this, opcodes); + + if (this.hasOpenBoundary) { + this.source.unshift(this.indent+" dom.insertBoundary(fragment, 0);\n"); + } + + if (this.hasCloseBoundary) { + this.source.unshift(this.indent+" dom.insertBoundary(fragment, null);\n"); + } + + var i, l; + if (this.morphs.length) { + var morphs = ""; + for (i = 0, l = this.morphs.length; i < l; ++i) { + var morph = this.morphs[i]; + morphs += this.indent+' var '+morph[0]+' = '+morph[1]+';\n'; + } + this.source.unshift(morphs); + } + + if (this.fragmentProcessing.length) { + var processing = ""; + for (i = 0, l = this.fragmentProcessing.length; i < l; ++i) { + processing += this.indent+' '+this.fragmentProcessing[i]+'\n'; + } + this.source.unshift(processing); + } + + return this.source.join(''); + }; + + prototype.prepareArray = function(length) { + var values = []; + + for (var i = 0; i < length; i++) { + values.push(this.stack.pop()); + } + + this.stack.push('[' + values.join(', ') + ']'); + }; + + prototype.prepareObject = function(size) { + var pairs = []; + + for (var i = 0; i < size; i++) { + pairs.push(this.stack.pop() + ': ' + this.stack.pop()); + } + + this.stack.push('{' + pairs.join(', ') + '}'); + }; + + prototype.pushRaw = function(value) { + this.stack.push(value); + }; + + prototype.openBoundary = function() { + this.hasOpenBoundary = true; + }; + + prototype.closeBoundary = function() { + this.hasCloseBoundary = true; + }; + + prototype.pushLiteral = function(value) { + if (typeof value === 'string') { + this.stack.push(string(value)); + } else { + this.stack.push(value.toString()); + } + }; + + prototype.pushHook = function(name, args) { + this.hooks[name] = true; + this.stack.push(name + '(' + args.join(', ') + ')'); + }; + + prototype.pushGetHook = function(path) { + this.pushHook('get', [ + 'env', + 'context', + string(path) + ]); + }; + + prototype.pushSexprHook = function() { + this.pushHook('subexpr', [ + 'env', + 'context', + this.stack.pop(), // path + this.stack.pop(), // params + this.stack.pop() // hash + ]); + }; + + prototype.pushConcatHook = function() { + this.pushHook('concat', [ + 'env', + this.stack.pop() // parts + ]); + }; + + prototype.printHook = function(name, args) { + this.hooks[name] = true; + this.source.push(this.indent + ' ' + name + '(' + args.join(', ') + ');\n'); + }; + + prototype.printSetHook = function(name, index) { + this.printHook('set', [ + 'env', + 'context', + string(name), + 'blockArguments[' + index + ']' + ]); + }; + + prototype.printBlockHook = function(morphNum, templateId, inverseId) { + this.printHook('block', [ + 'env', + 'morph' + morphNum, + 'context', + this.stack.pop(), // path + this.stack.pop(), // params + this.stack.pop(), // hash + templateId === null ? 'null' : 'child' + templateId, + inverseId === null ? 'null' : 'child' + inverseId + ]); + }; + + prototype.printInlineHook = function(morphNum) { + this.printHook('inline', [ + 'env', + 'morph' + morphNum, + 'context', + this.stack.pop(), // path + this.stack.pop(), // params + this.stack.pop() // hash + ]); + }; + + prototype.printContentHook = function(morphNum) { + this.printHook('content', [ + 'env', + 'morph' + morphNum, + 'context', + this.stack.pop() // path + ]); + }; + + prototype.printComponentHook = function(morphNum, templateId) { + this.printHook('component', [ + 'env', + 'morph' + morphNum, + 'context', + this.stack.pop(), // path + this.stack.pop(), // attrs + templateId === null ? 'null' : 'child' + templateId + ]); + }; + + prototype.printAttributeHook = function(attrMorphNum, elementNum) { + this.printHook('attribute', [ + 'env', + 'attrMorph' + attrMorphNum, + 'element' + elementNum, + this.stack.pop(), // name + this.stack.pop() // value + ]); + }; + + prototype.printElementHook = function(elementNum) { + this.printHook('element', [ + 'env', + 'element' + elementNum, + 'context', + this.stack.pop(), // path + this.stack.pop(), // params + this.stack.pop() // hash + ]); + }; + + prototype.createMorph = function(morphNum, parentPath, startIndex, endIndex, escaped) { + var isRoot = parentPath.length === 0; + var parent = this.getParent(); + + var morphMethod = escaped ? 'createMorphAt' : 'createUnsafeMorphAt'; + var morph = "dom."+morphMethod+"("+parent+ + ","+(startIndex === null ? "-1" : startIndex)+ + ","+(endIndex === null ? "-1" : endIndex)+ + (isRoot ? ",contextualElement)" : ")"); + + this.morphs.push(['morph' + morphNum, morph]); + }; + + prototype.createAttrMorph = function(attrMorphNum, elementNum, name, escaped, namespace) { + var morphMethod = escaped ? 'createAttrMorph' : 'createUnsafeAttrMorph'; + var morph = "dom."+morphMethod+"(element"+elementNum+", '"+name+(namespace ? "', '"+namespace : '')+"')"; + this.morphs.push(['attrMorph' + attrMorphNum, morph]); + }; + + prototype.repairClonedNode = function(blankChildTextNodes, isElementChecked) { + var parent = this.getParent(), + processing = 'if (this.cachedFragment) { dom.repairClonedNode('+parent+','+ + array(blankChildTextNodes)+ + ( isElementChecked ? ',true' : '' )+ + '); }'; + this.fragmentProcessing.push( + processing + ); + }; + + prototype.shareElement = function(elementNum){ + var elementNodesName = "element" + elementNum; + this.fragmentProcessing.push('var '+elementNodesName+' = '+this.getParent()+';'); + this.parents[this.parents.length-1] = [elementNodesName]; + }; + + prototype.consumeParent = function(i) { + var newParent = this.lastParent().slice(); + newParent.push(i); + + this.parents.push(newParent); + }; + + prototype.popParent = function() { + this.parents.pop(); + }; + + prototype.getParent = function() { + var last = this.lastParent().slice(); + var frag = last.shift(); + + if (!last.length) { + return frag; + } + + return 'dom.childAt(' + frag + ', [' + last.join(', ') + '])'; + }; + + prototype.lastParent = function() { + return this.parents[this.parents.length-1]; + }; + }); +enifed("htmlbars-compiler/hydration-opcode-compiler", + ["./template-visitor","./utils","../htmlbars-util","../htmlbars-util/array-utils","../htmlbars-syntax/utils","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { + "use strict"; + var TemplateVisitor = __dependency1__["default"]; + var processOpcodes = __dependency2__.processOpcodes; + var getAttrNamespace = __dependency3__.getAttrNamespace; + var forEach = __dependency4__.forEach; + var isHelper = __dependency5__.isHelper; + + function unwrapMustache(mustache) { + if (isHelper(mustache.sexpr)) { + return mustache.sexpr; + } else { + return mustache.sexpr.path; + } + } + + function detectIsElementChecked(element){ + for (var i=0, len=element.attributes.length;i 0) { + this.opcode('repairClonedNode', blankChildTextNodes); + } + }; + + HydrationOpcodeCompiler.prototype.endProgram = function() { + distributeMorphs(this.morphs, this.opcodes); + }; + + HydrationOpcodeCompiler.prototype.text = function() { + ++this.currentDOMChildIndex; + }; + + HydrationOpcodeCompiler.prototype.comment = function() { + ++this.currentDOMChildIndex; + }; + + HydrationOpcodeCompiler.prototype.openElement = function(element, pos, len, mustacheCount, blankChildTextNodes) { + distributeMorphs(this.morphs, this.opcodes); + ++this.currentDOMChildIndex; + + this.element = this.currentDOMChildIndex; + + this.opcode('consumeParent', this.currentDOMChildIndex); + + // If our parent reference will be used more than once, cache its reference. + if (mustacheCount > 1) { + this.opcode('shareElement', ++this.elementNum); + this.element = null; // Set element to null so we don't cache it twice + } + + var isElementChecked = detectIsElementChecked(element); + if (blankChildTextNodes.length > 0 || isElementChecked) { + this.opcode( 'repairClonedNode', + blankChildTextNodes, + isElementChecked ); + } + + this.paths.push(this.currentDOMChildIndex); + this.currentDOMChildIndex = -1; + + forEach(element.attributes, this.attribute, this); + forEach(element.modifiers, this.elementModifier, this); + }; + + HydrationOpcodeCompiler.prototype.closeElement = function() { + distributeMorphs(this.morphs, this.opcodes); + this.opcode('popParent'); + this.currentDOMChildIndex = this.paths.pop(); + }; + + HydrationOpcodeCompiler.prototype.mustache = function(mustache, childIndex, childCount) { + this.pushMorphPlaceholderNode(childIndex, childCount); + + var sexpr = mustache.sexpr; + + var morphNum = this.morphNum++; + var start = this.currentDOMChildIndex; + var end = this.currentDOMChildIndex; + this.morphs.push([morphNum, this.paths.slice(), start, end, mustache.escaped]); + + if (isHelper(sexpr)) { + prepareSexpr(this, sexpr); + this.opcode('printInlineHook', morphNum); + } else { + preparePath(this, sexpr.path); + this.opcode('printContentHook', morphNum); + } + }; + + HydrationOpcodeCompiler.prototype.block = function(block, childIndex, childCount) { + this.pushMorphPlaceholderNode(childIndex, childCount); + + var sexpr = block.sexpr; + + var morphNum = this.morphNum++; + var start = this.currentDOMChildIndex; + var end = this.currentDOMChildIndex; + this.morphs.push([morphNum, this.paths.slice(), start, end, true]); + + var templateId = this.templateId++; + var inverseId = block.inverse === null ? null : this.templateId++; + + prepareSexpr(this, sexpr); + this.opcode('printBlockHook', morphNum, templateId, inverseId); + }; + + HydrationOpcodeCompiler.prototype.component = function(component, childIndex, childCount) { + this.pushMorphPlaceholderNode(childIndex, childCount); + + var program = component.program || {}; + var blockParams = program.blockParams || []; + + var morphNum = this.morphNum++; + var start = this.currentDOMChildIndex; + var end = this.currentDOMChildIndex; + this.morphs.push([morphNum, this.paths.slice(), start, end, true]); + + var attrs = component.attributes; + for (var i = attrs.length - 1; i >= 0; i--) { + var name = attrs[i].name; + var value = attrs[i].value; + + // TODO: Introduce context specific AST nodes to avoid switching here. + if (value.type === 'TextNode') { + this.opcode('pushLiteral', value.chars); + } else if (value.type === 'MustacheStatement') { + this.accept(unwrapMustache(value)); + } else if (value.type === 'ConcatStatement') { + prepareParams(this, value.parts); + this.opcode('pushConcatHook'); + } + + this.opcode('pushLiteral', name); + } + + this.opcode('prepareObject', attrs.length); + this.opcode('pushLiteral', component.tag); + this.opcode('printComponentHook', morphNum, this.templateId++, blockParams.length); + }; + + HydrationOpcodeCompiler.prototype.attribute = function(attr) { + var value = attr.value; + var escaped = true; + var namespace = getAttrNamespace(attr.name); + + // TODO: Introduce context specific AST nodes to avoid switching here. + if (value.type === 'TextNode') { + return; + } else if (value.type === 'MustacheStatement') { + escaped = value.escaped; + this.accept(unwrapMustache(value)); + } else if (value.type === 'ConcatStatement') { + prepareParams(this, value.parts); + this.opcode('pushConcatHook'); + } + + this.opcode('pushLiteral', attr.name); + + if (this.element !== null) { + this.opcode('shareElement', ++this.elementNum); + this.element = null; + } + + var attrMorphNum = this.attrMorphNum++; + this.opcode('createAttrMorph', attrMorphNum, this.elementNum, attr.name, escaped, namespace); + this.opcode('printAttributeHook', attrMorphNum, this.elementNum); + }; + + HydrationOpcodeCompiler.prototype.elementModifier = function(modifier) { + prepareSexpr(this, modifier.sexpr); + + // If we have a helper in a node, and this element has not been cached, cache it + if (this.element !== null) { + this.opcode('shareElement', ++this.elementNum); + this.element = null; // Reset element so we don't cache it more than once + } + + this.opcode('printElementHook', this.elementNum); + }; + + HydrationOpcodeCompiler.prototype.pushMorphPlaceholderNode = function(childIndex, childCount) { + if (this.paths.length === 0) { + if (childIndex === 0) { + this.opcode('openBoundary'); + } + if (childIndex === childCount - 1) { + this.opcode('closeBoundary'); + } + } + this.comment(); + }; + + HydrationOpcodeCompiler.prototype.SubExpression = function(sexpr) { + prepareSexpr(this, sexpr); + this.opcode('pushSexprHook'); + }; + + HydrationOpcodeCompiler.prototype.PathExpression = function(path) { + this.opcode('pushGetHook', path.original); + }; + + HydrationOpcodeCompiler.prototype.StringLiteral = function(node) { + this.opcode('pushLiteral', node.value); + }; + + HydrationOpcodeCompiler.prototype.BooleanLiteral = function(node) { + this.opcode('pushLiteral', node.value); + }; + + HydrationOpcodeCompiler.prototype.NumberLiteral = function(node) { + this.opcode('pushLiteral', node.value); + }; + + function preparePath(compiler, path) { + compiler.opcode('pushLiteral', path.original); + } + + function prepareParams(compiler, params) { + for (var i = params.length - 1; i >= 0; i--) { + var param = params[i]; + compiler[param.type](param); + } + + compiler.opcode('prepareArray', params.length); + } + + function prepareHash(compiler, hash) { + var pairs = hash.pairs; + + for (var i = pairs.length - 1; i >= 0; i--) { + var key = pairs[i].key; + var value = pairs[i].value; + + compiler[value.type](value); + compiler.opcode('pushLiteral', key); + } + + compiler.opcode('prepareObject', pairs.length); + } + + function prepareSexpr(compiler, sexpr) { + prepareHash(compiler, sexpr.hash); + prepareParams(compiler, sexpr.params); + preparePath(compiler, sexpr.path); + } + + function distributeMorphs(morphs, opcodes) { + if (morphs.length === 0) { + return; + } + + // Splice morphs after the most recent shareParent/consumeParent. + var o; + for (o = opcodes.length - 1; o >= 0; --o) { + var opcode = opcodes[o][0]; + if (opcode === 'shareElement' || opcode === 'consumeParent' || opcode === 'popParent') { + break; + } + } + + var spliceArgs = [o + 1, 0]; + for (var i = 0; i < morphs.length; ++i) { + spliceArgs.push(['createMorph', morphs[i].slice()]); + } + opcodes.splice.apply(opcodes, spliceArgs); + morphs.length = 0; + } + }); +enifed("htmlbars-compiler/template-compiler", + ["./fragment-opcode-compiler","./fragment-javascript-compiler","./hydration-opcode-compiler","./hydration-javascript-compiler","./template-visitor","./utils","../htmlbars-util/quoting","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __exports__) { + "use strict"; + var FragmentOpcodeCompiler = __dependency1__["default"]; + var FragmentJavaScriptCompiler = __dependency2__["default"]; + var HydrationOpcodeCompiler = __dependency3__["default"]; + var HydrationJavaScriptCompiler = __dependency4__["default"]; + var TemplateVisitor = __dependency5__["default"]; + var processOpcodes = __dependency6__.processOpcodes; + var repeat = __dependency7__.repeat; + + function TemplateCompiler(options) { + this.options = options || {}; + this.revision = this.options.revision || "HTMLBars@v0.11.3"; + this.fragmentOpcodeCompiler = new FragmentOpcodeCompiler(); + this.fragmentCompiler = new FragmentJavaScriptCompiler(); + this.hydrationOpcodeCompiler = new HydrationOpcodeCompiler(); + this.hydrationCompiler = new HydrationJavaScriptCompiler(); + this.templates = []; + this.childTemplates = []; + } + + __exports__["default"] = TemplateCompiler; + + TemplateCompiler.prototype.compile = function(ast) { + var templateVisitor = new TemplateVisitor(); + templateVisitor.visit(ast); + + processOpcodes(this, templateVisitor.actions); + + return this.templates.pop(); + }; + + TemplateCompiler.prototype.startProgram = function(program, childTemplateCount, blankChildTextNodes) { + this.fragmentOpcodeCompiler.startProgram(program, childTemplateCount, blankChildTextNodes); + this.hydrationOpcodeCompiler.startProgram(program, childTemplateCount, blankChildTextNodes); + + this.childTemplates.length = 0; + while(childTemplateCount--) { + this.childTemplates.push(this.templates.pop()); + } + }; + + TemplateCompiler.prototype.getChildTemplateVars = function(indent) { + var vars = ''; + if (this.childTemplates) { + for (var i = 0; i < this.childTemplates.length; i++) { + vars += indent + 'var child' + i + ' = ' + this.childTemplates[i] + ';\n'; + } + } + return vars; + }; + + TemplateCompiler.prototype.getHydrationHooks = function(indent, hooks) { + var hookVars = []; + for (var hook in hooks) { + hookVars.push(hook + ' = hooks.' + hook); + } + + if (hookVars.length > 0) { + return indent + 'var hooks = env.hooks, ' + hookVars.join(', ') + ';\n'; + } else { + return ''; + } + }; + + TemplateCompiler.prototype.endProgram = function(program, programDepth) { + this.fragmentOpcodeCompiler.endProgram(program); + this.hydrationOpcodeCompiler.endProgram(program); + + var indent = repeat(" ", programDepth); + var options = { + indent: indent + " " + }; + + // function build(dom) { return fragment; } + var fragmentProgram = this.fragmentCompiler.compile( + this.fragmentOpcodeCompiler.opcodes, + options + ); + + // function hydrate(fragment) { return mustaches; } + var hydrationProgram = this.hydrationCompiler.compile( + this.hydrationOpcodeCompiler.opcodes, + options + ); + + var blockParams = program.blockParams || []; + + var templateSignature = 'context, env, contextualElement'; + if (blockParams.length > 0) { + templateSignature += ', blockArguments'; + } + + var template = + '(function() {\n' + + this.getChildTemplateVars(indent + ' ') + + indent+' return {\n' + + indent+' isHTMLBars: true,\n' + + indent+' revision: "' + this.revision + '",\n' + + indent+' blockParams: ' + blockParams.length + ',\n' + + indent+' cachedFragment: null,\n' + + indent+' hasRendered: false,\n' + + indent+' build: ' + fragmentProgram + ',\n' + + indent+' render: function render(' + templateSignature + ') {\n' + + indent+' var dom = env.dom;\n' + + this.getHydrationHooks(indent + ' ', this.hydrationCompiler.hooks) + + indent+' dom.detectNamespace(contextualElement);\n' + + indent+' var fragment;\n' + + indent+' if (env.useFragmentCache && dom.canClone) {\n' + + indent+' if (this.cachedFragment === null) {\n' + + indent+' fragment = this.build(dom);\n' + + indent+' if (this.hasRendered) {\n' + + indent+' this.cachedFragment = fragment;\n' + + indent+' } else {\n' + + indent+' this.hasRendered = true;\n' + + indent+' }\n' + + indent+' }\n' + + indent+' if (this.cachedFragment) {\n' + + indent+' fragment = dom.cloneNode(this.cachedFragment, true);\n' + + indent+' }\n' + + indent+' } else {\n' + + indent+' fragment = this.build(dom);\n' + + indent+' }\n' + + hydrationProgram + + indent+' return fragment;\n' + + indent+' }\n' + + indent+' };\n' + + indent+'}())'; + + this.templates.push(template); + }; + + TemplateCompiler.prototype.openElement = function(element, i, l, r, c, b) { + this.fragmentOpcodeCompiler.openElement(element, i, l, r, c, b); + this.hydrationOpcodeCompiler.openElement(element, i, l, r, c, b); + }; + + TemplateCompiler.prototype.closeElement = function(element, i, l, r) { + this.fragmentOpcodeCompiler.closeElement(element, i, l, r); + this.hydrationOpcodeCompiler.closeElement(element, i, l, r); + }; + + TemplateCompiler.prototype.component = function(component, i, l, s) { + this.fragmentOpcodeCompiler.component(component, i, l, s); + this.hydrationOpcodeCompiler.component(component, i, l, s); + }; + + TemplateCompiler.prototype.block = function(block, i, l, s) { + this.fragmentOpcodeCompiler.block(block, i, l, s); + this.hydrationOpcodeCompiler.block(block, i, l, s); + }; + + TemplateCompiler.prototype.text = function(string, i, l, r) { + this.fragmentOpcodeCompiler.text(string, i, l, r); + this.hydrationOpcodeCompiler.text(string, i, l, r); + }; + + TemplateCompiler.prototype.comment = function(string, i, l, r) { + this.fragmentOpcodeCompiler.comment(string, i, l, r); + this.hydrationOpcodeCompiler.comment(string, i, l, r); + }; + + TemplateCompiler.prototype.mustache = function (mustache, i, l, s) { + this.fragmentOpcodeCompiler.mustache(mustache, i, l, s); + this.hydrationOpcodeCompiler.mustache(mustache, i, l, s); + }; + + TemplateCompiler.prototype.setNamespace = function(namespace) { + this.fragmentOpcodeCompiler.setNamespace(namespace); + }; + }); +enifed("htmlbars-compiler/template-visitor", + ["exports"], + function(__exports__) { + "use strict"; + var push = Array.prototype.push; + + function Frame() { + this.parentNode = null; + this.children = null; + this.childIndex = null; + this.childCount = null; + this.childTemplateCount = 0; + this.mustacheCount = 0; + this.actions = []; + } + + /** + * Takes in an AST and outputs a list of actions to be consumed + * by a compiler. For example, the template + * + * foo{{bar}}
        baz
        + * + * produces the actions + * + * [['startProgram', [programNode, 0]], + * ['text', [textNode, 0, 3]], + * ['mustache', [mustacheNode, 1, 3]], + * ['openElement', [elementNode, 2, 3, 0]], + * ['text', [textNode, 0, 1]], + * ['closeElement', [elementNode, 2, 3], + * ['endProgram', [programNode]]] + * + * This visitor walks the AST depth first and backwards. As + * a result the bottom-most child template will appear at the + * top of the actions list whereas the root template will appear + * at the bottom of the list. For example, + * + *
        {{#if}}foo{{else}}bar{{/if}}
        + * + * produces the actions + * + * [['startProgram', [programNode, 0]], + * ['text', [textNode, 0, 2, 0]], + * ['openElement', [elementNode, 1, 2, 0]], + * ['closeElement', [elementNode, 1, 2]], + * ['endProgram', [programNode]], + * ['startProgram', [programNode, 0]], + * ['text', [textNode, 0, 1]], + * ['endProgram', [programNode]], + * ['startProgram', [programNode, 2]], + * ['openElement', [elementNode, 0, 1, 1]], + * ['block', [blockNode, 0, 1]], + * ['closeElement', [elementNode, 0, 1]], + * ['endProgram', [programNode]]] + * + * The state of the traversal is maintained by a stack of frames. + * Whenever a node with children is entered (either a ProgramNode + * or an ElementNode) a frame is pushed onto the stack. The frame + * contains information about the state of the traversal of that + * node. For example, + * + * - index of the current child node being visited + * - the number of mustaches contained within its child nodes + * - the list of actions generated by its child nodes + */ + + function TemplateVisitor() { + this.frameStack = []; + this.actions = []; + this.programDepth = -1; + } + + // Traversal methods + + TemplateVisitor.prototype.visit = function(node) { + this[node.type](node); + }; + + TemplateVisitor.prototype.Program = function(program) { + this.programDepth++; + + var parentFrame = this.getCurrentFrame(); + var programFrame = this.pushFrame(); + + programFrame.parentNode = program; + programFrame.children = program.body; + programFrame.childCount = program.body.length; + programFrame.blankChildTextNodes = []; + programFrame.actions.push(['endProgram', [program, this.programDepth]]); + + for (var i = program.body.length - 1; i >= 0; i--) { + programFrame.childIndex = i; + this.visit(program.body[i]); + } + + programFrame.actions.push(['startProgram', [ + program, programFrame.childTemplateCount, + programFrame.blankChildTextNodes.reverse() + ]]); + this.popFrame(); + + this.programDepth--; + + // Push the completed template into the global actions list + if (parentFrame) { parentFrame.childTemplateCount++; } + push.apply(this.actions, programFrame.actions.reverse()); + }; + + TemplateVisitor.prototype.ElementNode = function(element) { + var parentFrame = this.getCurrentFrame(); + var elementFrame = this.pushFrame(); + + elementFrame.parentNode = element; + elementFrame.children = element.children; + elementFrame.childCount = element.children.length; + elementFrame.mustacheCount += element.modifiers.length; + elementFrame.blankChildTextNodes = []; + + var actionArgs = [ + element, + parentFrame.childIndex, + parentFrame.childCount + ]; + + elementFrame.actions.push(['closeElement', actionArgs]); + + for (var i = element.attributes.length - 1; i >= 0; i--) { + this.visit(element.attributes[i]); + } + + for (i = element.children.length - 1; i >= 0; i--) { + elementFrame.childIndex = i; + this.visit(element.children[i]); + } + + elementFrame.actions.push(['openElement', actionArgs.concat([ + elementFrame.mustacheCount, elementFrame.blankChildTextNodes.reverse() ])]); + this.popFrame(); + + // Propagate the element's frame state to the parent frame + if (elementFrame.mustacheCount > 0) { parentFrame.mustacheCount++; } + parentFrame.childTemplateCount += elementFrame.childTemplateCount; + push.apply(parentFrame.actions, elementFrame.actions); + }; + + TemplateVisitor.prototype.AttrNode = function(attr) { + if (attr.value.type !== 'TextNode') { + this.getCurrentFrame().mustacheCount++; + } + }; + + TemplateVisitor.prototype.TextNode = function(text) { + var frame = this.getCurrentFrame(); + if (text.chars === '') { + frame.blankChildTextNodes.push(domIndexOf(frame.children, text)); + } + frame.actions.push(['text', [text, frame.childIndex, frame.childCount]]); + }; + + TemplateVisitor.prototype.BlockStatement = function(node) { + var frame = this.getCurrentFrame(); + + frame.mustacheCount++; + frame.actions.push(['block', [node, frame.childIndex, frame.childCount]]); + + if (node.inverse) { this.visit(node.inverse); } + if (node.program) { this.visit(node.program); } + }; + + TemplateVisitor.prototype.ComponentNode = function(node) { + var frame = this.getCurrentFrame(); + + frame.mustacheCount++; + frame.actions.push(['component', [node, frame.childIndex, frame.childCount]]); + + if (node.program) { this.visit(node.program); } + }; + + + TemplateVisitor.prototype.PartialStatement = function(node) { + var frame = this.getCurrentFrame(); + frame.mustacheCount++; + frame.actions.push(['mustache', [node, frame.childIndex, frame.childCount]]); + }; + + TemplateVisitor.prototype.CommentStatement = function(text) { + var frame = this.getCurrentFrame(); + frame.actions.push(['comment', [text, frame.childIndex, frame.childCount]]); + }; + + TemplateVisitor.prototype.MustacheStatement = function(mustache) { + var frame = this.getCurrentFrame(); + frame.mustacheCount++; + frame.actions.push(['mustache', [mustache, frame.childIndex, frame.childCount]]); + }; + + // Frame helpers + + TemplateVisitor.prototype.getCurrentFrame = function() { + return this.frameStack[this.frameStack.length - 1]; + }; + + TemplateVisitor.prototype.pushFrame = function() { + var frame = new Frame(); + this.frameStack.push(frame); + return frame; + }; + + TemplateVisitor.prototype.popFrame = function() { + return this.frameStack.pop(); + }; + + __exports__["default"] = TemplateVisitor; + + + // Returns the index of `domNode` in the `nodes` array, skipping + // over any nodes which do not represent DOM nodes. + function domIndexOf(nodes, domNode) { + var index = -1; + + for (var i = 0; i < nodes.length; i++) { + var node = nodes[i]; + + if (node.type !== 'TextNode' && node.type !== 'ElementNode') { + continue; + } else { + index++; + } + + if (node === domNode) { + return index; + } + } + + return -1; + } + }); +enifed("htmlbars-compiler/utils", + ["exports"], + function(__exports__) { + "use strict"; + function processOpcodes(compiler, opcodes) { + for (var i=0, l=opcodes.length; i 0) { + throw new Exception('Invalid path: ' + original, {loc: locInfo}); + } else if (part === '..') { + depth++; + depthString += '../'; + } + } else { + dig.push(part); + } + } + + return new this.PathExpression(data, depth, dig, original, locInfo); + } + + __exports__.preparePath = preparePath;function prepareMustache(sexpr, open, strip, locInfo) { + /*jshint -W040 */ + // Must use charAt to support IE pre-10 + var escapeFlag = open.charAt(3) || open.charAt(2), + escaped = escapeFlag !== '{' && escapeFlag !== '&'; + + return new this.MustacheStatement(sexpr, escaped, strip, this.locInfo(locInfo)); + } + + __exports__.prepareMustache = prepareMustache;function prepareRawBlock(openRawBlock, content, close, locInfo) { + /*jshint -W040 */ + if (openRawBlock.sexpr.path.original !== close) { + var errorNode = {loc: openRawBlock.sexpr.loc}; + + throw new Exception(openRawBlock.sexpr.path.original + " doesn't match " + close, errorNode); + } + + locInfo = this.locInfo(locInfo); + var program = new this.Program([content], null, {}, locInfo); + + return new this.BlockStatement( + openRawBlock.sexpr, program, undefined, + {}, {}, {}, + locInfo); + } + + __exports__.prepareRawBlock = prepareRawBlock;function prepareBlock(openBlock, program, inverseAndProgram, close, inverted, locInfo) { + /*jshint -W040 */ + // When we are chaining inverse calls, we will not have a close path + if (close && close.path && openBlock.sexpr.path.original !== close.path.original) { + var errorNode = {loc: openBlock.sexpr.loc}; + + throw new Exception(openBlock.sexpr.path.original + ' doesn\'t match ' + close.path.original, errorNode); + } + + program.blockParams = openBlock.blockParams; + + var inverse, + inverseStrip; + + if (inverseAndProgram) { + if (inverseAndProgram.chain) { + inverseAndProgram.program.body[0].closeStrip = close.strip || close.openStrip; + } + + inverseStrip = inverseAndProgram.strip; + inverse = inverseAndProgram.program; + } + + if (inverted) { + inverted = inverse; + inverse = program; + program = inverted; + } + + return new this.BlockStatement( + openBlock.sexpr, program, inverse, + openBlock.strip, inverseStrip, close && (close.strip || close.openStrip), + this.locInfo(locInfo)); + } + + __exports__.prepareBlock = prepareBlock; + }); +enifed("htmlbars-syntax/handlebars/compiler/parser", + ["exports"], + function(__exports__) { + "use strict"; + /* jshint ignore:start */ + /* istanbul ignore next */ + /* Jison generated parser */ + var handlebars = (function(){ + var parser = {trace: function trace() { }, + yy: {}, + symbols_: {"error":2,"root":3,"program":4,"EOF":5,"program_repetition0":6,"statement":7,"mustache":8,"block":9,"rawBlock":10,"partial":11,"content":12,"COMMENT":13,"CONTENT":14,"openRawBlock":15,"END_RAW_BLOCK":16,"OPEN_RAW_BLOCK":17,"sexpr":18,"CLOSE_RAW_BLOCK":19,"openBlock":20,"block_option0":21,"closeBlock":22,"openInverse":23,"block_option1":24,"OPEN_BLOCK":25,"openBlock_option0":26,"CLOSE":27,"OPEN_INVERSE":28,"openInverse_option0":29,"openInverseChain":30,"OPEN_INVERSE_CHAIN":31,"openInverseChain_option0":32,"inverseAndProgram":33,"INVERSE":34,"inverseChain":35,"inverseChain_option0":36,"OPEN_ENDBLOCK":37,"path":38,"OPEN":39,"OPEN_UNESCAPED":40,"CLOSE_UNESCAPED":41,"OPEN_PARTIAL":42,"helperName":43,"sexpr_repetition0":44,"sexpr_option0":45,"dataName":46,"param":47,"STRING":48,"NUMBER":49,"BOOLEAN":50,"OPEN_SEXPR":51,"CLOSE_SEXPR":52,"hash":53,"hash_repetition_plus0":54,"hashSegment":55,"ID":56,"EQUALS":57,"blockParams":58,"OPEN_BLOCK_PARAMS":59,"blockParams_repetition_plus0":60,"CLOSE_BLOCK_PARAMS":61,"DATA":62,"pathSegments":63,"SEP":64,"$accept":0,"$end":1}, + terminals_: {2:"error",5:"EOF",13:"COMMENT",14:"CONTENT",16:"END_RAW_BLOCK",17:"OPEN_RAW_BLOCK",19:"CLOSE_RAW_BLOCK",25:"OPEN_BLOCK",27:"CLOSE",28:"OPEN_INVERSE",31:"OPEN_INVERSE_CHAIN",34:"INVERSE",37:"OPEN_ENDBLOCK",39:"OPEN",40:"OPEN_UNESCAPED",41:"CLOSE_UNESCAPED",42:"OPEN_PARTIAL",48:"STRING",49:"NUMBER",50:"BOOLEAN",51:"OPEN_SEXPR",52:"CLOSE_SEXPR",56:"ID",57:"EQUALS",59:"OPEN_BLOCK_PARAMS",61:"CLOSE_BLOCK_PARAMS",62:"DATA",64:"SEP"}, + productions_: [0,[3,2],[4,1],[7,1],[7,1],[7,1],[7,1],[7,1],[7,1],[12,1],[10,3],[15,3],[9,4],[9,4],[20,4],[23,4],[30,4],[33,2],[35,3],[35,1],[22,3],[8,3],[8,3],[11,3],[18,3],[18,1],[47,1],[47,1],[47,1],[47,1],[47,1],[47,3],[53,1],[55,3],[58,3],[43,1],[43,1],[43,1],[46,2],[38,1],[63,3],[63,1],[6,0],[6,2],[21,0],[21,1],[24,0],[24,1],[26,0],[26,1],[29,0],[29,1],[32,0],[32,1],[36,0],[36,1],[44,0],[44,2],[45,0],[45,1],[54,1],[54,2],[60,1],[60,2]], + performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) { + + var $0 = $$.length - 1; + switch (yystate) { + case 1: return $$[$0-1]; + break; + case 2:this.$ = new yy.Program($$[$0], null, {}, yy.locInfo(this._$)); + break; + case 3:this.$ = $$[$0]; + break; + case 4:this.$ = $$[$0]; + break; + case 5:this.$ = $$[$0]; + break; + case 6:this.$ = $$[$0]; + break; + case 7:this.$ = $$[$0]; + break; + case 8:this.$ = new yy.CommentStatement(yy.stripComment($$[$0]), yy.stripFlags($$[$0], $$[$0]), yy.locInfo(this._$)); + break; + case 9:this.$ = new yy.ContentStatement($$[$0], yy.locInfo(this._$)); + break; + case 10:this.$ = yy.prepareRawBlock($$[$0-2], $$[$0-1], $$[$0], this._$); + break; + case 11:this.$ = { sexpr: $$[$0-1] }; + break; + case 12:this.$ = yy.prepareBlock($$[$0-3], $$[$0-2], $$[$0-1], $$[$0], false, this._$); + break; + case 13:this.$ = yy.prepareBlock($$[$0-3], $$[$0-2], $$[$0-1], $$[$0], true, this._$); + break; + case 14:this.$ = { sexpr: $$[$0-2], blockParams: $$[$0-1], strip: yy.stripFlags($$[$0-3], $$[$0]) }; + break; + case 15:this.$ = { sexpr: $$[$0-2], blockParams: $$[$0-1], strip: yy.stripFlags($$[$0-3], $$[$0]) }; + break; + case 16:this.$ = { sexpr: $$[$0-2], blockParams: $$[$0-1], strip: yy.stripFlags($$[$0-3], $$[$0]) }; + break; + case 17:this.$ = { strip: yy.stripFlags($$[$0-1], $$[$0-1]), program: $$[$0] }; + break; + case 18: + var inverse = yy.prepareBlock($$[$0-2], $$[$0-1], $$[$0], $$[$0], false, this._$), + program = new yy.Program([inverse], null, {}, yy.locInfo(this._$)); + program.chained = true; + + this.$ = { strip: $$[$0-2].strip, program: program, chain: true }; + + break; + case 19:this.$ = $$[$0]; + break; + case 20:this.$ = {path: $$[$0-1], strip: yy.stripFlags($$[$0-2], $$[$0])}; + break; + case 21:this.$ = yy.prepareMustache($$[$0-1], $$[$0-2], yy.stripFlags($$[$0-2], $$[$0]), this._$); + break; + case 22:this.$ = yy.prepareMustache($$[$0-1], $$[$0-2], yy.stripFlags($$[$0-2], $$[$0]), this._$); + break; + case 23:this.$ = new yy.PartialStatement($$[$0-1], yy.stripFlags($$[$0-2], $$[$0]), yy.locInfo(this._$)); + break; + case 24:this.$ = new yy.SubExpression($$[$0-2], $$[$0-1], $$[$0], yy.locInfo(this._$)); + break; + case 25:this.$ = new yy.SubExpression($$[$0], null, null, yy.locInfo(this._$)); + break; + case 26:this.$ = $$[$0]; + break; + case 27:this.$ = new yy.StringLiteral($$[$0], yy.locInfo(this._$)); + break; + case 28:this.$ = new yy.NumberLiteral($$[$0], yy.locInfo(this._$)); + break; + case 29:this.$ = new yy.BooleanLiteral($$[$0], yy.locInfo(this._$)); + break; + case 30:this.$ = $$[$0]; + break; + case 31:this.$ = $$[$0-1]; + break; + case 32:this.$ = new yy.Hash($$[$0], yy.locInfo(this._$)); + break; + case 33:this.$ = new yy.HashPair($$[$0-2], $$[$0], yy.locInfo(this._$)); + break; + case 34:this.$ = $$[$0-1]; + break; + case 35:this.$ = $$[$0]; + break; + case 36:this.$ = new yy.StringLiteral($$[$0], yy.locInfo(this._$)), yy.locInfo(this._$); + break; + case 37:this.$ = new yy.NumberLiteral($$[$0], yy.locInfo(this._$)); + break; + case 38:this.$ = yy.preparePath(true, $$[$0], this._$); + break; + case 39:this.$ = yy.preparePath(false, $$[$0], this._$); + break; + case 40: $$[$0-2].push({part: $$[$0], separator: $$[$0-1]}); this.$ = $$[$0-2]; + break; + case 41:this.$ = [{part: $$[$0]}]; + break; + case 42:this.$ = []; + break; + case 43:$$[$0-1].push($$[$0]); + break; + case 56:this.$ = []; + break; + case 57:$$[$0-1].push($$[$0]); + break; + case 60:this.$ = [$$[$0]]; + break; + case 61:$$[$0-1].push($$[$0]); + break; + case 62:this.$ = [$$[$0]]; + break; + case 63:$$[$0-1].push($$[$0]); + break; + } + }, + table: [{3:1,4:2,5:[2,42],6:3,13:[2,42],14:[2,42],17:[2,42],25:[2,42],28:[2,42],39:[2,42],40:[2,42],42:[2,42]},{1:[3]},{5:[1,4]},{5:[2,2],7:5,8:6,9:7,10:8,11:9,12:10,13:[1,11],14:[1,18],15:16,17:[1,21],20:14,23:15,25:[1,19],28:[1,20],31:[2,2],34:[2,2],37:[2,2],39:[1,12],40:[1,13],42:[1,17]},{1:[2,1]},{5:[2,43],13:[2,43],14:[2,43],17:[2,43],25:[2,43],28:[2,43],31:[2,43],34:[2,43],37:[2,43],39:[2,43],40:[2,43],42:[2,43]},{5:[2,3],13:[2,3],14:[2,3],17:[2,3],25:[2,3],28:[2,3],31:[2,3],34:[2,3],37:[2,3],39:[2,3],40:[2,3],42:[2,3]},{5:[2,4],13:[2,4],14:[2,4],17:[2,4],25:[2,4],28:[2,4],31:[2,4],34:[2,4],37:[2,4],39:[2,4],40:[2,4],42:[2,4]},{5:[2,5],13:[2,5],14:[2,5],17:[2,5],25:[2,5],28:[2,5],31:[2,5],34:[2,5],37:[2,5],39:[2,5],40:[2,5],42:[2,5]},{5:[2,6],13:[2,6],14:[2,6],17:[2,6],25:[2,6],28:[2,6],31:[2,6],34:[2,6],37:[2,6],39:[2,6],40:[2,6],42:[2,6]},{5:[2,7],13:[2,7],14:[2,7],17:[2,7],25:[2,7],28:[2,7],31:[2,7],34:[2,7],37:[2,7],39:[2,7],40:[2,7],42:[2,7]},{5:[2,8],13:[2,8],14:[2,8],17:[2,8],25:[2,8],28:[2,8],31:[2,8],34:[2,8],37:[2,8],39:[2,8],40:[2,8],42:[2,8]},{18:22,38:25,43:23,46:24,48:[1,26],49:[1,27],56:[1,30],62:[1,28],63:29},{18:31,38:25,43:23,46:24,48:[1,26],49:[1,27],56:[1,30],62:[1,28],63:29},{4:32,6:3,13:[2,42],14:[2,42],17:[2,42],25:[2,42],28:[2,42],31:[2,42],34:[2,42],37:[2,42],39:[2,42],40:[2,42],42:[2,42]},{4:33,6:3,13:[2,42],14:[2,42],17:[2,42],25:[2,42],28:[2,42],34:[2,42],37:[2,42],39:[2,42],40:[2,42],42:[2,42]},{12:34,14:[1,18]},{18:35,38:25,43:23,46:24,48:[1,26],49:[1,27],56:[1,30],62:[1,28],63:29},{5:[2,9],13:[2,9],14:[2,9],16:[2,9],17:[2,9],25:[2,9],28:[2,9],31:[2,9],34:[2,9],37:[2,9],39:[2,9],40:[2,9],42:[2,9]},{18:36,38:25,43:23,46:24,48:[1,26],49:[1,27],56:[1,30],62:[1,28],63:29},{18:37,38:25,43:23,46:24,48:[1,26],49:[1,27],56:[1,30],62:[1,28],63:29},{18:38,38:25,43:23,46:24,48:[1,26],49:[1,27],56:[1,30],62:[1,28],63:29},{27:[1,39]},{19:[2,56],27:[2,56],41:[2,56],44:40,48:[2,56],49:[2,56],50:[2,56],51:[2,56],52:[2,56],56:[2,56],59:[2,56],62:[2,56]},{19:[2,25],27:[2,25],41:[2,25],52:[2,25],59:[2,25]},{19:[2,35],27:[2,35],41:[2,35],48:[2,35],49:[2,35],50:[2,35],51:[2,35],52:[2,35],56:[2,35],59:[2,35],62:[2,35]},{19:[2,36],27:[2,36],41:[2,36],48:[2,36],49:[2,36],50:[2,36],51:[2,36],52:[2,36],56:[2,36],59:[2,36],62:[2,36]},{19:[2,37],27:[2,37],41:[2,37],48:[2,37],49:[2,37],50:[2,37],51:[2,37],52:[2,37],56:[2,37],59:[2,37],62:[2,37]},{56:[1,30],63:41},{19:[2,39],27:[2,39],41:[2,39],48:[2,39],49:[2,39],50:[2,39],51:[2,39],52:[2,39],56:[2,39],59:[2,39],62:[2,39],64:[1,42]},{19:[2,41],27:[2,41],41:[2,41],48:[2,41],49:[2,41],50:[2,41],51:[2,41],52:[2,41],56:[2,41],59:[2,41],62:[2,41],64:[2,41]},{41:[1,43]},{21:44,30:46,31:[1,48],33:47,34:[1,49],35:45,37:[2,44]},{24:50,33:51,34:[1,49],37:[2,46]},{16:[1,52]},{27:[1,53]},{26:54,27:[2,48],58:55,59:[1,56]},{27:[2,50],29:57,58:58,59:[1,56]},{19:[1,59]},{5:[2,21],13:[2,21],14:[2,21],17:[2,21],25:[2,21],28:[2,21],31:[2,21],34:[2,21],37:[2,21],39:[2,21],40:[2,21],42:[2,21]},{19:[2,58],27:[2,58],38:63,41:[2,58],45:60,46:67,47:61,48:[1,64],49:[1,65],50:[1,66],51:[1,68],52:[2,58],53:62,54:69,55:70,56:[1,71],59:[2,58],62:[1,28],63:29},{19:[2,38],27:[2,38],41:[2,38],48:[2,38],49:[2,38],50:[2,38],51:[2,38],52:[2,38],56:[2,38],59:[2,38],62:[2,38],64:[1,42]},{56:[1,72]},{5:[2,22],13:[2,22],14:[2,22],17:[2,22],25:[2,22],28:[2,22],31:[2,22],34:[2,22],37:[2,22],39:[2,22],40:[2,22],42:[2,22]},{22:73,37:[1,74]},{37:[2,45]},{4:75,6:3,13:[2,42],14:[2,42],17:[2,42],25:[2,42],28:[2,42],31:[2,42],34:[2,42],37:[2,42],39:[2,42],40:[2,42],42:[2,42]},{37:[2,19]},{18:76,38:25,43:23,46:24,48:[1,26],49:[1,27],56:[1,30],62:[1,28],63:29},{4:77,6:3,13:[2,42],14:[2,42],17:[2,42],25:[2,42],28:[2,42],37:[2,42],39:[2,42],40:[2,42],42:[2,42]},{22:78,37:[1,74]},{37:[2,47]},{5:[2,10],13:[2,10],14:[2,10],17:[2,10],25:[2,10],28:[2,10],31:[2,10],34:[2,10],37:[2,10],39:[2,10],40:[2,10],42:[2,10]},{5:[2,23],13:[2,23],14:[2,23],17:[2,23],25:[2,23],28:[2,23],31:[2,23],34:[2,23],37:[2,23],39:[2,23],40:[2,23],42:[2,23]},{27:[1,79]},{27:[2,49]},{56:[1,81],60:80},{27:[1,82]},{27:[2,51]},{14:[2,11]},{19:[2,24],27:[2,24],41:[2,24],52:[2,24],59:[2,24]},{19:[2,57],27:[2,57],41:[2,57],48:[2,57],49:[2,57],50:[2,57],51:[2,57],52:[2,57],56:[2,57],59:[2,57],62:[2,57]},{19:[2,59],27:[2,59],41:[2,59],52:[2,59],59:[2,59]},{19:[2,26],27:[2,26],41:[2,26],48:[2,26],49:[2,26],50:[2,26],51:[2,26],52:[2,26],56:[2,26],59:[2,26],62:[2,26]},{19:[2,27],27:[2,27],41:[2,27],48:[2,27],49:[2,27],50:[2,27],51:[2,27],52:[2,27],56:[2,27],59:[2,27],62:[2,27]},{19:[2,28],27:[2,28],41:[2,28],48:[2,28],49:[2,28],50:[2,28],51:[2,28],52:[2,28],56:[2,28],59:[2,28],62:[2,28]},{19:[2,29],27:[2,29],41:[2,29],48:[2,29],49:[2,29],50:[2,29],51:[2,29],52:[2,29],56:[2,29],59:[2,29],62:[2,29]},{19:[2,30],27:[2,30],41:[2,30],48:[2,30],49:[2,30],50:[2,30],51:[2,30],52:[2,30],56:[2,30],59:[2,30],62:[2,30]},{18:83,38:25,43:23,46:24,48:[1,26],49:[1,27],56:[1,30],62:[1,28],63:29},{19:[2,32],27:[2,32],41:[2,32],52:[2,32],55:84,56:[1,85],59:[2,32]},{19:[2,60],27:[2,60],41:[2,60],52:[2,60],56:[2,60],59:[2,60]},{19:[2,41],27:[2,41],41:[2,41],48:[2,41],49:[2,41],50:[2,41],51:[2,41],52:[2,41],56:[2,41],57:[1,86],59:[2,41],62:[2,41],64:[2,41]},{19:[2,40],27:[2,40],41:[2,40],48:[2,40],49:[2,40],50:[2,40],51:[2,40],52:[2,40],56:[2,40],59:[2,40],62:[2,40],64:[2,40]},{5:[2,12],13:[2,12],14:[2,12],17:[2,12],25:[2,12],28:[2,12],31:[2,12],34:[2,12],37:[2,12],39:[2,12],40:[2,12],42:[2,12]},{38:87,56:[1,30],63:29},{30:46,31:[1,48],33:47,34:[1,49],35:89,36:88,37:[2,54]},{27:[2,52],32:90,58:91,59:[1,56]},{37:[2,17]},{5:[2,13],13:[2,13],14:[2,13],17:[2,13],25:[2,13],28:[2,13],31:[2,13],34:[2,13],37:[2,13],39:[2,13],40:[2,13],42:[2,13]},{13:[2,14],14:[2,14],17:[2,14],25:[2,14],28:[2,14],31:[2,14],34:[2,14],37:[2,14],39:[2,14],40:[2,14],42:[2,14]},{56:[1,93],61:[1,92]},{56:[2,62],61:[2,62]},{13:[2,15],14:[2,15],17:[2,15],25:[2,15],28:[2,15],34:[2,15],37:[2,15],39:[2,15],40:[2,15],42:[2,15]},{52:[1,94]},{19:[2,61],27:[2,61],41:[2,61],52:[2,61],56:[2,61],59:[2,61]},{57:[1,86]},{38:63,46:67,47:95,48:[1,64],49:[1,65],50:[1,66],51:[1,68],56:[1,30],62:[1,28],63:29},{27:[1,96]},{37:[2,18]},{37:[2,55]},{27:[1,97]},{27:[2,53]},{27:[2,34]},{56:[2,63],61:[2,63]},{19:[2,31],27:[2,31],41:[2,31],48:[2,31],49:[2,31],50:[2,31],51:[2,31],52:[2,31],56:[2,31],59:[2,31],62:[2,31]},{19:[2,33],27:[2,33],41:[2,33],52:[2,33],56:[2,33],59:[2,33]},{5:[2,20],13:[2,20],14:[2,20],17:[2,20],25:[2,20],28:[2,20],31:[2,20],34:[2,20],37:[2,20],39:[2,20],40:[2,20],42:[2,20]},{13:[2,16],14:[2,16],17:[2,16],25:[2,16],28:[2,16],31:[2,16],34:[2,16],37:[2,16],39:[2,16],40:[2,16],42:[2,16]}], + defaultActions: {4:[2,1],45:[2,45],47:[2,19],51:[2,47],55:[2,49],58:[2,51],59:[2,11],77:[2,17],88:[2,18],89:[2,55],91:[2,53],92:[2,34]}, + parseError: function parseError(str, hash) { + throw new Error(str); + }, + parse: function parse(input) { + var self = this, stack = [0], vstack = [null], lstack = [], table = this.table, yytext = "", yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1; + this.lexer.setInput(input); + this.lexer.yy = this.yy; + this.yy.lexer = this.lexer; + this.yy.parser = this; + if (typeof this.lexer.yylloc == "undefined") + this.lexer.yylloc = {}; + var yyloc = this.lexer.yylloc; + lstack.push(yyloc); + var ranges = this.lexer.options && this.lexer.options.ranges; + if (typeof this.yy.parseError === "function") + this.parseError = this.yy.parseError; + function popStack(n) { + stack.length = stack.length - 2 * n; + vstack.length = vstack.length - n; + lstack.length = lstack.length - n; + } + function lex() { + var token; + token = self.lexer.lex() || 1; + if (typeof token !== "number") { + token = self.symbols_[token] || token; + } + return token; + } + var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected; + while (true) { + state = stack[stack.length - 1]; + if (this.defaultActions[state]) { + action = this.defaultActions[state]; + } else { + if (symbol === null || typeof symbol == "undefined") { + symbol = lex(); + } + action = table[state] && table[state][symbol]; + } + if (typeof action === "undefined" || !action.length || !action[0]) { + var errStr = ""; + if (!recovering) { + expected = []; + for (p in table[state]) + if (this.terminals_[p] && p > 2) { + expected.push("'" + this.terminals_[p] + "'"); + } + if (this.lexer.showPosition) { + errStr = "Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", ") + ", got '" + (this.terminals_[symbol] || symbol) + "'"; + } else { + errStr = "Parse error on line " + (yylineno + 1) + ": Unexpected " + (symbol == 1?"end of input":"'" + (this.terminals_[symbol] || symbol) + "'"); + } + this.parseError(errStr, {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected}); + } + } + if (action[0] instanceof Array && action.length > 1) { + throw new Error("Parse Error: multiple actions possible at state: " + state + ", token: " + symbol); + } + switch (action[0]) { + case 1: + stack.push(symbol); + vstack.push(this.lexer.yytext); + lstack.push(this.lexer.yylloc); + stack.push(action[1]); + symbol = null; + if (!preErrorSymbol) { + yyleng = this.lexer.yyleng; + yytext = this.lexer.yytext; + yylineno = this.lexer.yylineno; + yyloc = this.lexer.yylloc; + if (recovering > 0) + recovering--; + } else { + symbol = preErrorSymbol; + preErrorSymbol = null; + } + break; + case 2: + len = this.productions_[action[1]][1]; + yyval.$ = vstack[vstack.length - len]; + yyval._$ = {first_line: lstack[lstack.length - (len || 1)].first_line, last_line: lstack[lstack.length - 1].last_line, first_column: lstack[lstack.length - (len || 1)].first_column, last_column: lstack[lstack.length - 1].last_column}; + if (ranges) { + yyval._$.range = [lstack[lstack.length - (len || 1)].range[0], lstack[lstack.length - 1].range[1]]; + } + r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack); + if (typeof r !== "undefined") { + return r; + } + if (len) { + stack = stack.slice(0, -1 * len * 2); + vstack = vstack.slice(0, -1 * len); + lstack = lstack.slice(0, -1 * len); + } + stack.push(this.productions_[action[1]][0]); + vstack.push(yyval.$); + lstack.push(yyval._$); + newState = table[stack[stack.length - 2]][stack[stack.length - 1]]; + stack.push(newState); + break; + case 3: + return true; + } + } + return true; + } + }; + /* Jison generated lexer */ + var lexer = (function(){ + var lexer = ({EOF:1, + parseError:function parseError(str, hash) { + if (this.yy.parser) { + this.yy.parser.parseError(str, hash); + } else { + throw new Error(str); + } + }, + setInput:function (input) { + this._input = input; + this._more = this._less = this.done = false; + this.yylineno = this.yyleng = 0; + this.yytext = this.matched = this.match = ''; + this.conditionStack = ['INITIAL']; + this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0}; + if (this.options.ranges) this.yylloc.range = [0,0]; + this.offset = 0; + return this; + }, + input:function () { + var ch = this._input[0]; + this.yytext += ch; + this.yyleng++; + this.offset++; + this.match += ch; + this.matched += ch; + var lines = ch.match(/(?:\r\n?|\n).*/g); + if (lines) { + this.yylineno++; + this.yylloc.last_line++; + } else { + this.yylloc.last_column++; + } + if (this.options.ranges) this.yylloc.range[1]++; + + this._input = this._input.slice(1); + return ch; + }, + unput:function (ch) { + var len = ch.length; + var lines = ch.split(/(?:\r\n?|\n)/g); + + this._input = ch + this._input; + this.yytext = this.yytext.substr(0, this.yytext.length-len-1); + //this.yyleng -= len; + this.offset -= len; + var oldLines = this.match.split(/(?:\r\n?|\n)/g); + this.match = this.match.substr(0, this.match.length-1); + this.matched = this.matched.substr(0, this.matched.length-1); + + if (lines.length-1) this.yylineno -= lines.length-1; + var r = this.yylloc.range; + + this.yylloc = {first_line: this.yylloc.first_line, + last_line: this.yylineno+1, + first_column: this.yylloc.first_column, + last_column: lines ? + (lines.length === oldLines.length ? this.yylloc.first_column : 0) + oldLines[oldLines.length - lines.length].length - lines[0].length: + this.yylloc.first_column - len + }; + + if (this.options.ranges) { + this.yylloc.range = [r[0], r[0] + this.yyleng - len]; + } + return this; + }, + more:function () { + this._more = true; + return this; + }, + less:function (n) { + this.unput(this.match.slice(n)); + }, + pastInput:function () { + var past = this.matched.substr(0, this.matched.length - this.match.length); + return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, ""); + }, + upcomingInput:function () { + var next = this.match; + if (next.length < 20) { + next += this._input.substr(0, 20-next.length); + } + return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, ""); + }, + showPosition:function () { + var pre = this.pastInput(); + var c = new Array(pre.length + 1).join("-"); + return pre + this.upcomingInput() + "\n" + c+"^"; + }, + next:function () { + if (this.done) { + return this.EOF; + } + if (!this._input) this.done = true; + + var token, + match, + tempMatch, + index, + col, + lines; + if (!this._more) { + this.yytext = ''; + this.match = ''; + } + var rules = this._currentRules(); + for (var i=0;i < rules.length; i++) { + tempMatch = this._input.match(this.rules[rules[i]]); + if (tempMatch && (!match || tempMatch[0].length > match[0].length)) { + match = tempMatch; + index = i; + if (!this.options.flex) break; + } + } + if (match) { + lines = match[0].match(/(?:\r\n?|\n).*/g); + if (lines) this.yylineno += lines.length; + this.yylloc = {first_line: this.yylloc.last_line, + last_line: this.yylineno+1, + first_column: this.yylloc.last_column, + last_column: lines ? lines[lines.length-1].length-lines[lines.length-1].match(/\r?\n?/)[0].length : this.yylloc.last_column + match[0].length}; + this.yytext += match[0]; + this.match += match[0]; + this.matches = match; + this.yyleng = this.yytext.length; + if (this.options.ranges) { + this.yylloc.range = [this.offset, this.offset += this.yyleng]; + } + this._more = false; + this._input = this._input.slice(match[0].length); + this.matched += match[0]; + token = this.performAction.call(this, this.yy, this, rules[index],this.conditionStack[this.conditionStack.length-1]); + if (this.done && this._input) this.done = false; + if (token) return token; + else return; + } + if (this._input === "") { + return this.EOF; + } else { + return this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(), + {text: "", token: null, line: this.yylineno}); + } + }, + lex:function lex() { + var r = this.next(); + if (typeof r !== 'undefined') { + return r; + } else { + return this.lex(); + } + }, + begin:function begin(condition) { + this.conditionStack.push(condition); + }, + popState:function popState() { + return this.conditionStack.pop(); + }, + _currentRules:function _currentRules() { + return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules; + }, + topState:function () { + return this.conditionStack[this.conditionStack.length-2]; + }, + pushState:function begin(condition) { + this.begin(condition); + }}); + lexer.options = {}; + lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) { + + + function strip(start, end) { + return yy_.yytext = yy_.yytext.substr(start, yy_.yyleng-end); + } + + + var YYSTATE=YY_START + switch($avoiding_name_collisions) { + case 0: + if(yy_.yytext.slice(-2) === "\\\\") { + strip(0,1); + this.begin("mu"); + } else if(yy_.yytext.slice(-1) === "\\") { + strip(0,1); + this.begin("emu"); + } else { + this.begin("mu"); + } + if(yy_.yytext) return 14; + + break; + case 1:return 14; + break; + case 2: + this.popState(); + return 14; + + break; + case 3: + yy_.yytext = yy_.yytext.substr(5, yy_.yyleng-9); + this.popState(); + return 16; + + break; + case 4: return 14; + break; + case 5: + this.popState(); + return 13; + + break; + case 6:return 51; + break; + case 7:return 52; + break; + case 8: return 17; + break; + case 9: + this.popState(); + this.begin('raw'); + return 19; + + break; + case 10:return 42; + break; + case 11:return 25; + break; + case 12:return 37; + break; + case 13:this.popState(); return 34; + break; + case 14:this.popState(); return 34; + break; + case 15:return 28; + break; + case 16:return 31; + break; + case 17:return 40; + break; + case 18:return 39; + break; + case 19: + this.unput(yy_.yytext); + this.popState(); + this.begin('com'); + + break; + case 20: + this.popState(); + return 13; + + break; + case 21:return 39; + break; + case 22:return 57; + break; + case 23:return 56; + break; + case 24:return 56; + break; + case 25:return 64; + break; + case 26:// ignore whitespace + break; + case 27:this.popState(); return 41; + break; + case 28:this.popState(); return 27; + break; + case 29:yy_.yytext = strip(1,2).replace(/\\"/g,'"'); return 48; + break; + case 30:yy_.yytext = strip(1,2).replace(/\\'/g,"'"); return 48; + break; + case 31:return 62; + break; + case 32:return 50; + break; + case 33:return 50; + break; + case 34:return 49; + break; + case 35:return 59; + break; + case 36:return 61; + break; + case 37:return 56; + break; + case 38:yy_.yytext = strip(1,2); return 56; + break; + case 39:return 'INVALID'; + break; + case 40:return 5; + break; + } + }; + lexer.rules = [/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|\\\{\{|\\\\\{\{|$)))/,/^(?:\{\{\{\{\/[^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=[=}\s\/.])\}\}\}\})/,/^(?:[^\x00]*?(?=(\{\{\{\{\/)))/,/^(?:[\s\S]*?--(~)?\}\})/,/^(?:\()/,/^(?:\))/,/^(?:\{\{\{\{)/,/^(?:\}\}\}\})/,/^(?:\{\{(~)?>)/,/^(?:\{\{(~)?#)/,/^(?:\{\{(~)?\/)/,/^(?:\{\{(~)?\^\s*(~)?\}\})/,/^(?:\{\{(~)?\s*else\s*(~)?\}\})/,/^(?:\{\{(~)?\^)/,/^(?:\{\{(~)?\s*else\b)/,/^(?:\{\{(~)?\{)/,/^(?:\{\{(~)?&)/,/^(?:\{\{(~)?!--)/,/^(?:\{\{(~)?![\s\S]*?\}\})/,/^(?:\{\{(~)?)/,/^(?:=)/,/^(?:\.\.)/,/^(?:\.(?=([=~}\s\/.)|])))/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}(~)?\}\})/,/^(?:(~)?\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@)/,/^(?:true(?=([~}\s)])))/,/^(?:false(?=([~}\s)])))/,/^(?:-?[0-9]+(?:\.[0-9]+)?(?=([~}\s)])))/,/^(?:as\s+\|)/,/^(?:\|)/,/^(?:([^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=([=~}\s\/.)|]))))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:$)/]; + lexer.conditions = {"mu":{"rules":[6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40],"inclusive":false},"emu":{"rules":[2],"inclusive":false},"com":{"rules":[5],"inclusive":false},"raw":{"rules":[3,4],"inclusive":false},"INITIAL":{"rules":[0,1,40],"inclusive":true}}; + return lexer;})() + parser.lexer = lexer; + function Parser () { this.yy = {}; }Parser.prototype = parser;parser.Parser = Parser; + return new Parser; + })();__exports__["default"] = handlebars; + /* jshint ignore:end */ + }); +enifed("htmlbars-syntax/handlebars/compiler/visitor", + ["exports"], + function(__exports__) { + "use strict"; + function Visitor() {} + + Visitor.prototype = { + constructor: Visitor, + + accept: function(object) { + return object && this[object.type](object); + }, + + Program: function(program) { + var body = program.body, + i, l; + + for(i=0, l=body.length; i": ">", + '"': """, + "'": "'", + "`": "`" + }; + + var badChars = /[&<>"'`]/g; + var possible = /[&<>"'`]/; + + function escapeChar(chr) { + return escape[chr]; + } + + function extend(obj /* , ...source */) { + for (var i = 1; i < arguments.length; i++) { + for (var key in arguments[i]) { + if (Object.prototype.hasOwnProperty.call(arguments[i], key)) { + obj[key] = arguments[i][key]; + } + } + } + + return obj; + } + + __exports__.extend = extend;var toString = Object.prototype.toString; + __exports__.toString = toString; + // Sourced from lodash + // https://github.com/bestiejs/lodash/blob/master/LICENSE.txt + var isFunction = function(value) { + return typeof value === 'function'; + }; + // fallback for older versions of Chrome and Safari + /* istanbul ignore next */ + if (isFunction(/x/)) { + isFunction = function(value) { + return typeof value === 'function' && toString.call(value) === '[object Function]'; + }; + } + var isFunction; + __exports__.isFunction = isFunction; + /* istanbul ignore next */ + var isArray = Array.isArray || function(value) { + return (value && typeof value === 'object') ? toString.call(value) === '[object Array]' : false; + }; + __exports__.isArray = isArray; + + function escapeExpression(string) { + // don't escape SafeStrings, since they're already safe + if (string && string.toHTML) { + return string.toHTML(); + } else if (string == null) { + return ""; + } else if (!string) { + return string + ''; + } + + // Force a string conversion as this will be done by the append regardless and + // the regex test will do this transparently behind the scenes, causing issues if + // an object's to string has escaped characters in it. + string = "" + string; + + if(!possible.test(string)) { return string; } + return string.replace(badChars, escapeChar); + } + + __exports__.escapeExpression = escapeExpression;function isEmpty(value) { + if (!value && value !== 0) { + return true; + } else if (isArray(value) && value.length === 0) { + return true; + } else { + return false; + } + } + + __exports__.isEmpty = isEmpty;function appendContextPath(contextPath, id) { + return (contextPath ? contextPath + '.' : '') + id; + } + + __exports__.appendContextPath = appendContextPath; + }); +enifed("htmlbars-syntax/node-handlers", + ["./builders","../htmlbars-util/array-utils","./utils","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; + var buildProgram = __dependency1__.buildProgram; + var buildBlock = __dependency1__.buildBlock; + var buildHash = __dependency1__.buildHash; + var forEach = __dependency2__.forEach; + var appendChild = __dependency3__.appendChild; + + var nodeHandlers = { + + Program: function(program) { + var body = []; + var node = buildProgram(body, program.blockParams); + var i, l = program.body.length; + + this.elementStack.push(node); + + if (l === 0) { return this.elementStack.pop(); } + + for (i = 0; i < l; i++) { + this.acceptNode(program.body[i]); + } + + this.acceptToken(this.tokenizer.tokenizeEOF()); + + // Ensure that that the element stack is balanced properly. + var poppedNode = this.elementStack.pop(); + if (poppedNode !== node) { + throw new Error("Unclosed element `" + poppedNode.tag + "` (on line " + poppedNode.loc.start.line + ")."); + } + + return node; + }, + + BlockStatement: function(block) { + delete block.inverseStrip; + delete block.openString; + delete block.closeStrip; + + if (this.tokenizer.state === 'comment') { + this.tokenizer.addChar('{{' + this.sourceForMustache(block) + '}}'); + return; + } + + switchToHandlebars(this); + this.acceptToken(block); + + var sexpr = this.acceptNode(block.sexpr); + var program = block.program ? this.acceptNode(block.program) : null; + var inverse = block.inverse ? this.acceptNode(block.inverse) : null; + + var node = buildBlock(sexpr, program, inverse); + var parentProgram = this.currentElement(); + appendChild(parentProgram, node); + }, + + MustacheStatement: function(mustache) { + delete mustache.strip; + + if (this.tokenizer.state === 'comment') { + this.tokenizer.addChar('{{' + this.sourceForMustache(mustache) + '}}'); + return; + } + + this.acceptNode(mustache.sexpr); + switchToHandlebars(this); + this.acceptToken(mustache); + + return mustache; + }, + + ContentStatement: function(content) { + var changeLines = 0; + if (content.rightStripped) { + changeLines = leadingNewlineDifference(content.original, content.value); + } + + this.tokenizer.line = this.tokenizer.line + changeLines; + + var tokens = this.tokenizer.tokenizePart(content.value); + + return forEach(tokens, this.acceptToken, this); + }, + + CommentStatement: function(comment) { + return comment; + }, + + PartialStatement: function(partial) { + appendChild(this.currentElement(), partial); + return partial; + }, + + SubExpression: function(sexpr) { + delete sexpr.isHelper; + + this.acceptNode(sexpr.path); + + if (sexpr.params) { + for (var i = 0; i < sexpr.params.length; i++) { + this.acceptNode(sexpr.params[i]); + } + } else { + sexpr.params = []; + } + + if (sexpr.hash) { + this.acceptNode(sexpr.hash); + } else { + sexpr.hash = buildHash(); + } + + return sexpr; + }, + + PathExpression: function(path) { + delete path.data; + delete path.depth; + + return path; + }, + + Hash: function(hash) { + for (var i = 0; i < hash.pairs.length; i++) { + this.acceptNode(hash.pairs[i].value); + } + + return hash; + }, + + StringLiteral: function() {}, + BooleanLiteral: function() {}, + NumberLiteral: function() {} + }; + + function switchToHandlebars(processor) { + var token = processor.tokenizer.token; + + if (token && token.type === 'Chars') { + processor.acceptToken(token); + processor.tokenizer.token = null; + } + } + + function leadingNewlineDifference(original, value) { + if (value === '') { + // if it is empty, just return the count of newlines + // in original + return original.split("\n").length - 1; + } + + // otherwise, return the number of newlines prior to + // `value` + var difference = original.split(value)[0]; + var lines = difference.split(/\n/); + + return lines.length - 1; + } + + __exports__["default"] = nodeHandlers; + }); +enifed("htmlbars-syntax/parser", + ["./handlebars/compiler/base","./tokenizer","../simple-html-tokenizer/entity-parser","../simple-html-tokenizer/char-refs/full","./node-handlers","./token-handlers","../htmlbars-syntax","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __exports__) { + "use strict"; + var parse = __dependency1__.parse; + var Tokenizer = __dependency2__.Tokenizer; + var EntityParser = __dependency3__["default"]; + var fullCharRefs = __dependency4__["default"]; + var nodeHandlers = __dependency5__["default"]; + var tokenHandlers = __dependency6__["default"]; + + // this should be: + // `import * from "../htmlbars-syntax"; + // + // But this version of the transpiler does not support it properly + var syntax = __dependency7__; + + var splitLines; + // IE8 throws away blank pieces when splitting strings with a regex + // So we split using a string instead as appropriate + if ("foo\n\nbar".split(/\n/).length === 2) { + splitLines = function(str) { + var clean = str.replace(/\r\n?/g, '\n'); + return clean.split('\n'); + }; + } else { + splitLines = function(str) { + return str.split(/(?:\r\n?|\n)/g); + }; + } + + function preprocess(html, options) { + var ast = (typeof html === 'object') ? html : parse(html); + var combined = new HTMLProcessor(html, options).acceptNode(ast); + + if (options && options.plugins && options.plugins.ast) { + for (var i = 0, l = options.plugins.ast.length; i < l; i++) { + var plugin = new options.plugins.ast[i](options); + + plugin.syntax = syntax; + + combined = plugin.transform(combined); + } + } + + return combined; + } + + __exports__.preprocess = preprocess;function HTMLProcessor(source, options) { + this.options = options || {}; + this.elementStack = []; + this.tokenizer = new Tokenizer('', new EntityParser(fullCharRefs)); + this.nodeHandlers = nodeHandlers; + this.tokenHandlers = tokenHandlers; + + if (typeof source === 'string') { + this.source = splitLines(source); + } + } + + HTMLProcessor.prototype.acceptNode = function(node) { + return this.nodeHandlers[node.type].call(this, node); + }; + + HTMLProcessor.prototype.acceptToken = function(token) { + if (token) { + return this.tokenHandlers[token.type].call(this, token); + } + }; + + HTMLProcessor.prototype.currentElement = function() { + return this.elementStack[this.elementStack.length - 1]; + }; + + HTMLProcessor.prototype.sourceForMustache = function(mustache) { + var firstLine = mustache.loc.start.line - 1; + var lastLine = mustache.loc.end.line - 1; + var currentLine = firstLine - 1; + var firstColumn = mustache.loc.start.column + 2; + var lastColumn = mustache.loc.end.column - 2; + var string = []; + var line; + + if (!this.source) { + return '{{' + mustache.path.id.original + '}}'; + } + + while (currentLine < lastLine) { + currentLine++; + line = this.source[currentLine]; + + if (currentLine === firstLine) { + if (firstLine === lastLine) { + string.push(line.slice(firstColumn, lastColumn)); + } else { + string.push(line.slice(firstColumn)); + } + } else if (currentLine === lastLine) { + string.push(line.slice(0, lastColumn)); + } else { + string.push(line); + } + } + + return string.join('\n'); + }; + }); +enifed("htmlbars-syntax/token-handlers", + ["../htmlbars-util/array-utils","./builders","./utils","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; + var forEach = __dependency1__.forEach; + var buildProgram = __dependency2__.buildProgram; + var buildComponent = __dependency2__.buildComponent; + var buildElement = __dependency2__.buildElement; + var buildComment = __dependency2__.buildComment; + var buildText = __dependency2__.buildText; + var appendChild = __dependency3__.appendChild; + var parseComponentBlockParams = __dependency3__.parseComponentBlockParams; + + // The HTML elements in this list are speced by + // http://www.w3.org/TR/html-markup/syntax.html#syntax-elements, + // and will be forced to close regardless of if they have a + // self-closing /> at the end. + var voidTagNames = "area base br col command embed hr img input keygen link meta param source track wbr"; + var voidMap = {}; + + forEach(voidTagNames.split(" "), function(tagName) { + voidMap[tagName] = true; + }); + + // Except for `mustache`, all tokens are only allowed outside of + // a start or end tag. + var tokenHandlers = { + Comment: function(token) { + var current = this.currentElement(); + var comment = buildComment(token.chars); + appendChild(current, comment); + }, + + Chars: function(token) { + var current = this.currentElement(); + var text = buildText(token.chars); + appendChild(current, text); + }, + + StartTag: function(tag) { + var element = buildElement(tag.tagName, tag.attributes, tag.modifiers || [], []); + element.loc = { + start: { line: tag.firstLine, column: tag.firstColumn}, + end: { line: null, column: null} + }; + + this.elementStack.push(element); + if (voidMap.hasOwnProperty(tag.tagName) || tag.selfClosing) { + tokenHandlers.EndTag.call(this, tag); + } + }, + + BlockStatement: function(/*block*/) { + if (this.tokenizer.state === 'comment') { + return; + } else if (this.tokenizer.state !== 'data') { + throw new Error("A block may only be used inside an HTML element or another block."); + } + }, + + MustacheStatement: function(mustache) { + var tokenizer = this.tokenizer; + + switch(tokenizer.state) { + // Tag helpers + case "tagName": + tokenizer.addElementModifier(mustache); + tokenizer.state = "beforeAttributeName"; + return; + case "beforeAttributeName": + tokenizer.addElementModifier(mustache); + return; + case "attributeName": + case "afterAttributeName": + tokenizer.finalizeAttributeValue(); + tokenizer.addElementModifier(mustache); + tokenizer.state = "beforeAttributeName"; + return; + case "afterAttributeValueQuoted": + tokenizer.addElementModifier(mustache); + tokenizer.state = "beforeAttributeName"; + return; + + // Attribute values + case "beforeAttributeValue": + tokenizer.markAttributeQuoted(false); + tokenizer.addToAttributeValue(mustache); + tokenizer.state = 'attributeValueUnquoted'; + return; + case "attributeValueDoubleQuoted": + case "attributeValueSingleQuoted": + case "attributeValueUnquoted": + tokenizer.addToAttributeValue(mustache); + return; + + // TODO: Only append child when the tokenizer state makes + // sense to do so, otherwise throw an error. + default: + appendChild(this.currentElement(), mustache); + } + }, + + EndTag: function(tag) { + var element = this.elementStack.pop(); + var parent = this.currentElement(); + var disableComponentGeneration = this.options.disableComponentGeneration === true; + + validateEndTag(tag, element); + + if (disableComponentGeneration || element.tag.indexOf("-") === -1) { + appendChild(parent, element); + } else { + var program = buildProgram(element.children); + parseComponentBlockParams(element, program); + var component = buildComponent(element.tag, element.attributes, program); + appendChild(parent, component); + } + + } + + }; + + function validateEndTag(tag, element) { + var error; + + if (voidMap[tag.tagName] && element.tag === undefined) { + // For void elements, we check element.tag is undefined because endTag is called by the startTag token handler in + // the normal case, so checking only voidMap[tag.tagName] would lead to an error being thrown on the opening tag. + error = "Invalid end tag " + formatEndTagInfo(tag) + " (void elements cannot have end tags)."; + } else if (element.tag === undefined) { + error = "Closing tag " + formatEndTagInfo(tag) + " without an open tag."; + } else if (element.tag !== tag.tagName) { + error = "Closing tag " + formatEndTagInfo(tag) + " did not match last open tag `" + element.tag + "` (on line " + + element.loc.start.line + ")."; + } + + if (error) { throw new Error(error); } + } + + function formatEndTagInfo(tag) { + return "`" + tag.tagName + "` (on line " + tag.lastLine + ")"; + } + + __exports__["default"] = tokenHandlers; + }); +enifed("htmlbars-syntax/tokenizer", + ["../simple-html-tokenizer","./utils","../htmlbars-util/array-utils","./builders","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { + "use strict"; + var Tokenizer = __dependency1__.Tokenizer; + var isHelper = __dependency2__.isHelper; + var map = __dependency3__.map; + var builders = __dependency4__["default"]; + + Tokenizer.prototype.createAttribute = function(char) { + if (this.token.type === 'EndTag') { + throw new Error('Invalid end tag: closing tag must not have attributes, in ' + formatTokenInfo(this) + '.'); + } + this.currentAttribute = builders.attr(char.toLowerCase(), [], null); + this.token.attributes.push(this.currentAttribute); + this.state = 'attributeName'; + }; + + Tokenizer.prototype.markAttributeQuoted = function(value) { + this.currentAttribute.quoted = value; + }; + + Tokenizer.prototype.addToAttributeName = function(char) { + this.currentAttribute.name += char; + }; + + Tokenizer.prototype.addToAttributeValue = function(char) { + var value = this.currentAttribute.value; + + if (!this.currentAttribute.quoted && char === '/') { + throw new Error("A space is required between an unquoted attribute value and `/`, in " + formatTokenInfo(this) + + '.'); + } + if (!this.currentAttribute.quoted && value.length > 0 && + (char.type === 'MustacheStatement' || value[0].type === 'MustacheStatement')) { + throw new Error("Unquoted attribute value must be a single string or mustache (on line " + this.line + ")"); + } + + if (typeof char === 'object') { + if (char.type === 'MustacheStatement') { + value.push(char); + } else { + throw new Error("Unsupported node in attribute value: " + char.type); + } + } else { + if (value.length > 0 && value[value.length - 1].type === 'TextNode') { + value[value.length - 1].chars += char; + } else { + value.push(builders.text(char)); + } + } + }; + + Tokenizer.prototype.finalizeAttributeValue = function() { + if (this.currentAttribute) { + this.currentAttribute.value = prepareAttributeValue(this.currentAttribute); + delete this.currentAttribute.quoted; + delete this.currentAttribute; + } + }; + + Tokenizer.prototype.addElementModifier = function(mustache) { + if (!this.token.modifiers) { + this.token.modifiers = []; + } + + var modifier = builders.elementModifier(mustache.sexpr); + this.token.modifiers.push(modifier); + }; + + function prepareAttributeValue(attr) { + var parts = attr.value; + var length = parts.length; + + if (length === 0) { + return builders.text(''); + } else if (length === 1 && parts[0].type === "TextNode") { + return parts[0]; + } else if (!attr.quoted) { + return parts[0]; + } else { + return builders.concat(map(parts, prepareConcatPart)); + } + } + + function prepareConcatPart(node) { + switch (node.type) { + case 'TextNode': return builders.string(node.chars); + case 'MustacheStatement': return unwrapMustache(node); + default: + throw new Error("Unsupported node in quoted attribute value: " + node.type); + } + } + + function formatTokenInfo(tokenizer) { + return '`' + tokenizer.token.tagName + '` (on line ' + tokenizer.line + ')'; + } + + function unwrapMustache(mustache) { + if (isHelper(mustache.sexpr)) { + return mustache.sexpr; + } else { + return mustache.sexpr.path; + } + } + + __exports__.unwrapMustache = unwrapMustache;__exports__.Tokenizer = Tokenizer; + }); +enifed("htmlbars-syntax/utils", + ["../htmlbars-util/array-utils","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var indexOfArray = __dependency1__.indexOfArray; + // Regex to validate the identifier for block parameters. + // Based on the ID validation regex in Handlebars. + + var ID_INVERSE_PATTERN = /[!"#%-,\.\/;->@\[-\^`\{-~]/; + + // Checks the component's attributes to see if it uses block params. + // If it does, registers the block params with the program and + // removes the corresponding attributes from the element. + + function parseComponentBlockParams(element, program) { + var l = element.attributes.length; + var attrNames = []; + + for (var i = 0; i < l; i++) { + attrNames.push(element.attributes[i].name); + } + + var asIndex = indexOfArray(attrNames, 'as'); + + if (asIndex !== -1 && l > asIndex && attrNames[asIndex + 1].charAt(0) === '|') { + // Some basic validation, since we're doing the parsing ourselves + var paramsString = attrNames.slice(asIndex).join(' '); + if (paramsString.charAt(paramsString.length - 1) !== '|' || paramsString.match(/\|/g).length !== 2) { + throw new Error('Invalid block parameters syntax: \'' + paramsString + '\''); + } + + var params = []; + for (i = asIndex + 1; i < l; i++) { + var param = attrNames[i].replace(/\|/g, ''); + if (param !== '') { + if (ID_INVERSE_PATTERN.test(param)) { + throw new Error('Invalid identifier for block parameters: \'' + param + '\' in \'' + paramsString + '\''); + } + params.push(param); + } + } + + if (params.length === 0) { + throw new Error('Cannot use zero block parameters: \'' + paramsString + '\''); + } + + element.attributes = element.attributes.slice(0, asIndex); + program.blockParams = params; + } + } + + __exports__.parseComponentBlockParams = parseComponentBlockParams;function childrenFor(node) { + if (node.type === 'Program') { + return node.body; + } + if (node.type === 'ElementNode') { + return node.children; + } + } + + __exports__.childrenFor = childrenFor;function appendChild(parent, node) { + childrenFor(parent).push(node); + } + + __exports__.appendChild = appendChild;function isHelper(sexpr) { + return (sexpr.params && sexpr.params.length > 0) || + (sexpr.hash && sexpr.hash.pairs.length > 0); + } + + __exports__.isHelper = isHelper; + }); +enifed("htmlbars-syntax/walker", + ["exports"], + function(__exports__) { + "use strict"; + function Walker(order) { + this.order = order; + this.stack = []; + } + + __exports__["default"] = Walker; + + Walker.prototype.visit = function(node, callback) { + if (!node) { + return; + } + + this.stack.push(node); + + if (this.order === 'post') { + this.children(node, callback); + callback(node, this); + } else { + callback(node, this); + this.children(node, callback); + } + + this.stack.pop(); + }; + + var visitors = { + Program: function(walker, node, callback) { + for (var i = 0; i < node.body.length; i++) { + walker.visit(node.body[i], callback); + } + }, + + ElementNode: function(walker, node, callback) { + for (var i = 0; i < node.children.length; i++) { + walker.visit(node.children[i], callback); + } + }, + + BlockStatement: function(walker, node, callback) { + walker.visit(node.program, callback); + walker.visit(node.inverse, callback); + }, + + ComponentNode: function(walker, node, callback) { + walker.visit(node.program, callback); + } + }; + + Walker.prototype.children = function(node, callback) { + var visitor = visitors[node.type]; + if (visitor) { + visitor(this, node, callback); + } + }; + }); +enifed("htmlbars-test-helpers", + ["exports"], + function(__exports__) { + "use strict"; + function equalInnerHTML(fragment, html) { + var actualHTML = normalizeInnerHTML(fragment.innerHTML); + QUnit.push(actualHTML === html, actualHTML, html); + } + + __exports__.equalInnerHTML = equalInnerHTML;function equalHTML(node, html) { + var fragment; + if (!node.nodeType && node.length) { + fragment = document.createDocumentFragment(); + while (node[0]) { + fragment.appendChild(node[0]); + } + } else { + fragment = node; + } + + var div = document.createElement("div"); + div.appendChild(fragment.cloneNode(true)); + + equalInnerHTML(div, html); + } + + __exports__.equalHTML = equalHTML;// detect weird IE8 html strings + var ie8InnerHTMLTestElement = document.createElement('div'); + ie8InnerHTMLTestElement.setAttribute('id', 'womp'); + var ie8InnerHTML = (ie8InnerHTMLTestElement.outerHTML.indexOf('id=womp') > -1); + + // detect side-effects of cloning svg elements in IE9-11 + var ieSVGInnerHTML = (function () { + if (!document.createElementNS) { + return false; + } + var div = document.createElement('div'); + var node = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + div.appendChild(node); + var clone = div.cloneNode(true); + return clone.innerHTML === ''; + })(); + + function normalizeInnerHTML(actualHTML) { + if (ie8InnerHTML) { + // drop newlines in IE8 + actualHTML = actualHTML.replace(/\r\n/gm, ''); + // downcase ALLCAPS tags in IE8 + actualHTML = actualHTML.replace(/<\/?[A-Z\-]+/gi, function(tag){ + return tag.toLowerCase(); + }); + // quote ids in IE8 + actualHTML = actualHTML.replace(/id=([^ >]+)/gi, function(match, id){ + return 'id="'+id+'"'; + }); + // IE8 adds ':' to some tags + // becomes <:keygen> + actualHTML = actualHTML.replace(/<(\/?):([^ >]+)/gi, function(match, slash, tag){ + return '<'+slash+tag; + }); + + // Normalize the style attribute + actualHTML = actualHTML.replace(/style="(.+?)"/gi, function(match, val){ + return 'style="'+val.toLowerCase()+';"'; + }); + + } + if (ieSVGInnerHTML) { + // Replace `` with ``, etc. + // drop namespace attribute + actualHTML = actualHTML.replace(/ xmlns="[^"]+"/, ''); + // replace self-closing elements + actualHTML = actualHTML.replace(/<([^ >]+) [^\/>]*\/>/gi, function(tag, tagName) { + return tag.slice(0, tag.length - 3) + '>'; + }); + } + + return actualHTML; + } + + __exports__.normalizeInnerHTML = normalizeInnerHTML;// detect weird IE8 checked element string + var checkedInput = document.createElement('input'); + checkedInput.setAttribute('checked', 'checked'); + var checkedInputString = checkedInput.outerHTML; + function isCheckedInputHTML(element) { + equal(element.outerHTML, checkedInputString); + } + + __exports__.isCheckedInputHTML = isCheckedInputHTML;// check which property has the node's text content + var textProperty = document.createElement('div').textContent === undefined ? 'innerText' : 'textContent'; + function getTextContent(el) { + // textNode + if (el.nodeType === 3) { + return el.nodeValue; + } else { + return el[textProperty]; + } + } + + __exports__.getTextContent = getTextContent;// IE8 does not have Object.create, so use a polyfill if needed. + // Polyfill based on Mozilla's (MDN) + function createObject(obj) { + if (typeof Object.create === 'function') { + return Object.create(obj); + } else { + var Temp = function() {}; + Temp.prototype = obj; + return new Temp(); + } + } + __exports__.createObject = createObject; + }); +enifed("htmlbars-util", + ["./htmlbars-util/safe-string","./htmlbars-util/handlebars/utils","./htmlbars-util/namespaces","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; + var SafeString = __dependency1__["default"]; + var escapeExpression = __dependency2__.escapeExpression; + var getAttrNamespace = __dependency3__.getAttrNamespace; + + __exports__.SafeString = SafeString; + __exports__.escapeExpression = escapeExpression; + __exports__.getAttrNamespace = getAttrNamespace; + }); +enifed("htmlbars-util/array-utils", + ["exports"], + function(__exports__) { + "use strict"; + function forEach(array, callback, binding) { + var i, l; + if (binding === undefined) { + for (i = 0, l = array.length; i < l; i++) { + callback(array[i], i, array); + } + } else { + for (i = 0, l = array.length; i < l; i++) { + callback.call(binding, array[i], i, array); + } + } + } + + __exports__.forEach = forEach;function map(array, callback) { + var output = []; + var i, l; + + for (i = 0, l = array.length; i < l; i++) { + output.push(callback(array[i], i, array)); + } + + return output; + } + + __exports__.map = map;var getIdx; + if (Array.prototype.indexOf) { + getIdx = function(array, obj, from){ + return array.indexOf(obj, from); + }; + } else { + getIdx = function(array, obj, from) { + if (from === undefined || from === null) { + from = 0; + } else if (from < 0) { + from = Math.max(0, array.length + from); + } + for (var i = from, l= array.length; i < l; i++) { + if (array[i] === obj) { + return i; + } + } + return -1; + }; + } + + var indexOfArray = getIdx; + __exports__.indexOfArray = indexOfArray; + }); +enifed("htmlbars-util/handlebars/safe-string", + ["exports"], + function(__exports__) { + "use strict"; + // Build out our basic SafeString type + function SafeString(string) { + this.string = string; + } + + SafeString.prototype.toString = SafeString.prototype.toHTML = function() { + return "" + this.string; + }; + + __exports__["default"] = SafeString; + }); +enifed("htmlbars-util/handlebars/utils", + ["./safe-string","exports"], + function(__dependency1__, __exports__) { + "use strict"; + /*jshint -W004 */ + var SafeString = __dependency1__["default"]; + + var escape = { + "&": "&", + "<": "<", + ">": ">", + '"': """, + "'": "'", + "`": "`" + }; + + var badChars = /[&<>"'`]/g; + var possible = /[&<>"'`]/; + + function escapeChar(chr) { + return escape[chr]; + } + + function extend(obj /* , ...source */) { + for (var i = 1; i < arguments.length; i++) { + for (var key in arguments[i]) { + if (Object.prototype.hasOwnProperty.call(arguments[i], key)) { + obj[key] = arguments[i][key]; + } + } + } + + return obj; + } + + __exports__.extend = extend;var toString = Object.prototype.toString; + __exports__.toString = toString; + // Sourced from lodash + // https://github.com/bestiejs/lodash/blob/master/LICENSE.txt + var isFunction = function(value) { + return typeof value === 'function'; + }; + // fallback for older versions of Chrome and Safari + /* istanbul ignore next */ + if (isFunction(/x/)) { + isFunction = function(value) { + return typeof value === 'function' && toString.call(value) === '[object Function]'; + }; + } + var isFunction; + __exports__.isFunction = isFunction; + /* istanbul ignore next */ + var isArray = Array.isArray || function(value) { + return (value && typeof value === 'object') ? toString.call(value) === '[object Array]' : false; + }; + __exports__.isArray = isArray; + + function escapeExpression(string) { + // don't escape SafeStrings, since they're already safe + if (string && string.toHTML) { + return string.toHTML(); + } else if (string == null) { + return ""; + } else if (!string) { + return string + ''; + } + + // Force a string conversion as this will be done by the append regardless and + // the regex test will do this transparently behind the scenes, causing issues if + // an object's to string has escaped characters in it. + string = "" + string; + + if(!possible.test(string)) { return string; } + return string.replace(badChars, escapeChar); + } + + __exports__.escapeExpression = escapeExpression;function isEmpty(value) { + if (!value && value !== 0) { + return true; + } else if (isArray(value) && value.length === 0) { + return true; + } else { + return false; + } + } + + __exports__.isEmpty = isEmpty;function appendContextPath(contextPath, id) { + return (contextPath ? contextPath + '.' : '') + id; + } + + __exports__.appendContextPath = appendContextPath; + }); +enifed("htmlbars-util/namespaces", + ["exports"], + function(__exports__) { + "use strict"; + // ref http://dev.w3.org/html5/spec-LC/namespaces.html + var defaultNamespaces = { + html: 'http://www.w3.org/1999/xhtml', + mathml: 'http://www.w3.org/1998/Math/MathML', + svg: 'http://www.w3.org/2000/svg', + xlink: 'http://www.w3.org/1999/xlink', + xml: 'http://www.w3.org/XML/1998/namespace' + }; + + function getAttrNamespace(attrName) { + var namespace; + + var colonIndex = attrName.indexOf(':'); + if (colonIndex !== -1) { + var prefix = attrName.slice(0, colonIndex); + namespace = defaultNamespaces[prefix]; + } + + return namespace || null; + } + + __exports__.getAttrNamespace = getAttrNamespace; + }); +enifed("htmlbars-util/object-utils", + ["exports"], + function(__exports__) { + "use strict"; + function merge(options, defaults) { + for (var prop in defaults) { + if (options.hasOwnProperty(prop)) { continue; } + options[prop] = defaults[prop]; + } + return options; + } + + __exports__.merge = merge; + }); +enifed("htmlbars-util/quoting", + ["exports"], + function(__exports__) { + "use strict"; + function escapeString(str) { + str = str.replace(/\\/g, "\\\\"); + str = str.replace(/"/g, '\\"'); + str = str.replace(/\n/g, "\\n"); + return str; + } + + __exports__.escapeString = escapeString; + + function string(str) { + return '"' + escapeString(str) + '"'; + } + + __exports__.string = string; + + function array(a) { + return "[" + a + "]"; + } + + __exports__.array = array; + + function hash(pairs) { + return "{" + pairs.join(", ") + "}"; + } + + __exports__.hash = hash;function repeat(chars, times) { + var str = ""; + while (times--) { + str += chars; + } + return str; + } + + __exports__.repeat = repeat; + }); +enifed("htmlbars-util/safe-string", + ["./handlebars/safe-string","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var SafeString = __dependency1__["default"]; + + __exports__["default"] = SafeString; + }); +enifed("simple-html-tokenizer", + ["./simple-html-tokenizer/tokenizer","./simple-html-tokenizer/tokenize","./simple-html-tokenizer/generator","./simple-html-tokenizer/generate","./simple-html-tokenizer/tokens","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { + "use strict"; + /*jshint boss:true*/ + var Tokenizer = __dependency1__["default"]; + var tokenize = __dependency2__["default"]; + var Generator = __dependency3__["default"]; + var generate = __dependency4__["default"]; + var StartTag = __dependency5__.StartTag; + var EndTag = __dependency5__.EndTag; + var Chars = __dependency5__.Chars; + var Comment = __dependency5__.Comment; + + __exports__.Tokenizer = Tokenizer; + __exports__.tokenize = tokenize; + __exports__.Generator = Generator; + __exports__.generate = generate; + __exports__.StartTag = StartTag; + __exports__.EndTag = EndTag; + __exports__.Chars = Chars; + __exports__.Comment = Comment; + }); +enifed("simple-html-tokenizer/char-refs/full", + ["exports"], + function(__exports__) { + "use strict"; + __exports__["default"] = { + AElig: [198], + AMP: [38], + Aacute: [193], + Abreve: [258], + Acirc: [194], + Acy: [1040], + Afr: [120068], + Agrave: [192], + Alpha: [913], + Amacr: [256], + And: [10835], + Aogon: [260], + Aopf: [120120], + ApplyFunction: [8289], + Aring: [197], + Ascr: [119964], + Assign: [8788], + Atilde: [195], + Auml: [196], + Backslash: [8726], + Barv: [10983], + Barwed: [8966], + Bcy: [1041], + Because: [8757], + Bernoullis: [8492], + Beta: [914], + Bfr: [120069], + Bopf: [120121], + Breve: [728], + Bscr: [8492], + Bumpeq: [8782], + CHcy: [1063], + COPY: [169], + Cacute: [262], + Cap: [8914], + CapitalDifferentialD: [8517], + Cayleys: [8493], + Ccaron: [268], + Ccedil: [199], + Ccirc: [264], + Cconint: [8752], + Cdot: [266], + Cedilla: [184], + CenterDot: [183], + Cfr: [8493], + Chi: [935], + CircleDot: [8857], + CircleMinus: [8854], + CirclePlus: [8853], + CircleTimes: [8855], + ClockwiseContourIntegral: [8754], + CloseCurlyDoubleQuote: [8221], + CloseCurlyQuote: [8217], + Colon: [8759], + Colone: [10868], + Congruent: [8801], + Conint: [8751], + ContourIntegral: [8750], + Copf: [8450], + Coproduct: [8720], + CounterClockwiseContourIntegral: [8755], + Cross: [10799], + Cscr: [119966], + Cup: [8915], + CupCap: [8781], + DD: [8517], + DDotrahd: [10513], + DJcy: [1026], + DScy: [1029], + DZcy: [1039], + Dagger: [8225], + Darr: [8609], + Dashv: [10980], + Dcaron: [270], + Dcy: [1044], + Del: [8711], + Delta: [916], + Dfr: [120071], + DiacriticalAcute: [180], + DiacriticalDot: [729], + DiacriticalDoubleAcute: [733], + DiacriticalGrave: [96], + DiacriticalTilde: [732], + Diamond: [8900], + DifferentialD: [8518], + Dopf: [120123], + Dot: [168], + DotDot: [8412], + DotEqual: [8784], + DoubleContourIntegral: [8751], + DoubleDot: [168], + DoubleDownArrow: [8659], + DoubleLeftArrow: [8656], + DoubleLeftRightArrow: [8660], + DoubleLeftTee: [10980], + DoubleLongLeftArrow: [10232], + DoubleLongLeftRightArrow: [10234], + DoubleLongRightArrow: [10233], + DoubleRightArrow: [8658], + DoubleRightTee: [8872], + DoubleUpArrow: [8657], + DoubleUpDownArrow: [8661], + DoubleVerticalBar: [8741], + DownArrow: [8595], + DownArrowBar: [10515], + DownArrowUpArrow: [8693], + DownBreve: [785], + DownLeftRightVector: [10576], + DownLeftTeeVector: [10590], + DownLeftVector: [8637], + DownLeftVectorBar: [10582], + DownRightTeeVector: [10591], + DownRightVector: [8641], + DownRightVectorBar: [10583], + DownTee: [8868], + DownTeeArrow: [8615], + Downarrow: [8659], + Dscr: [119967], + Dstrok: [272], + ENG: [330], + ETH: [208], + Eacute: [201], + Ecaron: [282], + Ecirc: [202], + Ecy: [1069], + Edot: [278], + Efr: [120072], + Egrave: [200], + Element: [8712], + Emacr: [274], + EmptySmallSquare: [9723], + EmptyVerySmallSquare: [9643], + Eogon: [280], + Eopf: [120124], + Epsilon: [917], + Equal: [10869], + EqualTilde: [8770], + Equilibrium: [8652], + Escr: [8496], + Esim: [10867], + Eta: [919], + Euml: [203], + Exists: [8707], + ExponentialE: [8519], + Fcy: [1060], + Ffr: [120073], + FilledSmallSquare: [9724], + FilledVerySmallSquare: [9642], + Fopf: [120125], + ForAll: [8704], + Fouriertrf: [8497], + Fscr: [8497], + GJcy: [1027], + GT: [62], + Gamma: [915], + Gammad: [988], + Gbreve: [286], + Gcedil: [290], + Gcirc: [284], + Gcy: [1043], + Gdot: [288], + Gfr: [120074], + Gg: [8921], + Gopf: [120126], + GreaterEqual: [8805], + GreaterEqualLess: [8923], + GreaterFullEqual: [8807], + GreaterGreater: [10914], + GreaterLess: [8823], + GreaterSlantEqual: [10878], + GreaterTilde: [8819], + Gscr: [119970], + Gt: [8811], + HARDcy: [1066], + Hacek: [711], + Hat: [94], + Hcirc: [292], + Hfr: [8460], + HilbertSpace: [8459], + Hopf: [8461], + HorizontalLine: [9472], + Hscr: [8459], + Hstrok: [294], + HumpDownHump: [8782], + HumpEqual: [8783], + IEcy: [1045], + IJlig: [306], + IOcy: [1025], + Iacute: [205], + Icirc: [206], + Icy: [1048], + Idot: [304], + Ifr: [8465], + Igrave: [204], + Im: [8465], + Imacr: [298], + ImaginaryI: [8520], + Implies: [8658], + Int: [8748], + Integral: [8747], + Intersection: [8898], + InvisibleComma: [8291], + InvisibleTimes: [8290], + Iogon: [302], + Iopf: [120128], + Iota: [921], + Iscr: [8464], + Itilde: [296], + Iukcy: [1030], + Iuml: [207], + Jcirc: [308], + Jcy: [1049], + Jfr: [120077], + Jopf: [120129], + Jscr: [119973], + Jsercy: [1032], + Jukcy: [1028], + KHcy: [1061], + KJcy: [1036], + Kappa: [922], + Kcedil: [310], + Kcy: [1050], + Kfr: [120078], + Kopf: [120130], + Kscr: [119974], + LJcy: [1033], + LT: [60], + Lacute: [313], + Lambda: [923], + Lang: [10218], + Laplacetrf: [8466], + Larr: [8606], + Lcaron: [317], + Lcedil: [315], + Lcy: [1051], + LeftAngleBracket: [10216], + LeftArrow: [8592], + LeftArrowBar: [8676], + LeftArrowRightArrow: [8646], + LeftCeiling: [8968], + LeftDoubleBracket: [10214], + LeftDownTeeVector: [10593], + LeftDownVector: [8643], + LeftDownVectorBar: [10585], + LeftFloor: [8970], + LeftRightArrow: [8596], + LeftRightVector: [10574], + LeftTee: [8867], + LeftTeeArrow: [8612], + LeftTeeVector: [10586], + LeftTriangle: [8882], + LeftTriangleBar: [10703], + LeftTriangleEqual: [8884], + LeftUpDownVector: [10577], + LeftUpTeeVector: [10592], + LeftUpVector: [8639], + LeftUpVectorBar: [10584], + LeftVector: [8636], + LeftVectorBar: [10578], + Leftarrow: [8656], + Leftrightarrow: [8660], + LessEqualGreater: [8922], + LessFullEqual: [8806], + LessGreater: [8822], + LessLess: [10913], + LessSlantEqual: [10877], + LessTilde: [8818], + Lfr: [120079], + Ll: [8920], + Lleftarrow: [8666], + Lmidot: [319], + LongLeftArrow: [10229], + LongLeftRightArrow: [10231], + LongRightArrow: [10230], + Longleftarrow: [10232], + Longleftrightarrow: [10234], + Longrightarrow: [10233], + Lopf: [120131], + LowerLeftArrow: [8601], + LowerRightArrow: [8600], + Lscr: [8466], + Lsh: [8624], + Lstrok: [321], + Lt: [8810], + Map: [10501], + Mcy: [1052], + MediumSpace: [8287], + Mellintrf: [8499], + Mfr: [120080], + MinusPlus: [8723], + Mopf: [120132], + Mscr: [8499], + Mu: [924], + NJcy: [1034], + Nacute: [323], + Ncaron: [327], + Ncedil: [325], + Ncy: [1053], + NegativeMediumSpace: [8203], + NegativeThickSpace: [8203], + NegativeThinSpace: [8203], + NegativeVeryThinSpace: [8203], + NestedGreaterGreater: [8811], + NestedLessLess: [8810], + NewLine: [10], + Nfr: [120081], + NoBreak: [8288], + NonBreakingSpace: [160], + Nopf: [8469], + Not: [10988], + NotCongruent: [8802], + NotCupCap: [8813], + NotDoubleVerticalBar: [8742], + NotElement: [8713], + NotEqual: [8800], + NotEqualTilde: [8770, 824], + NotExists: [8708], + NotGreater: [8815], + NotGreaterEqual: [8817], + NotGreaterFullEqual: [8807, 824], + NotGreaterGreater: [8811, 824], + NotGreaterLess: [8825], + NotGreaterSlantEqual: [10878, 824], + NotGreaterTilde: [8821], + NotHumpDownHump: [8782, 824], + NotHumpEqual: [8783, 824], + NotLeftTriangle: [8938], + NotLeftTriangleBar: [10703, 824], + NotLeftTriangleEqual: [8940], + NotLess: [8814], + NotLessEqual: [8816], + NotLessGreater: [8824], + NotLessLess: [8810, 824], + NotLessSlantEqual: [10877, 824], + NotLessTilde: [8820], + NotNestedGreaterGreater: [10914, 824], + NotNestedLessLess: [10913, 824], + NotPrecedes: [8832], + NotPrecedesEqual: [10927, 824], + NotPrecedesSlantEqual: [8928], + NotReverseElement: [8716], + NotRightTriangle: [8939], + NotRightTriangleBar: [10704, 824], + NotRightTriangleEqual: [8941], + NotSquareSubset: [8847, 824], + NotSquareSubsetEqual: [8930], + NotSquareSuperset: [8848, 824], + NotSquareSupersetEqual: [8931], + NotSubset: [8834, 8402], + NotSubsetEqual: [8840], + NotSucceeds: [8833], + NotSucceedsEqual: [10928, 824], + NotSucceedsSlantEqual: [8929], + NotSucceedsTilde: [8831, 824], + NotSuperset: [8835, 8402], + NotSupersetEqual: [8841], + NotTilde: [8769], + NotTildeEqual: [8772], + NotTildeFullEqual: [8775], + NotTildeTilde: [8777], + NotVerticalBar: [8740], + Nscr: [119977], + Ntilde: [209], + Nu: [925], + OElig: [338], + Oacute: [211], + Ocirc: [212], + Ocy: [1054], + Odblac: [336], + Ofr: [120082], + Ograve: [210], + Omacr: [332], + Omega: [937], + Omicron: [927], + Oopf: [120134], + OpenCurlyDoubleQuote: [8220], + OpenCurlyQuote: [8216], + Or: [10836], + Oscr: [119978], + Oslash: [216], + Otilde: [213], + Otimes: [10807], + Ouml: [214], + OverBar: [8254], + OverBrace: [9182], + OverBracket: [9140], + OverParenthesis: [9180], + PartialD: [8706], + Pcy: [1055], + Pfr: [120083], + Phi: [934], + Pi: [928], + PlusMinus: [177], + Poincareplane: [8460], + Popf: [8473], + Pr: [10939], + Precedes: [8826], + PrecedesEqual: [10927], + PrecedesSlantEqual: [8828], + PrecedesTilde: [8830], + Prime: [8243], + Product: [8719], + Proportion: [8759], + Proportional: [8733], + Pscr: [119979], + Psi: [936], + QUOT: [34], + Qfr: [120084], + Qopf: [8474], + Qscr: [119980], + RBarr: [10512], + REG: [174], + Racute: [340], + Rang: [10219], + Rarr: [8608], + Rarrtl: [10518], + Rcaron: [344], + Rcedil: [342], + Rcy: [1056], + Re: [8476], + ReverseElement: [8715], + ReverseEquilibrium: [8651], + ReverseUpEquilibrium: [10607], + Rfr: [8476], + Rho: [929], + RightAngleBracket: [10217], + RightArrow: [8594], + RightArrowBar: [8677], + RightArrowLeftArrow: [8644], + RightCeiling: [8969], + RightDoubleBracket: [10215], + RightDownTeeVector: [10589], + RightDownVector: [8642], + RightDownVectorBar: [10581], + RightFloor: [8971], + RightTee: [8866], + RightTeeArrow: [8614], + RightTeeVector: [10587], + RightTriangle: [8883], + RightTriangleBar: [10704], + RightTriangleEqual: [8885], + RightUpDownVector: [10575], + RightUpTeeVector: [10588], + RightUpVector: [8638], + RightUpVectorBar: [10580], + RightVector: [8640], + RightVectorBar: [10579], + Rightarrow: [8658], + Ropf: [8477], + RoundImplies: [10608], + Rrightarrow: [8667], + Rscr: [8475], + Rsh: [8625], + RuleDelayed: [10740], + SHCHcy: [1065], + SHcy: [1064], + SOFTcy: [1068], + Sacute: [346], + Sc: [10940], + Scaron: [352], + Scedil: [350], + Scirc: [348], + Scy: [1057], + Sfr: [120086], + ShortDownArrow: [8595], + ShortLeftArrow: [8592], + ShortRightArrow: [8594], + ShortUpArrow: [8593], + Sigma: [931], + SmallCircle: [8728], + Sopf: [120138], + Sqrt: [8730], + Square: [9633], + SquareIntersection: [8851], + SquareSubset: [8847], + SquareSubsetEqual: [8849], + SquareSuperset: [8848], + SquareSupersetEqual: [8850], + SquareUnion: [8852], + Sscr: [119982], + Star: [8902], + Sub: [8912], + Subset: [8912], + SubsetEqual: [8838], + Succeeds: [8827], + SucceedsEqual: [10928], + SucceedsSlantEqual: [8829], + SucceedsTilde: [8831], + SuchThat: [8715], + Sum: [8721], + Sup: [8913], + Superset: [8835], + SupersetEqual: [8839], + Supset: [8913], + THORN: [222], + TRADE: [8482], + TSHcy: [1035], + TScy: [1062], + Tab: [9], + Tau: [932], + Tcaron: [356], + Tcedil: [354], + Tcy: [1058], + Tfr: [120087], + Therefore: [8756], + Theta: [920], + ThickSpace: [8287, 8202], + ThinSpace: [8201], + Tilde: [8764], + TildeEqual: [8771], + TildeFullEqual: [8773], + TildeTilde: [8776], + Topf: [120139], + TripleDot: [8411], + Tscr: [119983], + Tstrok: [358], + Uacute: [218], + Uarr: [8607], + Uarrocir: [10569], + Ubrcy: [1038], + Ubreve: [364], + Ucirc: [219], + Ucy: [1059], + Udblac: [368], + Ufr: [120088], + Ugrave: [217], + Umacr: [362], + UnderBar: [95], + UnderBrace: [9183], + UnderBracket: [9141], + UnderParenthesis: [9181], + Union: [8899], + UnionPlus: [8846], + Uogon: [370], + Uopf: [120140], + UpArrow: [8593], + UpArrowBar: [10514], + UpArrowDownArrow: [8645], + UpDownArrow: [8597], + UpEquilibrium: [10606], + UpTee: [8869], + UpTeeArrow: [8613], + Uparrow: [8657], + Updownarrow: [8661], + UpperLeftArrow: [8598], + UpperRightArrow: [8599], + Upsi: [978], + Upsilon: [933], + Uring: [366], + Uscr: [119984], + Utilde: [360], + Uuml: [220], + VDash: [8875], + Vbar: [10987], + Vcy: [1042], + Vdash: [8873], + Vdashl: [10982], + Vee: [8897], + Verbar: [8214], + Vert: [8214], + VerticalBar: [8739], + VerticalLine: [124], + VerticalSeparator: [10072], + VerticalTilde: [8768], + VeryThinSpace: [8202], + Vfr: [120089], + Vopf: [120141], + Vscr: [119985], + Vvdash: [8874], + Wcirc: [372], + Wedge: [8896], + Wfr: [120090], + Wopf: [120142], + Wscr: [119986], + Xfr: [120091], + Xi: [926], + Xopf: [120143], + Xscr: [119987], + YAcy: [1071], + YIcy: [1031], + YUcy: [1070], + Yacute: [221], + Ycirc: [374], + Ycy: [1067], + Yfr: [120092], + Yopf: [120144], + Yscr: [119988], + Yuml: [376], + ZHcy: [1046], + Zacute: [377], + Zcaron: [381], + Zcy: [1047], + Zdot: [379], + ZeroWidthSpace: [8203], + Zeta: [918], + Zfr: [8488], + Zopf: [8484], + Zscr: [119989], + aacute: [225], + abreve: [259], + ac: [8766], + acE: [8766, 819], + acd: [8767], + acirc: [226], + acute: [180], + acy: [1072], + aelig: [230], + af: [8289], + afr: [120094], + agrave: [224], + alefsym: [8501], + aleph: [8501], + alpha: [945], + amacr: [257], + amalg: [10815], + amp: [38], + and: [8743], + andand: [10837], + andd: [10844], + andslope: [10840], + andv: [10842], + ang: [8736], + ange: [10660], + angle: [8736], + angmsd: [8737], + angmsdaa: [10664], + angmsdab: [10665], + angmsdac: [10666], + angmsdad: [10667], + angmsdae: [10668], + angmsdaf: [10669], + angmsdag: [10670], + angmsdah: [10671], + angrt: [8735], + angrtvb: [8894], + angrtvbd: [10653], + angsph: [8738], + angst: [197], + angzarr: [9084], + aogon: [261], + aopf: [120146], + ap: [8776], + apE: [10864], + apacir: [10863], + ape: [8778], + apid: [8779], + apos: [39], + approx: [8776], + approxeq: [8778], + aring: [229], + ascr: [119990], + ast: [42], + asymp: [8776], + asympeq: [8781], + atilde: [227], + auml: [228], + awconint: [8755], + awint: [10769], + bNot: [10989], + backcong: [8780], + backepsilon: [1014], + backprime: [8245], + backsim: [8765], + backsimeq: [8909], + barvee: [8893], + barwed: [8965], + barwedge: [8965], + bbrk: [9141], + bbrktbrk: [9142], + bcong: [8780], + bcy: [1073], + bdquo: [8222], + becaus: [8757], + because: [8757], + bemptyv: [10672], + bepsi: [1014], + bernou: [8492], + beta: [946], + beth: [8502], + between: [8812], + bfr: [120095], + bigcap: [8898], + bigcirc: [9711], + bigcup: [8899], + bigodot: [10752], + bigoplus: [10753], + bigotimes: [10754], + bigsqcup: [10758], + bigstar: [9733], + bigtriangledown: [9661], + bigtriangleup: [9651], + biguplus: [10756], + bigvee: [8897], + bigwedge: [8896], + bkarow: [10509], + blacklozenge: [10731], + blacksquare: [9642], + blacktriangle: [9652], + blacktriangledown: [9662], + blacktriangleleft: [9666], + blacktriangleright: [9656], + blank: [9251], + blk12: [9618], + blk14: [9617], + blk34: [9619], + block: [9608], + bne: [61, 8421], + bnequiv: [8801, 8421], + bnot: [8976], + bopf: [120147], + bot: [8869], + bottom: [8869], + bowtie: [8904], + boxDL: [9559], + boxDR: [9556], + boxDl: [9558], + boxDr: [9555], + boxH: [9552], + boxHD: [9574], + boxHU: [9577], + boxHd: [9572], + boxHu: [9575], + boxUL: [9565], + boxUR: [9562], + boxUl: [9564], + boxUr: [9561], + boxV: [9553], + boxVH: [9580], + boxVL: [9571], + boxVR: [9568], + boxVh: [9579], + boxVl: [9570], + boxVr: [9567], + boxbox: [10697], + boxdL: [9557], + boxdR: [9554], + boxdl: [9488], + boxdr: [9484], + boxh: [9472], + boxhD: [9573], + boxhU: [9576], + boxhd: [9516], + boxhu: [9524], + boxminus: [8863], + boxplus: [8862], + boxtimes: [8864], + boxuL: [9563], + boxuR: [9560], + boxul: [9496], + boxur: [9492], + boxv: [9474], + boxvH: [9578], + boxvL: [9569], + boxvR: [9566], + boxvh: [9532], + boxvl: [9508], + boxvr: [9500], + bprime: [8245], + breve: [728], + brvbar: [166], + bscr: [119991], + bsemi: [8271], + bsim: [8765], + bsime: [8909], + bsol: [92], + bsolb: [10693], + bsolhsub: [10184], + bull: [8226], + bullet: [8226], + bump: [8782], + bumpE: [10926], + bumpe: [8783], + bumpeq: [8783], + cacute: [263], + cap: [8745], + capand: [10820], + capbrcup: [10825], + capcap: [10827], + capcup: [10823], + capdot: [10816], + caps: [8745, 65024], + caret: [8257], + caron: [711], + ccaps: [10829], + ccaron: [269], + ccedil: [231], + ccirc: [265], + ccups: [10828], + ccupssm: [10832], + cdot: [267], + cedil: [184], + cemptyv: [10674], + cent: [162], + centerdot: [183], + cfr: [120096], + chcy: [1095], + check: [10003], + checkmark: [10003], + chi: [967], + cir: [9675], + cirE: [10691], + circ: [710], + circeq: [8791], + circlearrowleft: [8634], + circlearrowright: [8635], + circledR: [174], + circledS: [9416], + circledast: [8859], + circledcirc: [8858], + circleddash: [8861], + cire: [8791], + cirfnint: [10768], + cirmid: [10991], + cirscir: [10690], + clubs: [9827], + clubsuit: [9827], + colon: [58], + colone: [8788], + coloneq: [8788], + comma: [44], + commat: [64], + comp: [8705], + compfn: [8728], + complement: [8705], + complexes: [8450], + cong: [8773], + congdot: [10861], + conint: [8750], + copf: [120148], + coprod: [8720], + copy: [169], + copysr: [8471], + crarr: [8629], + cross: [10007], + cscr: [119992], + csub: [10959], + csube: [10961], + csup: [10960], + csupe: [10962], + ctdot: [8943], + cudarrl: [10552], + cudarrr: [10549], + cuepr: [8926], + cuesc: [8927], + cularr: [8630], + cularrp: [10557], + cup: [8746], + cupbrcap: [10824], + cupcap: [10822], + cupcup: [10826], + cupdot: [8845], + cupor: [10821], + cups: [8746, 65024], + curarr: [8631], + curarrm: [10556], + curlyeqprec: [8926], + curlyeqsucc: [8927], + curlyvee: [8910], + curlywedge: [8911], + curren: [164], + curvearrowleft: [8630], + curvearrowright: [8631], + cuvee: [8910], + cuwed: [8911], + cwconint: [8754], + cwint: [8753], + cylcty: [9005], + dArr: [8659], + dHar: [10597], + dagger: [8224], + daleth: [8504], + darr: [8595], + dash: [8208], + dashv: [8867], + dbkarow: [10511], + dblac: [733], + dcaron: [271], + dcy: [1076], + dd: [8518], + ddagger: [8225], + ddarr: [8650], + ddotseq: [10871], + deg: [176], + delta: [948], + demptyv: [10673], + dfisht: [10623], + dfr: [120097], + dharl: [8643], + dharr: [8642], + diam: [8900], + diamond: [8900], + diamondsuit: [9830], + diams: [9830], + die: [168], + digamma: [989], + disin: [8946], + div: [247], + divide: [247], + divideontimes: [8903], + divonx: [8903], + djcy: [1106], + dlcorn: [8990], + dlcrop: [8973], + dollar: [36], + dopf: [120149], + dot: [729], + doteq: [8784], + doteqdot: [8785], + dotminus: [8760], + dotplus: [8724], + dotsquare: [8865], + doublebarwedge: [8966], + downarrow: [8595], + downdownarrows: [8650], + downharpoonleft: [8643], + downharpoonright: [8642], + drbkarow: [10512], + drcorn: [8991], + drcrop: [8972], + dscr: [119993], + dscy: [1109], + dsol: [10742], + dstrok: [273], + dtdot: [8945], + dtri: [9663], + dtrif: [9662], + duarr: [8693], + duhar: [10607], + dwangle: [10662], + dzcy: [1119], + dzigrarr: [10239], + eDDot: [10871], + eDot: [8785], + eacute: [233], + easter: [10862], + ecaron: [283], + ecir: [8790], + ecirc: [234], + ecolon: [8789], + ecy: [1101], + edot: [279], + ee: [8519], + efDot: [8786], + efr: [120098], + eg: [10906], + egrave: [232], + egs: [10902], + egsdot: [10904], + el: [10905], + elinters: [9191], + ell: [8467], + els: [10901], + elsdot: [10903], + emacr: [275], + empty: [8709], + emptyset: [8709], + emptyv: [8709], + emsp: [8195], + emsp13: [8196], + emsp14: [8197], + eng: [331], + ensp: [8194], + eogon: [281], + eopf: [120150], + epar: [8917], + eparsl: [10723], + eplus: [10865], + epsi: [949], + epsilon: [949], + epsiv: [1013], + eqcirc: [8790], + eqcolon: [8789], + eqsim: [8770], + eqslantgtr: [10902], + eqslantless: [10901], + equals: [61], + equest: [8799], + equiv: [8801], + equivDD: [10872], + eqvparsl: [10725], + erDot: [8787], + erarr: [10609], + escr: [8495], + esdot: [8784], + esim: [8770], + eta: [951], + eth: [240], + euml: [235], + euro: [8364], + excl: [33], + exist: [8707], + expectation: [8496], + exponentiale: [8519], + fallingdotseq: [8786], + fcy: [1092], + female: [9792], + ffilig: [64259], + fflig: [64256], + ffllig: [64260], + ffr: [120099], + filig: [64257], + fjlig: [102, 106], + flat: [9837], + fllig: [64258], + fltns: [9649], + fnof: [402], + fopf: [120151], + forall: [8704], + fork: [8916], + forkv: [10969], + fpartint: [10765], + frac12: [189], + frac13: [8531], + frac14: [188], + frac15: [8533], + frac16: [8537], + frac18: [8539], + frac23: [8532], + frac25: [8534], + frac34: [190], + frac35: [8535], + frac38: [8540], + frac45: [8536], + frac56: [8538], + frac58: [8541], + frac78: [8542], + frasl: [8260], + frown: [8994], + fscr: [119995], + gE: [8807], + gEl: [10892], + gacute: [501], + gamma: [947], + gammad: [989], + gap: [10886], + gbreve: [287], + gcirc: [285], + gcy: [1075], + gdot: [289], + ge: [8805], + gel: [8923], + geq: [8805], + geqq: [8807], + geqslant: [10878], + ges: [10878], + gescc: [10921], + gesdot: [10880], + gesdoto: [10882], + gesdotol: [10884], + gesl: [8923, 65024], + gesles: [10900], + gfr: [120100], + gg: [8811], + ggg: [8921], + gimel: [8503], + gjcy: [1107], + gl: [8823], + glE: [10898], + gla: [10917], + glj: [10916], + gnE: [8809], + gnap: [10890], + gnapprox: [10890], + gne: [10888], + gneq: [10888], + gneqq: [8809], + gnsim: [8935], + gopf: [120152], + grave: [96], + gscr: [8458], + gsim: [8819], + gsime: [10894], + gsiml: [10896], + gt: [62], + gtcc: [10919], + gtcir: [10874], + gtdot: [8919], + gtlPar: [10645], + gtquest: [10876], + gtrapprox: [10886], + gtrarr: [10616], + gtrdot: [8919], + gtreqless: [8923], + gtreqqless: [10892], + gtrless: [8823], + gtrsim: [8819], + gvertneqq: [8809, 65024], + gvnE: [8809, 65024], + hArr: [8660], + hairsp: [8202], + half: [189], + hamilt: [8459], + hardcy: [1098], + harr: [8596], + harrcir: [10568], + harrw: [8621], + hbar: [8463], + hcirc: [293], + hearts: [9829], + heartsuit: [9829], + hellip: [8230], + hercon: [8889], + hfr: [120101], + hksearow: [10533], + hkswarow: [10534], + hoarr: [8703], + homtht: [8763], + hookleftarrow: [8617], + hookrightarrow: [8618], + hopf: [120153], + horbar: [8213], + hscr: [119997], + hslash: [8463], + hstrok: [295], + hybull: [8259], + hyphen: [8208], + iacute: [237], + ic: [8291], + icirc: [238], + icy: [1080], + iecy: [1077], + iexcl: [161], + iff: [8660], + ifr: [120102], + igrave: [236], + ii: [8520], + iiiint: [10764], + iiint: [8749], + iinfin: [10716], + iiota: [8489], + ijlig: [307], + imacr: [299], + image: [8465], + imagline: [8464], + imagpart: [8465], + imath: [305], + imof: [8887], + imped: [437], + "in": [8712], + incare: [8453], + infin: [8734], + infintie: [10717], + inodot: [305], + "int": [8747], + intcal: [8890], + integers: [8484], + intercal: [8890], + intlarhk: [10775], + intprod: [10812], + iocy: [1105], + iogon: [303], + iopf: [120154], + iota: [953], + iprod: [10812], + iquest: [191], + iscr: [119998], + isin: [8712], + isinE: [8953], + isindot: [8949], + isins: [8948], + isinsv: [8947], + isinv: [8712], + it: [8290], + itilde: [297], + iukcy: [1110], + iuml: [239], + jcirc: [309], + jcy: [1081], + jfr: [120103], + jmath: [567], + jopf: [120155], + jscr: [119999], + jsercy: [1112], + jukcy: [1108], + kappa: [954], + kappav: [1008], + kcedil: [311], + kcy: [1082], + kfr: [120104], + kgreen: [312], + khcy: [1093], + kjcy: [1116], + kopf: [120156], + kscr: [120000], + lAarr: [8666], + lArr: [8656], + lAtail: [10523], + lBarr: [10510], + lE: [8806], + lEg: [10891], + lHar: [10594], + lacute: [314], + laemptyv: [10676], + lagran: [8466], + lambda: [955], + lang: [10216], + langd: [10641], + langle: [10216], + lap: [10885], + laquo: [171], + larr: [8592], + larrb: [8676], + larrbfs: [10527], + larrfs: [10525], + larrhk: [8617], + larrlp: [8619], + larrpl: [10553], + larrsim: [10611], + larrtl: [8610], + lat: [10923], + latail: [10521], + late: [10925], + lates: [10925, 65024], + lbarr: [10508], + lbbrk: [10098], + lbrace: [123], + lbrack: [91], + lbrke: [10635], + lbrksld: [10639], + lbrkslu: [10637], + lcaron: [318], + lcedil: [316], + lceil: [8968], + lcub: [123], + lcy: [1083], + ldca: [10550], + ldquo: [8220], + ldquor: [8222], + ldrdhar: [10599], + ldrushar: [10571], + ldsh: [8626], + le: [8804], + leftarrow: [8592], + leftarrowtail: [8610], + leftharpoondown: [8637], + leftharpoonup: [8636], + leftleftarrows: [8647], + leftrightarrow: [8596], + leftrightarrows: [8646], + leftrightharpoons: [8651], + leftrightsquigarrow: [8621], + leftthreetimes: [8907], + leg: [8922], + leq: [8804], + leqq: [8806], + leqslant: [10877], + les: [10877], + lescc: [10920], + lesdot: [10879], + lesdoto: [10881], + lesdotor: [10883], + lesg: [8922, 65024], + lesges: [10899], + lessapprox: [10885], + lessdot: [8918], + lesseqgtr: [8922], + lesseqqgtr: [10891], + lessgtr: [8822], + lesssim: [8818], + lfisht: [10620], + lfloor: [8970], + lfr: [120105], + lg: [8822], + lgE: [10897], + lhard: [8637], + lharu: [8636], + lharul: [10602], + lhblk: [9604], + ljcy: [1113], + ll: [8810], + llarr: [8647], + llcorner: [8990], + llhard: [10603], + lltri: [9722], + lmidot: [320], + lmoust: [9136], + lmoustache: [9136], + lnE: [8808], + lnap: [10889], + lnapprox: [10889], + lne: [10887], + lneq: [10887], + lneqq: [8808], + lnsim: [8934], + loang: [10220], + loarr: [8701], + lobrk: [10214], + longleftarrow: [10229], + longleftrightarrow: [10231], + longmapsto: [10236], + longrightarrow: [10230], + looparrowleft: [8619], + looparrowright: [8620], + lopar: [10629], + lopf: [120157], + loplus: [10797], + lotimes: [10804], + lowast: [8727], + lowbar: [95], + loz: [9674], + lozenge: [9674], + lozf: [10731], + lpar: [40], + lparlt: [10643], + lrarr: [8646], + lrcorner: [8991], + lrhar: [8651], + lrhard: [10605], + lrm: [8206], + lrtri: [8895], + lsaquo: [8249], + lscr: [120001], + lsh: [8624], + lsim: [8818], + lsime: [10893], + lsimg: [10895], + lsqb: [91], + lsquo: [8216], + lsquor: [8218], + lstrok: [322], + lt: [60], + ltcc: [10918], + ltcir: [10873], + ltdot: [8918], + lthree: [8907], + ltimes: [8905], + ltlarr: [10614], + ltquest: [10875], + ltrPar: [10646], + ltri: [9667], + ltrie: [8884], + ltrif: [9666], + lurdshar: [10570], + luruhar: [10598], + lvertneqq: [8808, 65024], + lvnE: [8808, 65024], + mDDot: [8762], + macr: [175], + male: [9794], + malt: [10016], + maltese: [10016], + map: [8614], + mapsto: [8614], + mapstodown: [8615], + mapstoleft: [8612], + mapstoup: [8613], + marker: [9646], + mcomma: [10793], + mcy: [1084], + mdash: [8212], + measuredangle: [8737], + mfr: [120106], + mho: [8487], + micro: [181], + mid: [8739], + midast: [42], + midcir: [10992], + middot: [183], + minus: [8722], + minusb: [8863], + minusd: [8760], + minusdu: [10794], + mlcp: [10971], + mldr: [8230], + mnplus: [8723], + models: [8871], + mopf: [120158], + mp: [8723], + mscr: [120002], + mstpos: [8766], + mu: [956], + multimap: [8888], + mumap: [8888], + nGg: [8921, 824], + nGt: [8811, 8402], + nGtv: [8811, 824], + nLeftarrow: [8653], + nLeftrightarrow: [8654], + nLl: [8920, 824], + nLt: [8810, 8402], + nLtv: [8810, 824], + nRightarrow: [8655], + nVDash: [8879], + nVdash: [8878], + nabla: [8711], + nacute: [324], + nang: [8736, 8402], + nap: [8777], + napE: [10864, 824], + napid: [8779, 824], + napos: [329], + napprox: [8777], + natur: [9838], + natural: [9838], + naturals: [8469], + nbsp: [160], + nbump: [8782, 824], + nbumpe: [8783, 824], + ncap: [10819], + ncaron: [328], + ncedil: [326], + ncong: [8775], + ncongdot: [10861, 824], + ncup: [10818], + ncy: [1085], + ndash: [8211], + ne: [8800], + neArr: [8663], + nearhk: [10532], + nearr: [8599], + nearrow: [8599], + nedot: [8784, 824], + nequiv: [8802], + nesear: [10536], + nesim: [8770, 824], + nexist: [8708], + nexists: [8708], + nfr: [120107], + ngE: [8807, 824], + nge: [8817], + ngeq: [8817], + ngeqq: [8807, 824], + ngeqslant: [10878, 824], + nges: [10878, 824], + ngsim: [8821], + ngt: [8815], + ngtr: [8815], + nhArr: [8654], + nharr: [8622], + nhpar: [10994], + ni: [8715], + nis: [8956], + nisd: [8954], + niv: [8715], + njcy: [1114], + nlArr: [8653], + nlE: [8806, 824], + nlarr: [8602], + nldr: [8229], + nle: [8816], + nleftarrow: [8602], + nleftrightarrow: [8622], + nleq: [8816], + nleqq: [8806, 824], + nleqslant: [10877, 824], + nles: [10877, 824], + nless: [8814], + nlsim: [8820], + nlt: [8814], + nltri: [8938], + nltrie: [8940], + nmid: [8740], + nopf: [120159], + not: [172], + notin: [8713], + notinE: [8953, 824], + notindot: [8949, 824], + notinva: [8713], + notinvb: [8951], + notinvc: [8950], + notni: [8716], + notniva: [8716], + notnivb: [8958], + notnivc: [8957], + npar: [8742], + nparallel: [8742], + nparsl: [11005, 8421], + npart: [8706, 824], + npolint: [10772], + npr: [8832], + nprcue: [8928], + npre: [10927, 824], + nprec: [8832], + npreceq: [10927, 824], + nrArr: [8655], + nrarr: [8603], + nrarrc: [10547, 824], + nrarrw: [8605, 824], + nrightarrow: [8603], + nrtri: [8939], + nrtrie: [8941], + nsc: [8833], + nsccue: [8929], + nsce: [10928, 824], + nscr: [120003], + nshortmid: [8740], + nshortparallel: [8742], + nsim: [8769], + nsime: [8772], + nsimeq: [8772], + nsmid: [8740], + nspar: [8742], + nsqsube: [8930], + nsqsupe: [8931], + nsub: [8836], + nsubE: [10949, 824], + nsube: [8840], + nsubset: [8834, 8402], + nsubseteq: [8840], + nsubseteqq: [10949, 824], + nsucc: [8833], + nsucceq: [10928, 824], + nsup: [8837], + nsupE: [10950, 824], + nsupe: [8841], + nsupset: [8835, 8402], + nsupseteq: [8841], + nsupseteqq: [10950, 824], + ntgl: [8825], + ntilde: [241], + ntlg: [8824], + ntriangleleft: [8938], + ntrianglelefteq: [8940], + ntriangleright: [8939], + ntrianglerighteq: [8941], + nu: [957], + num: [35], + numero: [8470], + numsp: [8199], + nvDash: [8877], + nvHarr: [10500], + nvap: [8781, 8402], + nvdash: [8876], + nvge: [8805, 8402], + nvgt: [62, 8402], + nvinfin: [10718], + nvlArr: [10498], + nvle: [8804, 8402], + nvlt: [60, 8402], + nvltrie: [8884, 8402], + nvrArr: [10499], + nvrtrie: [8885, 8402], + nvsim: [8764, 8402], + nwArr: [8662], + nwarhk: [10531], + nwarr: [8598], + nwarrow: [8598], + nwnear: [10535], + oS: [9416], + oacute: [243], + oast: [8859], + ocir: [8858], + ocirc: [244], + ocy: [1086], + odash: [8861], + odblac: [337], + odiv: [10808], + odot: [8857], + odsold: [10684], + oelig: [339], + ofcir: [10687], + ofr: [120108], + ogon: [731], + ograve: [242], + ogt: [10689], + ohbar: [10677], + ohm: [937], + oint: [8750], + olarr: [8634], + olcir: [10686], + olcross: [10683], + oline: [8254], + olt: [10688], + omacr: [333], + omega: [969], + omicron: [959], + omid: [10678], + ominus: [8854], + oopf: [120160], + opar: [10679], + operp: [10681], + oplus: [8853], + or: [8744], + orarr: [8635], + ord: [10845], + order: [8500], + orderof: [8500], + ordf: [170], + ordm: [186], + origof: [8886], + oror: [10838], + orslope: [10839], + orv: [10843], + oscr: [8500], + oslash: [248], + osol: [8856], + otilde: [245], + otimes: [8855], + otimesas: [10806], + ouml: [246], + ovbar: [9021], + par: [8741], + para: [182], + parallel: [8741], + parsim: [10995], + parsl: [11005], + part: [8706], + pcy: [1087], + percnt: [37], + period: [46], + permil: [8240], + perp: [8869], + pertenk: [8241], + pfr: [120109], + phi: [966], + phiv: [981], + phmmat: [8499], + phone: [9742], + pi: [960], + pitchfork: [8916], + piv: [982], + planck: [8463], + planckh: [8462], + plankv: [8463], + plus: [43], + plusacir: [10787], + plusb: [8862], + pluscir: [10786], + plusdo: [8724], + plusdu: [10789], + pluse: [10866], + plusmn: [177], + plussim: [10790], + plustwo: [10791], + pm: [177], + pointint: [10773], + popf: [120161], + pound: [163], + pr: [8826], + prE: [10931], + prap: [10935], + prcue: [8828], + pre: [10927], + prec: [8826], + precapprox: [10935], + preccurlyeq: [8828], + preceq: [10927], + precnapprox: [10937], + precneqq: [10933], + precnsim: [8936], + precsim: [8830], + prime: [8242], + primes: [8473], + prnE: [10933], + prnap: [10937], + prnsim: [8936], + prod: [8719], + profalar: [9006], + profline: [8978], + profsurf: [8979], + prop: [8733], + propto: [8733], + prsim: [8830], + prurel: [8880], + pscr: [120005], + psi: [968], + puncsp: [8200], + qfr: [120110], + qint: [10764], + qopf: [120162], + qprime: [8279], + qscr: [120006], + quaternions: [8461], + quatint: [10774], + quest: [63], + questeq: [8799], + quot: [34], + rAarr: [8667], + rArr: [8658], + rAtail: [10524], + rBarr: [10511], + rHar: [10596], + race: [8765, 817], + racute: [341], + radic: [8730], + raemptyv: [10675], + rang: [10217], + rangd: [10642], + range: [10661], + rangle: [10217], + raquo: [187], + rarr: [8594], + rarrap: [10613], + rarrb: [8677], + rarrbfs: [10528], + rarrc: [10547], + rarrfs: [10526], + rarrhk: [8618], + rarrlp: [8620], + rarrpl: [10565], + rarrsim: [10612], + rarrtl: [8611], + rarrw: [8605], + ratail: [10522], + ratio: [8758], + rationals: [8474], + rbarr: [10509], + rbbrk: [10099], + rbrace: [125], + rbrack: [93], + rbrke: [10636], + rbrksld: [10638], + rbrkslu: [10640], + rcaron: [345], + rcedil: [343], + rceil: [8969], + rcub: [125], + rcy: [1088], + rdca: [10551], + rdldhar: [10601], + rdquo: [8221], + rdquor: [8221], + rdsh: [8627], + real: [8476], + realine: [8475], + realpart: [8476], + reals: [8477], + rect: [9645], + reg: [174], + rfisht: [10621], + rfloor: [8971], + rfr: [120111], + rhard: [8641], + rharu: [8640], + rharul: [10604], + rho: [961], + rhov: [1009], + rightarrow: [8594], + rightarrowtail: [8611], + rightharpoondown: [8641], + rightharpoonup: [8640], + rightleftarrows: [8644], + rightleftharpoons: [8652], + rightrightarrows: [8649], + rightsquigarrow: [8605], + rightthreetimes: [8908], + ring: [730], + risingdotseq: [8787], + rlarr: [8644], + rlhar: [8652], + rlm: [8207], + rmoust: [9137], + rmoustache: [9137], + rnmid: [10990], + roang: [10221], + roarr: [8702], + robrk: [10215], + ropar: [10630], + ropf: [120163], + roplus: [10798], + rotimes: [10805], + rpar: [41], + rpargt: [10644], + rppolint: [10770], + rrarr: [8649], + rsaquo: [8250], + rscr: [120007], + rsh: [8625], + rsqb: [93], + rsquo: [8217], + rsquor: [8217], + rthree: [8908], + rtimes: [8906], + rtri: [9657], + rtrie: [8885], + rtrif: [9656], + rtriltri: [10702], + ruluhar: [10600], + rx: [8478], + sacute: [347], + sbquo: [8218], + sc: [8827], + scE: [10932], + scap: [10936], + scaron: [353], + sccue: [8829], + sce: [10928], + scedil: [351], + scirc: [349], + scnE: [10934], + scnap: [10938], + scnsim: [8937], + scpolint: [10771], + scsim: [8831], + scy: [1089], + sdot: [8901], + sdotb: [8865], + sdote: [10854], + seArr: [8664], + searhk: [10533], + searr: [8600], + searrow: [8600], + sect: [167], + semi: [59], + seswar: [10537], + setminus: [8726], + setmn: [8726], + sext: [10038], + sfr: [120112], + sfrown: [8994], + sharp: [9839], + shchcy: [1097], + shcy: [1096], + shortmid: [8739], + shortparallel: [8741], + shy: [173], + sigma: [963], + sigmaf: [962], + sigmav: [962], + sim: [8764], + simdot: [10858], + sime: [8771], + simeq: [8771], + simg: [10910], + simgE: [10912], + siml: [10909], + simlE: [10911], + simne: [8774], + simplus: [10788], + simrarr: [10610], + slarr: [8592], + smallsetminus: [8726], + smashp: [10803], + smeparsl: [10724], + smid: [8739], + smile: [8995], + smt: [10922], + smte: [10924], + smtes: [10924, 65024], + softcy: [1100], + sol: [47], + solb: [10692], + solbar: [9023], + sopf: [120164], + spades: [9824], + spadesuit: [9824], + spar: [8741], + sqcap: [8851], + sqcaps: [8851, 65024], + sqcup: [8852], + sqcups: [8852, 65024], + sqsub: [8847], + sqsube: [8849], + sqsubset: [8847], + sqsubseteq: [8849], + sqsup: [8848], + sqsupe: [8850], + sqsupset: [8848], + sqsupseteq: [8850], + squ: [9633], + square: [9633], + squarf: [9642], + squf: [9642], + srarr: [8594], + sscr: [120008], + ssetmn: [8726], + ssmile: [8995], + sstarf: [8902], + star: [9734], + starf: [9733], + straightepsilon: [1013], + straightphi: [981], + strns: [175], + sub: [8834], + subE: [10949], + subdot: [10941], + sube: [8838], + subedot: [10947], + submult: [10945], + subnE: [10955], + subne: [8842], + subplus: [10943], + subrarr: [10617], + subset: [8834], + subseteq: [8838], + subseteqq: [10949], + subsetneq: [8842], + subsetneqq: [10955], + subsim: [10951], + subsub: [10965], + subsup: [10963], + succ: [8827], + succapprox: [10936], + succcurlyeq: [8829], + succeq: [10928], + succnapprox: [10938], + succneqq: [10934], + succnsim: [8937], + succsim: [8831], + sum: [8721], + sung: [9834], + sup: [8835], + sup1: [185], + sup2: [178], + sup3: [179], + supE: [10950], + supdot: [10942], + supdsub: [10968], + supe: [8839], + supedot: [10948], + suphsol: [10185], + suphsub: [10967], + suplarr: [10619], + supmult: [10946], + supnE: [10956], + supne: [8843], + supplus: [10944], + supset: [8835], + supseteq: [8839], + supseteqq: [10950], + supsetneq: [8843], + supsetneqq: [10956], + supsim: [10952], + supsub: [10964], + supsup: [10966], + swArr: [8665], + swarhk: [10534], + swarr: [8601], + swarrow: [8601], + swnwar: [10538], + szlig: [223], + target: [8982], + tau: [964], + tbrk: [9140], + tcaron: [357], + tcedil: [355], + tcy: [1090], + tdot: [8411], + telrec: [8981], + tfr: [120113], + there4: [8756], + therefore: [8756], + theta: [952], + thetasym: [977], + thetav: [977], + thickapprox: [8776], + thicksim: [8764], + thinsp: [8201], + thkap: [8776], + thksim: [8764], + thorn: [254], + tilde: [732], + times: [215], + timesb: [8864], + timesbar: [10801], + timesd: [10800], + tint: [8749], + toea: [10536], + top: [8868], + topbot: [9014], + topcir: [10993], + topf: [120165], + topfork: [10970], + tosa: [10537], + tprime: [8244], + trade: [8482], + triangle: [9653], + triangledown: [9663], + triangleleft: [9667], + trianglelefteq: [8884], + triangleq: [8796], + triangleright: [9657], + trianglerighteq: [8885], + tridot: [9708], + trie: [8796], + triminus: [10810], + triplus: [10809], + trisb: [10701], + tritime: [10811], + trpezium: [9186], + tscr: [120009], + tscy: [1094], + tshcy: [1115], + tstrok: [359], + twixt: [8812], + twoheadleftarrow: [8606], + twoheadrightarrow: [8608], + uArr: [8657], + uHar: [10595], + uacute: [250], + uarr: [8593], + ubrcy: [1118], + ubreve: [365], + ucirc: [251], + ucy: [1091], + udarr: [8645], + udblac: [369], + udhar: [10606], + ufisht: [10622], + ufr: [120114], + ugrave: [249], + uharl: [8639], + uharr: [8638], + uhblk: [9600], + ulcorn: [8988], + ulcorner: [8988], + ulcrop: [8975], + ultri: [9720], + umacr: [363], + uml: [168], + uogon: [371], + uopf: [120166], + uparrow: [8593], + updownarrow: [8597], + upharpoonleft: [8639], + upharpoonright: [8638], + uplus: [8846], + upsi: [965], + upsih: [978], + upsilon: [965], + upuparrows: [8648], + urcorn: [8989], + urcorner: [8989], + urcrop: [8974], + uring: [367], + urtri: [9721], + uscr: [120010], + utdot: [8944], + utilde: [361], + utri: [9653], + utrif: [9652], + uuarr: [8648], + uuml: [252], + uwangle: [10663], + vArr: [8661], + vBar: [10984], + vBarv: [10985], + vDash: [8872], + vangrt: [10652], + varepsilon: [1013], + varkappa: [1008], + varnothing: [8709], + varphi: [981], + varpi: [982], + varpropto: [8733], + varr: [8597], + varrho: [1009], + varsigma: [962], + varsubsetneq: [8842, 65024], + varsubsetneqq: [10955, 65024], + varsupsetneq: [8843, 65024], + varsupsetneqq: [10956, 65024], + vartheta: [977], + vartriangleleft: [8882], + vartriangleright: [8883], + vcy: [1074], + vdash: [8866], + vee: [8744], + veebar: [8891], + veeeq: [8794], + vellip: [8942], + verbar: [124], + vert: [124], + vfr: [120115], + vltri: [8882], + vnsub: [8834, 8402], + vnsup: [8835, 8402], + vopf: [120167], + vprop: [8733], + vrtri: [8883], + vscr: [120011], + vsubnE: [10955, 65024], + vsubne: [8842, 65024], + vsupnE: [10956, 65024], + vsupne: [8843, 65024], + vzigzag: [10650], + wcirc: [373], + wedbar: [10847], + wedge: [8743], + wedgeq: [8793], + weierp: [8472], + wfr: [120116], + wopf: [120168], + wp: [8472], + wr: [8768], + wreath: [8768], + wscr: [120012], + xcap: [8898], + xcirc: [9711], + xcup: [8899], + xdtri: [9661], + xfr: [120117], + xhArr: [10234], + xharr: [10231], + xi: [958], + xlArr: [10232], + xlarr: [10229], + xmap: [10236], + xnis: [8955], + xodot: [10752], + xopf: [120169], + xoplus: [10753], + xotime: [10754], + xrArr: [10233], + xrarr: [10230], + xscr: [120013], + xsqcup: [10758], + xuplus: [10756], + xutri: [9651], + xvee: [8897], + xwedge: [8896], + yacute: [253], + yacy: [1103], + ycirc: [375], + ycy: [1099], + yen: [165], + yfr: [120118], + yicy: [1111], + yopf: [120170], + yscr: [120014], + yucy: [1102], + yuml: [255], + zacute: [378], + zcaron: [382], + zcy: [1079], + zdot: [380], + zeetrf: [8488], + zeta: [950], + zfr: [120119], + zhcy: [1078], + zigrarr: [8669], + zopf: [120171], + zscr: [120015], + zwj: [8205], + zwnj: [8204] + }; + }); +enifed("simple-html-tokenizer/char-refs/min", + ["exports"], + function(__exports__) { + "use strict"; + __exports__["default"] = { + quot: [34], + amp: [38], + apos: [39], + lt: [60], + gt: [62] + }; + }); +enifed("simple-html-tokenizer/entity-parser", + ["exports"], + function(__exports__) { + "use strict"; + function EntityParser(namedCodepoints) { + this.namedCodepoints = namedCodepoints; + } + + EntityParser.prototype.parse = function (tokenizer) { + var input = tokenizer.input.slice(tokenizer["char"]); + var matches = input.match(/^#(?:x|X)([0-9A-Fa-f]+);/); + if (matches) { + tokenizer["char"] += matches[0].length; + return String.fromCharCode(parseInt(matches[1], 16)); + } + matches = input.match(/^#([0-9]+);/); + if (matches) { + tokenizer["char"] += matches[0].length; + return String.fromCharCode(parseInt(matches[1], 10)); + } + matches = input.match(/^([A-Za-z]+);/); + if (matches) { + var codepoints = this.namedCodepoints[matches[1]]; + if (codepoints) { + tokenizer["char"] += matches[0].length; + for (var i = 0, buffer = ''; i < codepoints.length; i++) { + buffer += String.fromCharCode(codepoints[i]); + } + return buffer; + } + } + }; + + __exports__["default"] = EntityParser; + }); +enifed("simple-html-tokenizer/generate", + ["./generator","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var Generator = __dependency1__["default"]; + + __exports__["default"] = function generate(tokens) { + var generator = new Generator(); + return generator.generate(tokens); + } + }); +enifed("simple-html-tokenizer/generator", + ["exports"], + function(__exports__) { + "use strict"; + var escape = (function () { + var test = /[&<>"'`]/; + var replace = /[&<>"'`]/g; + var map = { + "&": "&", + "<": "<", + ">": ">", + '"': """, + "'": "'", + "`": "`" + }; + function escapeChar(char) { + return map["char"]; + } + return function escape(string) { + if(!test.test(string)) { + return string; + } + return string.replace(replace, escapeChar); + }; + }()); + + function Generator() { + this.escape = escape; + } + + Generator.prototype = { + generate: function (tokens) { + var buffer = ''; + var token; + for (var i=0; i"; + }, + + Chars: function (token) { + return this.escape(token.chars); + }, + + Comment: function (token) { + return ""; + }, + + Attributes: function (attributes) { + var out = [], attribute; + + for (var i=0, l=attributes.length; i") { + return this.emitToken(); + } else { + this.addToComment(char); + this.state = 'comment'; + } + }, + + commentStartDash: function(char) { + if (char === "-") { + this.state = 'commentEnd'; + } else if (char === ">") { + return this.emitToken(); + } else { + this.addToComment("-"); + this.state = 'comment'; + } + }, + + comment: function(char) { + if (char === "-") { + this.state = 'commentEndDash'; + } else { + this.addToComment(char); + } + }, + + commentEndDash: function(char) { + if (char === "-") { + this.state = 'commentEnd'; + } else { + this.addToComment("-" + char); + this.state = 'comment'; + } + }, + + commentEnd: function(char) { + if (char === ">") { + return this.emitToken(); + } else { + this.addToComment("--" + char); + this.state = 'comment'; + } + }, + + tagName: function(char) { + if (isSpace(char)) { + this.state = 'beforeAttributeName'; + } else if (char === "/") { + this.state = 'selfClosingStartTag'; + } else if (char === ">") { + return this.emitToken(); + } else { + this.addToTagName(char); + } + }, + + beforeAttributeName: function(char) { + if (isSpace(char)) { + return; + } else if (char === "/") { + this.state = 'selfClosingStartTag'; + } else if (char === ">") { + return this.emitToken(); + } else { + this.createAttribute(char); + } + }, + + attributeName: function(char) { + if (isSpace(char)) { + this.state = 'afterAttributeName'; + } else if (char === "/") { + this.state = 'selfClosingStartTag'; + } else if (char === "=") { + this.state = 'beforeAttributeValue'; + } else if (char === ">") { + return this.emitToken(); + } else { + this.addToAttributeName(char); + } + }, + + afterAttributeName: function(char) { + if (isSpace(char)) { + return; + } else if (char === "/") { + this.state = 'selfClosingStartTag'; + } else if (char === "=") { + this.state = 'beforeAttributeValue'; + } else if (char === ">") { + return this.emitToken(); + } else { + this.finalizeAttributeValue(); + this.createAttribute(char); + } + }, + + beforeAttributeValue: function(char) { + if (isSpace(char)) { + return; + } else if (char === '"') { + this.state = 'attributeValueDoubleQuoted'; + this.markAttributeQuoted(true); + } else if (char === "'") { + this.state = 'attributeValueSingleQuoted'; + this.markAttributeQuoted(true); + } else if (char === ">") { + return this.emitToken(); + } else { + this.state = 'attributeValueUnquoted'; + this.markAttributeQuoted(false); + this.addToAttributeValue(char); + } + }, + + attributeValueDoubleQuoted: function(char) { + if (char === '"') { + this.finalizeAttributeValue(); + this.state = 'afterAttributeValueQuoted'; + } else if (char === "&") { + this.addToAttributeValue(this.consumeCharRef('"') || "&"); + } else { + this.addToAttributeValue(char); + } + }, + + attributeValueSingleQuoted: function(char) { + if (char === "'") { + this.finalizeAttributeValue(); + this.state = 'afterAttributeValueQuoted'; + } else if (char === "&") { + this.addToAttributeValue(this.consumeCharRef("'") || "&"); + } else { + this.addToAttributeValue(char); + } + }, + + attributeValueUnquoted: function(char) { + if (isSpace(char)) { + this.finalizeAttributeValue(); + this.state = 'beforeAttributeName'; + } else if (char === "&") { + this.addToAttributeValue(this.consumeCharRef(">") || "&"); + } else if (char === ">") { + return this.emitToken(); + } else { + this.addToAttributeValue(char); + } + }, + + afterAttributeValueQuoted: function(char) { + if (isSpace(char)) { + this.state = 'beforeAttributeName'; + } else if (char === "/") { + this.state = 'selfClosingStartTag'; + } else if (char === ">") { + return this.emitToken(); + } else { + this["char"]--; + this.state = 'beforeAttributeName'; + } + }, + + selfClosingStartTag: function(char) { + if (char === ">") { + this.selfClosing(); + return this.emitToken(); + } else { + this["char"]--; + this.state = 'beforeAttributeName'; + } + }, + + endTagOpen: function(char) { + if (isAlpha(char)) { + this.createTag(EndTag, char.toLowerCase()); + } + } + } + }; + + __exports__["default"] = Tokenizer; + }); +enifed("simple-html-tokenizer/tokens", + ["exports"], + function(__exports__) { + "use strict"; + function StartTag(tagName, attributes, selfClosing) { + this.type = 'StartTag'; + this.tagName = tagName || ''; + this.attributes = attributes || []; + this.selfClosing = selfClosing === true; + } + + __exports__.StartTag = StartTag;function EndTag(tagName) { + this.type = 'EndTag'; + this.tagName = tagName || ''; + } + + __exports__.EndTag = EndTag;function Chars(chars) { + this.type = 'Chars'; + this.chars = chars || ""; + } + + __exports__.Chars = Chars;function Comment(chars) { + this.type = 'Comment'; + this.chars = chars || ''; + } + + __exports__.Comment = Comment; + }); +enifed("simple-html-tokenizer/utils", + ["exports"], + function(__exports__) { + "use strict"; + function isSpace(char) { + return (/[\t\n\f ]/).test(char); + } + + __exports__.isSpace = isSpace;function isAlpha(char) { + return (/[A-Za-z]/).test(char); + } + + __exports__.isAlpha = isAlpha;function preprocessInput(input) { + return input.replace(/\r\n?/g, "\n"); + } + + __exports__.preprocessInput = preprocessInput; + }); +requireModule("ember-debug"); +requireModule("ember-template-compiler"); + +})(); +; +if (typeof exports === "object") { + module.exports = Ember.__loader.require("ember-template-compiler"); + }//# sourceMappingURL=ember-template-compiler.map \ No newline at end of file diff --git a/dejacode/static/js/hcaptcha.js b/dejacode/static/js/hcaptcha.js new file mode 100644 index 00000000..578b0875 --- /dev/null +++ b/dejacode/static/js/hcaptcha.js @@ -0,0 +1,3 @@ +/* https://hcaptcha.com/license */ +!function(){"use strict";function t(t){var e=this.constructor;return this.then((function(i){return e.resolve(t()).then((function(){return i}))}),(function(i){return e.resolve(t()).then((function(){return e.reject(i)}))}))}function e(t){return new this((function(e,i){if(!t||"undefined"==typeof t.length)return i(new TypeError(typeof t+" "+t+" is not iterable(cannot read property Symbol(Symbol.iterator))"));var n=Array.prototype.slice.call(t);if(0===n.length)return e([]);var o=n.length;function s(t,i){if(i&&("object"==typeof i||"function"==typeof i)){var r=i.then;if("function"==typeof r)return void r.call(i,(function(e){s(t,e)}),(function(i){n[t]={status:"rejected",reason:i},0==--o&&e(n)}))}n[t]={status:"fulfilled",value:i},0==--o&&e(n)}for(var r=0;r=0&&t<=127},g=-1;function v(t){this.tokens=[].slice.call(t),this.tokens.reverse()}v.prototype={endOfStream:function(){return!this.tokens.length},read:function(){return this.tokens.length?this.tokens.pop():g},prepend:function(t){if(Array.isArray(t))for(var e=t;e.length;)this.tokens.push(e.pop());else this.tokens.push(t)},push:function(t){if(Array.isArray(t))for(var e=t;e.length;)this.tokens.unshift(e.shift());else this.tokens.unshift(t)}};var b=-1;function w(t,e){if(t)throw TypeError("Decoder error");return e||65533}function x(t){return t=String(t).trim().toLowerCase(),Object.prototype.hasOwnProperty.call(C,t)?C[t]:null}var C={};[{encodings:[{labels:["unicode-1-1-utf-8","utf-8","utf8"],name:"UTF-8"}],heading:"The Encoding"}].forEach((function(t){t.encodings.forEach((function(t){t.labels.forEach((function(e){C[e]=t}))}))}));var k,_={"UTF-8":function(t){return new H(t)}},E={"UTF-8":function(t){return new B(t)}},S="utf-8";function A(t,e){if(!(this instanceof A))throw TypeError("Called as a function. Did you forget 'new'?");t=t!==undefined?String(t):S,e=m(e),this._encoding=null,this._decoder=null,this._ignoreBOM=!1,this._BOMseen=!1,this._error_mode="replacement",this._do_not_flush=!1;var i=x(t);if(null===i||"replacement"===i.name)throw RangeError("Unknown encoding: "+t);if(!E[i.name])throw Error("Decoder not present. Did you forget to include encoding-indexes.js first?");var n=this;return n._encoding=i,e.fatal&&(n._error_mode="fatal"),e.ignoreBOM&&(n._ignoreBOM=!0),Object.defineProperty||(this.encoding=n._encoding.name.toLowerCase(),this.fatal="fatal"===n._error_mode,this.ignoreBOM=n._ignoreBOM),n}function L(t,e){if(!(this instanceof L))throw TypeError("Called as a function. Did you forget 'new'?");e=m(e),this._encoding=null,this._encoder=null,this._do_not_flush=!1,this._fatal=e.fatal?"fatal":"replacement";var i=this;if(e.NONSTANDARD_allowLegacyEncoding){var n=x(t=t!==undefined?String(t):S);if(null===n||"replacement"===n.name)throw RangeError("Unknown encoding: "+t);if(!_[n.name])throw Error("Encoder not present. Did you forget to include encoding-indexes.js first?");i._encoding=n}else i._encoding=x("utf-8");return Object.defineProperty||(this.encoding=i._encoding.name.toLowerCase()),i}function B(t){var e=t.fatal,i=0,n=0,o=0,s=128,r=191;this.handler=function(t,a){if(a===g&&0!==o)return o=0,w(e);if(a===g)return b;if(0===o){if(f(a,0,127))return a;if(f(a,194,223))o=1,i=31&a;else if(f(a,224,239))224===a&&(s=160),237===a&&(r=159),o=2,i=15&a;else{if(!f(a,240,244))return w(e);240===a&&(s=144),244===a&&(r=143),o=3,i=7&a}return null}if(!f(a,s,r))return i=o=n=0,s=128,r=191,t.prepend(a),w(e);if(s=128,r=191,i=i<<6|63&a,(n+=1)!==o)return null;var l=i;return i=o=n=0,l}}function H(t){t.fatal;this.handler=function(t,e){if(e===g)return b;if(y(e))return e;var i,n;f(e,128,2047)?(i=1,n=192):f(e,2048,65535)?(i=2,n=224):f(e,65536,1114111)&&(i=3,n=240);for(var o=[(e>>6*i)+n];i>0;){var s=e>>6*(i-1);o.push(128|63&s),i-=1}return o}}Object.defineProperty&&(Object.defineProperty(A.prototype,"encoding",{get:function(){return this._encoding.name.toLowerCase()}}),Object.defineProperty(A.prototype,"fatal",{get:function(){return"fatal"===this._error_mode}}),Object.defineProperty(A.prototype,"ignoreBOM",{get:function(){return this._ignoreBOM}})),A.prototype.decode=function(t,e){var i;i="object"==typeof t&&t instanceof ArrayBuffer?new Uint8Array(t):"object"==typeof t&&"buffer"in t&&t.buffer instanceof ArrayBuffer?new Uint8Array(t.buffer,t.byteOffset,t.byteLength):new Uint8Array(0),e=m(e),this._do_not_flush||(this._decoder=E[this._encoding.name]({fatal:"fatal"===this._error_mode}),this._BOMseen=!1),this._do_not_flush=Boolean(e.stream);for(var n,o=new v(i),s=[];;){var r=o.read();if(r===g)break;if((n=this._decoder.handler(o,r))===b)break;null!==n&&(Array.isArray(n)?s.push.apply(s,n):s.push(n))}if(!this._do_not_flush){do{if((n=this._decoder.handler(o,o.read()))===b)break;null!==n&&(Array.isArray(n)?s.push.apply(s,n):s.push(n))}while(!o.endOfStream());this._decoder=null}return function(t){var e,i;return e=["UTF-8","UTF-16LE","UTF-16BE"],i=this._encoding.name,-1===e.indexOf(i)||this._ignoreBOM||this._BOMseen||(t.length>0&&65279===t[0]?(this._BOMseen=!0,t.shift()):t.length>0&&(this._BOMseen=!0)),function(t){for(var e="",i=0;i>10),56320+(1023&n)))}return e}(t)}.call(this,s)},Object.defineProperty&&Object.defineProperty(L.prototype,"encoding",{get:function(){return this._encoding.name.toLowerCase()}}),L.prototype.encode=function(t,e){t=t===undefined?"":String(t),e=m(e),this._do_not_flush||(this._encoder=_[this._encoding.name]({fatal:"fatal"===this._fatal})),this._do_not_flush=Boolean(e.stream);for(var i,n=new v(function(t){for(var e=String(t),i=e.length,n=0,o=[];n57343)o.push(s);else if(s>=56320&&s<=57343)o.push(65533);else if(s>=55296&&s<=56319)if(n===i-1)o.push(65533);else{var r=e.charCodeAt(n+1);if(r>=56320&&r<=57343){var a=1023&s,l=1023&r;o.push(65536+(a<<10)+l),n+=1}else o.push(65533)}n+=1}return o}(t)),o=[];;){var s=n.read();if(s===g)break;if((i=this._encoder.handler(n,s))===b)break;Array.isArray(i)?o.push.apply(o,i):o.push(i)}if(!this._do_not_flush){for(;(i=this._encoder.handler(n,n.read()))!==b;)Array.isArray(i)?o.push.apply(o,i):o.push(i);this._encoder=null}return new Uint8Array(o)},window.TextDecoder||(window.TextDecoder=A),window.TextEncoder||(window.TextEncoder=L),function(t){if("function"!=typeof Promise)throw"Promise support required";var e=t.crypto||t.msCrypto;if(e){var i=e.subtle||e.webkitSubtle;if(i){var n=t.Crypto||e.constructor||Object,o=t.SubtleCrypto||i.constructor||Object,s=(t.CryptoKey||t.Key,t.navigator.userAgent.indexOf("Edge/")>-1),r=!!t.msCrypto&&!s,a=!e.subtle&&!!e.webkitSubtle;if(r||a){var l={KoZIhvcNAQEB:"1.2.840.113549.1.1.1"},c={"1.2.840.113549.1.1.1":"KoZIhvcNAQEB"};if(["generateKey","importKey","unwrapKey"].forEach((function(t){var n=i[t];i[t]=function(o,s,l){var c,h,u,f,w=[].slice.call(arguments);switch(t){case"generateKey":c=m(o),h=s,u=l;break;case"importKey":c=m(l),h=w[3],u=w[4],"jwk"===o&&((s=g(s)).alg||(s.alg=y(c)),s.key_ops||(s.key_ops="oct"!==s.kty?"d"in s?u.filter(E):u.filter(_):u.slice()),w[1]=v(s));break;case"unwrapKey":c=w[4],h=w[5],u=w[6],w[2]=l._key}if("generateKey"===t&&"HMAC"===c.name&&c.hash)return c.length=c.length||{"SHA-1":512,"SHA-256":512,"SHA-384":1024,"SHA-512":1024}[c.hash.name],i.importKey("raw",e.getRandomValues(new Uint8Array(c.length+7>>3)),c,h,u);if(a&&"generateKey"===t&&"RSASSA-PKCS1-v1_5"===c.name&&(!c.modulusLength||c.modulusLength>=2048))return(o=m(o)).name="RSAES-PKCS1-v1_5",delete o.hash,i.generateKey(o,!0,["encrypt","decrypt"]).then((function(t){return Promise.all([i.exportKey("jwk",t.publicKey),i.exportKey("jwk",t.privateKey)])})).then((function(t){return t[0].alg=t[1].alg=y(c),t[0].key_ops=u.filter(_),t[1].key_ops=u.filter(E),Promise.all([i.importKey("jwk",t[0],c,!0,t[0].key_ops),i.importKey("jwk",t[1],c,h,t[1].key_ops)])})).then((function(t){return{publicKey:t[0],privateKey:t[1]}}));if((a||r&&"SHA-1"===(c.hash||{}).name)&&"importKey"===t&&"jwk"===o&&"HMAC"===c.name&&"oct"===s.kty)return i.importKey("raw",p(d(s.k)),l,w[3],w[4]);if(a&&"importKey"===t&&("spki"===o||"pkcs8"===o))return i.importKey("jwk",b(s),l,w[3],w[4]);if(r&&"unwrapKey"===t)return i.decrypt(w[3],l,s).then((function(t){return i.importKey(o,t,w[4],w[5],w[6])}));try{f=n.apply(i,w)}catch(x){return Promise.reject(x)}return r&&(f=new Promise((function(t,e){f.onabort=f.onerror=function(t){e(t)},f.oncomplete=function(e){t(e.target.result)}}))),f=f.then((function(t){return"HMAC"===c.name&&(c.length||(c.length=8*t.algorithm.length)),0==c.name.search("RSA")&&(c.modulusLength||(c.modulusLength=(t.publicKey||t).algorithm.modulusLength),c.publicExponent||(c.publicExponent=(t.publicKey||t).algorithm.publicExponent)),t=t.publicKey&&t.privateKey?{publicKey:new k(t.publicKey,c,h,u.filter(_)),privateKey:new k(t.privateKey,c,h,u.filter(E))}:new k(t,c,h,u)}))}})),["exportKey","wrapKey"].forEach((function(t){var e=i[t];i[t]=function(n,o,s){var l,c=[].slice.call(arguments);switch(t){case"exportKey":c[1]=o._key;break;case"wrapKey":c[1]=o._key,c[2]=s._key}if((a||r&&"SHA-1"===(o.algorithm.hash||{}).name)&&"exportKey"===t&&"jwk"===n&&"HMAC"===o.algorithm.name&&(c[0]="raw"),!a||"exportKey"!==t||"spki"!==n&&"pkcs8"!==n||(c[0]="jwk"),r&&"wrapKey"===t)return i.exportKey(n,o).then((function(t){return"jwk"===n&&(t=p(unescape(encodeURIComponent(JSON.stringify(g(t)))))),i.encrypt(c[3],s,t)}));try{l=e.apply(i,c)}catch(h){return Promise.reject(h)}return r&&(l=new Promise((function(t,e){l.onabort=l.onerror=function(t){e(t)},l.oncomplete=function(e){t(e.target.result)}}))),"exportKey"===t&&"jwk"===n&&(l=l.then((function(t){return(a||r&&"SHA-1"===(o.algorithm.hash||{}).name)&&"HMAC"===o.algorithm.name?{kty:"oct",alg:y(o.algorithm),key_ops:o.usages.slice(),ext:!0,k:u(f(t))}:((t=g(t)).alg||(t.alg=y(o.algorithm)),t.key_ops||(t.key_ops="public"===o.type?o.usages.filter(_):"private"===o.type?o.usages.filter(E):o.usages.slice()),t)}))),!a||"exportKey"!==t||"spki"!==n&&"pkcs8"!==n||(l=l.then((function(t){return t=w(g(t))}))),l}})),["encrypt","decrypt","sign","verify"].forEach((function(t){var e=i[t];i[t]=function(n,o,s,a){if(r&&(!s.byteLength||a&&!a.byteLength))throw new Error("Empty input is not allowed");var l,c=[].slice.call(arguments),h=m(n);if(!r||"sign"!==t&&"verify"!==t||"RSASSA-PKCS1-v1_5"!==n&&"HMAC"!==n||(c[0]={name:n}),r&&o.algorithm.hash&&(c[0].hash=c[0].hash||o.algorithm.hash),r&&"decrypt"===t&&"AES-GCM"===h.name){var u=n.tagLength>>3;c[2]=(s.buffer||s).slice(0,s.byteLength-u),n.tag=(s.buffer||s).slice(s.byteLength-u)}r&&"AES-GCM"===h.name&&c[0].tagLength===undefined&&(c[0].tagLength=128),c[1]=o._key;try{l=e.apply(i,c)}catch(d){return Promise.reject(d)}return r&&(l=new Promise((function(e,i){l.onabort=l.onerror=function(t){i(t)},l.oncomplete=function(i){i=i.target.result;if("encrypt"===t&&i instanceof AesGcmEncryptResult){var n=i.ciphertext,o=i.tag;(i=new Uint8Array(n.byteLength+o.byteLength)).set(new Uint8Array(n),0),i.set(new Uint8Array(o),n.byteLength),i=i.buffer}e(i)}}))),l}})),r){var h=i.digest;i.digest=function(t,e){if(!e.byteLength)throw new Error("Empty input is not allowed");var n;try{n=h.call(i,t,e)}catch(o){return Promise.reject(o)}return n=new Promise((function(t,e){n.onabort=n.onerror=function(t){e(t)},n.oncomplete=function(e){t(e.target.result)}})),n},t.crypto=Object.create(e,{getRandomValues:{value:function(t){return e.getRandomValues(t)}},subtle:{value:i}}),t.CryptoKey=k}a&&(e.subtle=i,t.Crypto=n,t.SubtleCrypto=o,t.CryptoKey=k)}}}function u(t){return btoa(t).replace(/\=+$/,"").replace(/\+/g,"-").replace(/\//g,"_")}function d(t){return t=(t+="===").slice(0,-t.length%4),atob(t.replace(/-/g,"+").replace(/_/g,"/"))}function p(t){for(var e=new Uint8Array(t.length),i=0;i2&&(i=!0,e.shift());var n={ext:!0};if("1.2.840.113549.1.1.1"!==e[0][0])throw new TypeError("Unsupported key type");var o=["n","e","d","p","q","dp","dq","qi"],s=x(e[1]);i&&s.shift();for(var r=0;r2&&(n=!0,s.unshift(new Uint8Array([0]))),i[0][0]="1.2.840.113549.1.1.1",e=s,i.push(new Uint8Array(C(e)).buffer),n?i.unshift(new Uint8Array([0])):i[1]={tag:3,value:i[1]},new Uint8Array(C(i)).buffer}function x(t,e){if(t instanceof ArrayBuffer&&(t=new Uint8Array(t)),e||(e={pos:0,end:t.length}),e.end-e.pos<2||e.end>t.length)throw new RangeError("Malformed DER");var i,n=t[e.pos++],o=t[e.pos++];if(o>=128){if(o&=127,e.end-e.pos=128){var a=n;n=4;for(e.splice(o,0,a>>24&255,a>>16&255,a>>8&255,255&a);n>1&&!(a>>24);)a<<=8,n--;n<4&&e.splice(o,4-n),n|=128}return e.splice(o-2,2,i,n),e}function k(t,e,i,n){Object.defineProperties(this,{_key:{value:t},type:{value:t.type,enumerable:!0},extractable:{value:i===undefined?t.extractable:i,enumerable:!0},algorithm:{value:e===undefined?t.algorithm:e,enumerable:!0},usages:{value:n===undefined?t.usages:n,enumerable:!0}})}function _(t){return"verify"===t||"encrypt"===t||"wrapKey"===t}function E(t){return"sign"===t||"decrypt"===t||"unwrapKey"===t}}(window),Array.prototype.indexOf||(Array.prototype.indexOf=function(t){return function(e,i){if(null===this||this===undefined)throw TypeError("Array.prototype.indexOf called on null or undefined");var n=t(this),o=n.length>>>0,s=Math.min(0|i,o);if(s<0)s=Math.max(0,o+s);else if(s>=o)return-1;if(void 0===e){for(;s!==o;++s)if(void 0===n[s]&&s in n)return s}else if(e!=e){for(;s!==o;++s)if(n[s]!=n[s])return s}else for(;s!==o;++s)if(n[s]===e)return s;return-1}}(Object)),Array.isArray||(Array.isArray=function(t){return"[object Array]"===Object.prototype.toString.call(t)}),document.getElementsByClassName||(window.Element.prototype.getElementsByClassName=document.constructor.prototype.getElementsByClassName=function(t){if(document.querySelectorAll)return document.querySelectorAll("."+t);for(var e=document.getElementsByTagName("*"),i=new RegExp("(^|\\s)"+t+"(\\s|$)"),n=[],o=0;othis.length)&&(e=this.length),this.substring(e-t.length,e)===t});try{if(Object.defineProperty&&Object.getOwnPropertyDescriptor&&Object.getOwnPropertyDescriptor(Element.prototype,"textContent")&&!Object.getOwnPropertyDescriptor(Element.prototype,"textContent").get){var T=Object.getOwnPropertyDescriptor(Element.prototype,"innerText");Object.defineProperty(Element.prototype,"textContent",{get:function(){return T.get.call(this)},set:function(t){T.set.call(this,t)}})}}catch(Rs){}Function.prototype.bind||(Function.prototype.bind=function(t){if("function"!=typeof this)throw new TypeError("Function.prototype.bind: Item Can Not Be Bound.");var e=Array.prototype.slice.call(arguments,1),i=this,n=function(){},o=function(){return i.apply(this instanceof n?this:t,e.concat(Array.prototype.slice.call(arguments)))};return this.prototype&&(n.prototype=this.prototype),o.prototype=new n,o}),"function"!=typeof Object.create&&(Object.create=function(t,e){function i(){}if(i.prototype=t,"object"==typeof e)for(var n in e)e.hasOwnProperty(n)&&(i[n]=e[n]);return new i}),Date.now||(Date.now=function(){return(new Date).getTime()}),window.console||(window.console={});for(var M,O,V,R,P=["error","info","log","show","table","trace","warn"],F=function(t){},D=P.length;--D>-1;)k=P[D],window.console[k]||(window.console[k]=F);if(window.atob)try{window.atob(" ")}catch(Ps){window.atob=function(t){var e=function(e){return t(String(e).replace(/[\t\n\f\r ]+/g,""))};return e.original=t,e}(window.atob)}else{var I="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",$=/^(?:[A-Za-z\d+\/]{4})*?(?:[A-Za-z\d+\/]{2}(?:==)?|[A-Za-z\d+\/]{3}=?)?$/;window.atob=function(t){if(t=String(t).replace(/[\t\n\f\r ]+/g,""),!$.test(t))throw new TypeError("Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded.");var e,i,n;t+="==".slice(2-(3&t.length));for(var o="",s=0;s>16&255):64===n?String.fromCharCode(e>>16&255,e>>8&255):String.fromCharCode(e>>16&255,e>>8&255,255&e);return o}}if(Event.prototype.preventDefault||(Event.prototype.preventDefault=function(){this.returnValue=!1}),Event.prototype.stopPropagation||(Event.prototype.stopPropagation=function(){this.cancelBubble=!0}),window.Prototype&&Array.prototype.toJSON){console.error("[hCaptcha] Custom JSON polyfill detected, please remove to ensure hCaptcha works properly");var j=Array.prototype.toJSON,N=JSON.stringify;JSON.stringify=function(t){try{return delete Array.prototype.toJSON,N(t)}finally{Array.prototype.toJSON=j}}}Object.keys||(Object.keys=(M=Object.prototype.hasOwnProperty,O=!Object.prototype.propertyIsEnumerable.call({toString:null},"toString"),R=(V=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"]).length,function(t){if("function"!=typeof t&&("object"!=typeof t||null===t))throw new TypeError("Object.keys called on non-object");var e,i,n=[];for(e in t)M.call(t,e)&&n.push(e);if(O)for(i=0;ithis.k.maxBreadcrumbs&&this.u.shift(),this},addPlugin:function(t){var e=[].slice.call(arguments,1);return this.r.push([t,e]),this.n&&this.F(),this},setUserContext:function(t){return this.j.user=t,this},setExtraContext:function(t){return this.Z("extra",t),this},setTagsContext:function(t){return this.Z("tags",t),this},clearContext:function(){return this.j={},this},getContext:function(){return JSON.parse(a(this.j))},setEnvironment:function(t){return this.k.environment=t,this},setRelease:function(t){return this.k.release=t,this},setDataCallback:function(t){var e=this.k.dataCallback;return this.k.dataCallback=o(e,t),this},setBreadcrumbCallback:function(t){var e=this.k.breadcrumbCallback;return this.k.breadcrumbCallback=o(e,t),this},setShouldSendCallback:function(t){var e=this.k.shouldSendCallback;return this.k.shouldSendCallback=o(e,t),this},setTransport:function(t){return this.k.transport=t,this},lastException:function(){return this.d},lastEventId:function(){return this.f},isSetup:function(){return!(!this.a||!this.g&&(this.ravenNotConfiguredError||(this.ravenNotConfiguredError=!0,this.z("error","Error: Raven has not been configured.")),1))},afterLoad:function(){var t=z.RavenConfig;t&&this.config(t.dsn,t.config).install()},showReportDialog:function(t){if(Z){if(!(t=k({eventId:this.lastEventId(),dsn:this.H,user:this.j.user||{}},t)).eventId)throw new c("Missing eventId");if(!t.dsn)throw new c("Missing DSN");var e=encodeURIComponent,i=[];for(var n in t)if("user"===n){var o=t.user;o.name&&i.push("name="+e(o.name)),o.email&&i.push("email="+e(o.email))}else i.push(e(n)+"="+e(t[n]));var s=this.J(this.G(t.dsn)),r=Z.createElement("script");r.async=!0,r.src=s+"/api/embed/error-page/?"+i.join("&"),(Z.head||Z.body).appendChild(r)}},L:function(){var t=this;this.m+=1,setTimeout((function(){t.m-=1}))},$:function(t,e){var i,n;if(this.b){for(n in e=e||{},t="raven"+t.substr(0,1).toUpperCase()+t.substr(1),Z.createEvent?(i=Z.createEvent("HTMLEvents")).initEvent(t,!0,!0):(i=Z.createEventObject()).eventType=t,e)S(e,n)&&(i[n]=e[n]);if(Z.createEvent)Z.dispatchEvent(i);else try{Z.fireEvent("on"+i.eventType.toLowerCase(),i)}catch(o){}}},_:function(t){var e=this;return function(i){if(e.aa=null,e.v!==i){var n;e.v=i;try{n=H(i.target)}catch(o){n=""}e.captureBreadcrumb({category:"ui."+t,message:n})}}},ba:function(){var t=this;return function(e){var i;try{i=e.target}catch(o){return}var n=i&&i.tagName;if(n&&("INPUT"===n||"TEXTAREA"===n||i.isContentEditable)){var s=t.aa;s||t._("input")(e),clearTimeout(s),t.aa=setTimeout((function(){t.aa=null}),1e3)}}},ca:function(t,e){var i=O(this.w.href),n=O(e),o=O(t);this.x=e,i.protocol===n.protocol&&i.host===n.host&&(e=n.relative),i.protocol===o.protocol&&i.host===o.host&&(t=o.relative),this.captureBreadcrumb({category:"navigation",data:{to:e,from:t}})},C:function(){var t=this;t.da=Function.prototype.toString,Function.prototype.toString=function(){return"function"==typeof this&&this.M?t.da.apply(this.O,arguments):t.da.apply(this,arguments)}},Q:function(){this.da&&(Function.prototype.toString=this.da)},D:function(){function t(t){return function(e,n){for(var o=new Array(arguments.length),s=0;s"}}},r)),t.apply?t.apply(this,o):t(o[0],o[1])}}function e(t){var e=z[t]&&z[t].prototype;e&&e.hasOwnProperty&&e.hasOwnProperty("addEventListener")&&(V(e,"addEventListener",(function(e){return function(n,s,r,a){try{s&&s.handleEvent&&(s.handleEvent=i.wrap({mechanism:{type:"instrument",data:{target:t,"function":"handleEvent",handler:s&&s.name||""}}},s.handleEvent))}catch(l){}var c,h,u;return o&&o.dom&&("EventTarget"===t||"Node"===t)&&(h=i._("click"),u=i.ba(),c=function(t){if(t){var e;try{e=t.type}catch(i){return}return"click"===e?h(t):"keypress"===e?u(t):void 0}}),e.call(this,n,i.wrap({mechanism:{type:"instrument",data:{target:t,"function":"addEventListener",handler:s&&s.name||""}}},s,c),r,a)}}),n),V(e,"removeEventListener",(function(t){return function(e,i,n,o){try{i=i&&(i.N?i.N:i)}catch(s){}return t.call(this,e,i,n,o)}}),n))}var i=this,n=i.t,o=this.k.autoBreadcrumbs;V(z,"setTimeout",t,n),V(z,"setInterval",t,n),z.requestAnimationFrame&&V(z,"requestAnimationFrame",(function(t){return function(e){return t(i.wrap({mechanism:{type:"instrument",data:{"function":"requestAnimationFrame",handler:t&&t.name||""}}},e))}}),n);for(var s=["EventTarget","Window","Node","ApplicationCache","AudioTrackList","ChannelMergerNode","CryptoOperation","EventSource","FileReader","HTMLUnknownElement","IDBDatabase","IDBRequest","IDBTransaction","KeyOperation","MediaController","MessagePort","ModalWindow","Notification","SVGElementInstance","Screen","TextTrack","TextTrackCue","TextTrackList","WebSocket","WebSocketWorker","Worker","XMLHttpRequest","XMLHttpRequestEventTarget","XMLHttpRequestUpload"],r=0;r"}}},i)}))}var e=this,i=this.k.autoBreadcrumbs,n=e.t;if(i.xhr&&"XMLHttpRequest"in z){var o=z.XMLHttpRequest&&z.XMLHttpRequest.prototype;V(o,"open",(function(t){return function(i,n){return b(n)&&-1===n.indexOf(e.h)&&(this.ea={method:i,url:n,status_code:null}),t.apply(this,arguments)}}),n),V(o,"send",(function(i){return function(){function n(){if(o.ea&&4===o.readyState){try{o.ea.status_code=o.status}catch(t){}e.captureBreadcrumb({type:"http",category:"xhr",data:o.ea})}}for(var o=this,s=["onload","onerror","onprogress"],r=0;r"}}},t,n)})):o.onreadystatechange=n,i.apply(this,arguments)}}),n)}i.xhr&&R()&&V(z,"fetch",(function(t){return function(){for(var i=new Array(arguments.length),n=0;n2?arguments[2]:void 0;return i&&e.ca(e.x,i+""),t.apply(this,arguments)}};V(z.history,"pushState",l,n),V(z.history,"replaceState",l,n)}if(i.console&&"console"in z&&console.log){var c=function(t,i){e.captureBreadcrumb({message:t,level:i.level,category:"console"})};C(["debug","info","warn","error","log"],(function(t,e){$(console,e,c)}))}},R:function(){for(var t;this.t.length;){var e=(t=this.t.shift())[0],i=t[1],n=t[2];e[i]=n}},S:function(){for(var t in this.q)this.p[t]=this.q[t]},F:function(){var t=this;C(this.r,(function(e,i){var n=i[0],o=i[1];n.apply(t,[t].concat(o))}))},G:function(t){var e=N.exec(t),i={},n=7;try{for(;n--;)i[j[n]]=e[n]||""}catch(o){throw new c("Invalid DSN: "+t)}if(i.pass&&!this.k.allowSecretKey)throw new c("Do not specify your secret key in the DSN. See: http://bit.ly/raven-secret-key");return i},J:function(t){var e="//"+t.host+(t.port?":"+t.port:"");return t.protocol&&(e=t.protocol+":"+e),e},A:function(t,e){(e=e||{}).mechanism=e.mechanism||{type:"onerror",handled:!1},this.m||this.V(t,e)},V:function(t,e){var i=this.X(t,e);this.$("handle",{stackInfo:t,options:e}),this.fa(t.name,t.message,t.url,t.lineno,i,e)},X:function(t,e){var i=this,n=[];if(t.stack&&t.stack.length&&(C(t.stack,(function(e,o){var s=i.ga(o,t.url);s&&n.push(s)})),e&&e.trimHeadFrames))for(var o=0;o0&&(t.breadcrumbs={values:[].slice.call(this.u,0)}),this.j.user&&(t.user=this.j.user),e.environment&&(t.environment=e.environment),e.release&&(t.release=e.release),e.serverName&&(t.server_name=e.serverName),t=this.pa(t),Object.keys(t).forEach((function(e){(null==t[e]||""===t[e]||x(t[e]))&&delete t[e]})),v(e.dataCallback)&&(t=e.dataCallback(t)||t),t&&!x(t)&&(!v(e.shouldSendCallback)||e.shouldSendCallback(t)))return this.ma()?void this.z("warn","Raven dropped error due to backoff: ",t):void("number"==typeof e.sampleRate?Math.random() ".length;t&&n++<5&&!("html"===(e=f(t))||n>1&&o+i.length*s+e.length>=80);)i.push(e),o+=e.length,t=t.parentNode;return i.reverse().join(" > ")},htmlElementAsString:f,isSameException:function(t,e){return!m(t,e)&&(t=t.values[0],e=e.values[0],t.type===e.type&&t.value===e.value&&!function(t,e){return s(t)&&s(e)}(t.stacktrace,e.stacktrace)&&y(t.stacktrace,e.stacktrace))},isSameStacktrace:y,parseUrl:function(t){if("string"!=typeof t)return{};var e=t.match(/^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/),i=e[6]||"",n=e[8]||"";return{protocol:e[2],host:e[4],path:e[5],relative:e[5]+i+n}},fill:function(t,e,i,n){if(null!=t){var o=t[e];t[e]=i(o),t[e].M=!0,t[e].O=o,n&&n.push([t,e,o])}},safeJoin:function(t,e){if(!l(t))return"";for(var i=[],o=0;oi?E(t,e-1):n},serializeKeysForMessage:function(t,e){if("number"==typeof t||"string"==typeof t)return t.toString();if(!Array.isArray(t))return"";if(0===(t=t.filter((function(t){return"string"==typeof t}))).length)return"[object has no keys]";if(e="number"!=typeof e?_:e,t[0].length>=e)return t[0];for(var i=t.length;i>0;i--){var n=t.slice(0,i).join(", ");if(!(n.length>e))return i===t.length?n:n+"…"}return""},sanitize:function(t,e){if(!l(e)||l(e)&&0===e.length)return t;var i,n=p(e),s="********";try{i=JSON.parse(w(t))}catch(o){return t}return function a(t){return l(t)?t.map((function(t){return a(t)})):r(t)?Object.keys(t).reduce((function(e,i){return e[i]=n.test(i)?s:a(t[i]),e}),{}):t}(i)}}}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{7:7}],6:[function(t,e,i){(function(i){function n(){return"undefined"==typeof document||null==document.location?"":document.location.href}var o=t(5),s={collectWindowErrors:!0,debug:!1},r="undefined"!=typeof window?window:void 0!==i?i:"undefined"!=typeof self?self:{},a=[].slice,l="?",c=/^(?:[Uu]ncaught (?:exception: )?)?(?:((?:Eval|Internal|Range|Reference|Syntax|Type|URI|)Error): )?(.*)$/;s.report=function(){function t(e,i){var n=null;if(!i||s.collectWindowErrors){for(var o in p)if(p.hasOwnProperty(o))try{p[o].apply(null,[e].concat(a.call(arguments,2)))}catch(t){n=t}if(n)throw n}}function e(e,r,a,h,d){var p=o.isErrorEvent(d)?d.error:d,f=o.isErrorEvent(e)?e.message:e;if(y)s.computeStackTrace.augmentStackTraceWithInitialElement(y,r,a,f),i();else if(p&&o.isError(p))t(s.computeStackTrace(p),!0);else{var m,g={url:r,line:a,column:h},v=void 0;if("[object String]"==={}.toString.call(f))(m=f.match(c))&&(v=m[1],f=m[2]);g.func=l,t({name:v,message:f,url:n(),stack:[g]},!0)}return!!u&&u.apply(this,arguments)}function i(){var e=y,i=f;f=null,y=null,m=null,t.apply(null,[e,!1].concat(i))}function h(t,e){var n=a.call(arguments,1);if(y){if(m===t)return;i()}var o=s.computeStackTrace(t);if(y=o,m=t,f=n,setTimeout((function(){m===t&&i()}),o.incomplete?2e3:0),!1!==e)throw t}var u,d,p=[],f=null,m=null,y=null;return h.subscribe=function(t){d||(u=r.onerror,r.onerror=e,d=!0),p.push(t)},h.unsubscribe=function(t){for(var e=p.length-1;e>=0;--e)p[e]===t&&p.splice(e,1)},h.uninstall=function(){d&&(r.onerror=u,d=!1,u=void 0),p=[]},h}(),s.computeStackTrace=function(){function t(t){if("undefined"!=typeof t.stack&&t.stack){for(var e,i,o,s=/^\s*at (?:(.*?) ?\()?((?:file|https?|blob|chrome-extension|native|eval|webpack||[a-z]:|\/).*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i,r=/^\s*at (?:((?:\[object object\])?.+) )?\(?((?:file|ms-appx(?:-web)|https?|webpack|blob):.*?):(\d+)(?::(\d+))?\)?\s*$/i,a=/^\s*(.*?)(?:\((.*?)\))?(?:^|@)((?:file|https?|blob|chrome|webpack|resource|moz-extension).*?:\/.*?|\[native code\]|[^@]*(?:bundle|\d+\.js))(?::(\d+))?(?::(\d+))?\s*$/i,c=/(\S+) line (\d+)(?: > eval line \d+)* > eval/i,h=/\((\S*)(?::(\d+))(?::(\d+))\)/,u=t.stack.split("\n"),d=[],p=(/^(.*) is undefined$/.exec(t.message),0),f=u.length;p eval")>-1&&(e=c.exec(i[3]))?(i[3]=e[1],i[4]=e[2],i[5]=null):0!==p||i[5]||"undefined"==typeof t.columnNumber||(d[0].column=t.columnNumber+1),o={url:i[3],func:i[1]||l,args:i[2]?i[2].split(","):[],line:i[4]?+i[4]:null,column:i[5]?+i[5]:null}}if(!o.func&&o.line&&(o.func=l),o.url&&"blob:"===o.url.substr(0,5)){var y=new XMLHttpRequest;if(y.open("GET",o.url,!1),y.send(null),200===y.status){var g=y.responseText||"",v=(g=g.slice(-300)).match(/\/\/# sourceMappingURL=(.*)$/);if(v){var b=v[1];"~"===b.charAt(0)&&(b=("undefined"==typeof document||null==document.location?"":document.location.origin?document.location.origin:document.location.protocol+"//"+document.location.hostname+(document.location.port?":"+document.location.port:""))+b.slice(1)),o.url=b.slice(0,-4)}}}d.push(o)}return d.length?{name:t.name,message:t.message,url:n(),stack:d}:null}}function e(t,e,i,n){var o={url:e,line:i};if(o.url&&o.line){if(t.incomplete=!1,o.func||(o.func=l),t.stack.length>0&&t.stack[0].url===o.url){if(t.stack[0].line===o.line)return!1;if(!t.stack[0].line&&t.stack[0].func===o.func)return t.stack[0].line=o.line,!1}return t.stack.unshift(o),t.partial=!0,!0}return t.incomplete=!0,!1}function i(t,r){for(var a,c,h=/function\s+([_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*)?\s*\(/i,u=[],d={},p=!1,f=i.caller;f&&!p;f=f.caller)if(f!==o&&f!==s.report){if(c={url:null,func:l,line:null,column:null},f.name?c.func=f.name:(a=h.exec(f.toString()))&&(c.func=a[1]),"undefined"==typeof c.func)try{c.func=a.input.substring(0,a.input.indexOf("{"))}catch(y){}d[""+f]?p=!0:d[""+f]=!0,u.push(c)}r&&u.splice(0,r);var m={name:t.name,message:t.message,url:n(),stack:u};return e(m,t.sourceURL||t.fileName,t.line||t.lineNumber,t.message||t.description),m}function o(e,o){var a=null;o=null==o?0:+o;try{if(a=t(e))return a}catch(r){if(s.debug)throw r}try{if(a=i(e,o+1))return a}catch(r){if(s.debug)throw r}return{name:e.name,message:e.message,url:n()}}return o.augmentStackTraceWithInitialElement=e,o.computeStackTraceFromStackProp=t,o}(),e.exports=s}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{5:5}],7:[function(t,e,i){function n(t,e){for(var i=0;i0){var a=n(i,this);~a?i.splice(a+1):i.push(this),~a?o.splice(a,1/0,s):o.push(s),~n(i,r)&&(r=e.call(this,s,r))}else i.push(r);return null==t?r instanceof Error?function(t){var e={stack:t.stack,message:t.message,name:t.name};for(var i in t)Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e}(r):r:t.call(this,s,r)}}i=e.exports=function(t,e,i,n){return JSON.stringify(t,o(e,n),i)},i.getSerialize=o},{}],8:[function(t,e,i){function n(t,e){var i=(65535&t)+(65535&e);return(t>>16)+(e>>16)+(i>>16)<<16|65535&i}function o(t,e,i,o,s,r){return n(function(t,e){return t<>>32-e}(n(n(e,t),n(o,r)),s),i)}function s(t,e,i,n,s,r,a){return o(e&i|~e&n,t,e,s,r,a)}function r(t,e,i,n,s,r,a){return o(e&n|i&~n,t,e,s,r,a)}function a(t,e,i,n,s,r,a){return o(e^i^n,t,e,s,r,a)}function l(t,e,i,n,s,r,a){return o(i^(e|~n),t,e,s,r,a)}function c(t,e){t[e>>5]|=128<>>9<<4)]=e;var i,o,c,h,u,d=1732584193,p=-271733879,f=-1732584194,m=271733878;for(i=0;i>5]>>>e%32&255);return i}function u(t){var e,i=[];for(i[(t.length>>2)-1]=void 0,e=0;e>5]|=(255&t.charCodeAt(e/8))<>>4&15)+n.charAt(15&e);return o}function p(t){return unescape(encodeURIComponent(t))}function f(t){return function(t){return h(c(u(t),8*t.length))}(p(t))}function m(t,e){return function(t,e){var i,n,o=u(t),s=[],r=[];for(s[15]=r[15]=void 0,o.length>16&&(o=c(o,8*t.length)),i=0;i<16;i+=1)s[i]=909522486^o[i],r[i]=1549556828^o[i];return n=c(s.concat(u(e)),512+8*e.length),h(c(r.concat(n),640))}(p(t),p(e))}e.exports=function(t,e,i){return e?i?m(e,t):function(t,e){return d(m(t,e))}(e,t):i?f(t):function(t){return d(f(t))}(t)}},{}]},{},[4])(4)}));var z=[{family:"UC Browser",patterns:["(UC? ?Browser|UCWEB|U3)[ /]?(\\d+)\\.(\\d+)\\.(\\d+)"]},{family:"Opera",name_replace:"Opera Mobile",patterns:["(Opera)/.+Opera Mobi.+Version/(\\d+)\\.(\\d+)","(Opera)/(\\d+)\\.(\\d+).+Opera Mobi","Opera Mobi.+(Opera)(?:/|\\s+)(\\d+)\\.(\\d+)","Opera Mobi","(?:Mobile Safari).*(OPR)/(\\d+)\\.(\\d+)\\.(\\d+)"]},{family:"Opera",name_replace:"Opera Mini",patterns:["(Opera Mini)(?:/att|)/?(\\d+|)(?:\\.(\\d+)|)(?:\\.(\\d+)|)","(OPiOS)/(\\d+).(\\d+).(\\d+)"]},{family:"Opera",name_replace:"Opera Neon",patterns:["Chrome/.+( MMS)/(\\d+).(\\d+).(\\d+)"]},{name_replace:"Opera",patterns:["(Opera)/9.80.*Version/(\\d+)\\.(\\d+)(?:\\.(\\d+)|)","(?:Chrome).*(OPR)/(\\d+)\\.(\\d+)\\.(\\d+)"]},{family:"Firefox",name_replace:"Firefox Mobile",patterns:["(Fennec)/(\\d+)\\.(\\d+)\\.?([ab]?\\d+[a-z]*)","(Fennec)/(\\d+)\\.(\\d+)(pre)","(Fennec)/(\\d+)\\.(\\d+)","(?:Mobile|Tablet);.*(Firefox)/(\\d+)\\.(\\d+)","(FxiOS)/(\\d+)\\.(\\d+)(\\.(\\d+)|)(\\.(\\d+)|)"]},{name_replace:"Coc Coc",patterns:["(coc_coc_browser)/(\\d+)\\.(\\d+)(?:\\.(\\d+)|)"]},{family:"QQ",name_replace:"QQ Mini",patterns:["(MQQBrowser/Mini)(?:(\\d+)(?:\\.(\\d+)|)(?:\\.(\\d+)|)|)"]},{family:"QQ",name_replace:"QQ Mobile",patterns:["(MQQBrowser)(?:/(\\d+)(?:\\.(\\d+)|)(?:\\.(\\d+)|)|)"]},{name_replace:"QQ",patterns:["(QQBrowser)(?:/(\\d+)(?:\\.(\\d+)\\.(\\d+)(?:\\.(\\d+)|)|)|)"]},{family:"Edge",name:"Edge Mobile",patterns:["Windows Phone .*(Edge)/(\\d+)\\.(\\d+)","(EdgiOS|EdgA)/(\\d+)\\.(\\d+).(\\d+).(\\d+)"]},{name_replace:"Edge",patterns:["(Edge|Edg)/(\\d+)(?:\\.(\\d+)|)"]},{patterns:["(Puffin)/(\\d+)\\.(\\d+)(?:\\.(\\d+)|)"]},{family:"Chrome",name_replace:"Chrome Mobile",patterns:["Version/.+(Chrome)/(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)","; wv\\).+(Chrome)/(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)","(CriOS)/(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)","(CrMo)/(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)","(Chrome)/(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+) Mobile(?:[ /]|$)"," Mobile .*(Chrome)/(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)"]},{family:"Yandex",name_replace:"Yandex Mobile",patterns:["(YaBrowser)/(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+).*Mobile"]},{name_replace:"Yandex",patterns:["(YaBrowser)/(\\d+)\\.(\\d+)\\.(\\d+)"]},{patterns:["(Vivaldi)/(\\d+)\\.(\\d+)","(Vivaldi)/(\\d+)\\.(\\d+)\\.(\\d+)"]},{name_replace:"Brave",patterns:["(brave)/(\\d+)\\.(\\d+)\\.(\\d+) Chrome"]},{family:"Chrome",patterns:["(Chromium|Chrome)/(\\d+)\\.(\\d+)(?:\\.(\\d+)|)(?:\\.(\\d+)|)"]},{name_replace:"Internet Explorer Mobile",patterns:["(IEMobile)[ /](\\d+)\\.(\\d+)"]},{family:"Safari",name_replace:"Safari Mobile",patterns:["(iPod|iPhone|iPad).+Version/(d+).(d+)(?:.(d+)|).*[ +]Safari","(iPod|iPod touch|iPhone|iPad);.*CPU.*OS[ +](\\d+)_(\\d+)(?:_(\\d+)|).* AppleNews\\/\\d+\\.\\d+\\.\\d+?","(iPod|iPhone|iPad).+Version/(\\d+)\\.(\\d+)(?:\\.(\\d+)|)","(iPod|iPod touch|iPhone|iPad);.*CPU.*OS[ +](\\d+)_(\\d+)(?:_(\\d+)|).*Mobile.*[ +]Safari","(iPod|iPod touch|iPhone|iPad);.*CPU.*OS[ +](\\d+)_(\\d+)(?:_(\\d+)|).*Mobile","(iPod|iPod touch|iPhone|iPad).* Safari","(iPod|iPod touch|iPhone|iPad)"]},{name_replace:"Safari",patterns:["(Version)/(\\d+)\\.(\\d+)(?:\\.(\\d+)|).*Safari/"]},{name_replace:"Internet Explorer",patterns:["(Trident)/(7|8).(0)"],major_replace:"11"},{name_replace:"Internet Explorer",patterns:["(Trident)/(6)\\.(0)"],major_replace:"10"},{name_replace:"Internet Explorer",patterns:["(Trident)/(5)\\.(0)"],major_replace:"9"},{name_replace:"Internet Explorer",patterns:["(Trident)/(4)\\.(0)"],major_replace:"8"},{family:"Firefox",patterns:["(Firefox)/(\\d+)\\.(\\d+)\\.(\\d+)","(Firefox)/(\\d+)\\.(\\d+)(pre|[ab]\\d+[a-z]*|)"]}],Z=[{family:"Windows",name_replace:"Windows Phone",patterns:["(Windows Phone) (?:OS[ /])?(\\d+)\\.(\\d+)","^UCWEB.*; (wds) (\\d+)\\.(d+)(?:\\.(\\d+)|);","^UCWEB.*; (wds) (\\d+)\\.(\\d+)(?:\\.(\\d+)|);"]},{family:"Windows",name_replace:"Windows Mobile",patterns:["(Windows ?Mobile)"]},{name_replace:"Android",patterns:["(Android)[ \\-/](\\d+)(?:\\.(\\d+)|)(?:[.\\-]([a-z0-9]+)|)","(Android) (d+);","^UCWEB.*; (Adr) (\\d+)\\.(\\d+)(?:[.\\-]([a-z0-9]+)|);","^(JUC).*; ?U; ?(?:Android|)(\\d+)\\.(\\d+)(?:[\\.\\-]([a-z0-9]+)|)","(android)\\s(?:mobile\\/)(\\d+)(?:\\.(\\d+)(?:\\.(\\d+)|)|)","(Silk-Accelerated=[a-z]{4,5})","Puffin/[\\d\\.]+AT","Puffin/[\\d\\.]+AP"]},{name_replace:"Chrome OS",patterns:["(x86_64|aarch64)\\ (\\d+)\\.(\\d+)\\.(\\d+).*Chrome.*(?:CitrixChromeApp)$","(CrOS) [a-z0-9_]+ (\\d+)\\.(\\d+)(?:\\.(\\d+)|)"]},{name_replace:"Windows",patterns:["(Windows 10)","(Windows NT 6\\.4)","(Windows NT 10\\.0)"],major_replace:"10"},{name_replace:"Windows",patterns:["(Windows NT 6\\.3; ARM;)","(Windows NT 6.3)"],major_replace:"8",minor_replace:"1"},{name_replace:"Windows",patterns:["(Windows NT 6\\.2)"],major_replace:"8"},{name_replace:"Windows",patterns:["(Windows NT 6\\.1)"],major_replace:"7"},{name_replace:"Windows",patterns:["(Windows NT 6\\.0)"],major_replace:"Vista"},{name_replace:"Windows",patterns:["(Windows (?:NT 5\\.2|NT 5\\.1))"],major_replace:"XP"},{name_replace:"Mac OS X",patterns:["((?:Mac[ +]?|; )OS[ +]X)[\\s+/](?:(\\d+)[_.](\\d+)(?:[_.](\\d+)|)|Mach-O)","\\w+\\s+Mac OS X\\s+\\w+\\s+(\\d+).(\\d+).(\\d+).*","(?:PPC|Intel) (Mac OS X)"]},{name_replace:"Mac OS X",patterns:[" (Dar)(win)/(10).(d+).*((?:i386|x86_64))"],major_replace:"10",minor_replace:"6"},{name_replace:"Mac OS X",patterns:[" (Dar)(win)/(11).(\\d+).*\\((?:i386|x86_64)\\)"],major_replace:"10",minor_replace:"7"},{name_replace:"Mac OS X",patterns:[" (Dar)(win)/(12).(\\d+).*\\((?:i386|x86_64)\\)"],major_replace:"10",minor_replace:"8"},{name_replace:"Mac OS X",patterns:[" (Dar)(win)/(13).(\\d+).*\\((?:i386|x86_64)\\)"],major_replace:"10",minor_replace:"9"},{name_replace:"iOS",patterns:["^UCWEB.*; (iPad|iPh|iPd) OS (\\d+)_(\\d+)(?:_(\\d+)|);","(CPU[ +]OS|iPhone[ +]OS|CPU[ +]iPhone|CPU IPhone OS)[ +]+(\\d+)[_\\.](\\d+)(?:[_\\.](\\d+)|)","(iPhone|iPad|iPod); Opera","(iPhone|iPad|iPod).*Mac OS X.*Version/(\\d+)\\.(\\d+)","\\b(iOS[ /]|iOS; |iPhone(?:/| v|[ _]OS[/,]|; | OS : |\\d,\\d/|\\d,\\d; )|iPad/)(\\d{1,2})[_\\.](\\d{1,2})(?:[_\\.](\\d+)|)","\\((iOS);","(iPod|iPhone|iPad)","Puffin/[\\d\\.]+IT","Puffin/[\\d\\.]+IP"]},{family:"Chrome",name_replace:"Chromecast",patterns:["(CrKey -)(?:[ /](\\d+)\\.(\\d+)(?:\\.(\\d+)|)|)","(CrKey[ +]armv7l)(?:[ /](\\d+)\\.(\\d+)(?:\\.(\\d+)|)|)","(CrKey)(?:[/](\\d+)\\.(\\d+)(?:\\.(\\d+)|)|)"]},{name_replace:"Debian",patterns:["([Dd]ebian)"]},{family:"Linux",name_replace:"Linux",patterns:["(Linux Mint)(?:/(\\d+)|)"]},{family:"Linux",patterns:["(Ubuntu|Kubuntu|Arch Linux|CentOS|Slackware|Gentoo|openSUSE|SUSE|Red Hat|Fedora|PCLinuxOS|Mageia|(?:Free|Open|Net|\\b)BSD)","(Mandriva)(?: Linux|)/(?:[\\d.-]+m[a-z]{2}(\\d+).(\\d)|)","(Linux)(?:[ /](\\d+)\\.(\\d+)(?:\\.(\\d+)|)|)","\\(linux-gnu\\)"]},{family:"BlackBerry",name_replace:"BlackBerry OS",patterns:["(BB10);.+Version/(\\d+)\\.(\\d+)\\.(\\d+)","(Black[Bb]erry)[0-9a-z]+/(\\d+)\\.(\\d+)\\.(\\d+)(?:\\.(\\d+)|)","(Black[Bb]erry).+Version/(\\d+)\\.(\\d+)\\.(\\d+)(?:\\.(\\d+)|)","(Black[Bb]erry)"]},{patterns:["(Fedora|Red Hat|PCLinuxOS|Puppy|Ubuntu|Kindle|Bada|Sailfish|Lubuntu|BackTrack|Slackware|(?:Free|Open|Net|\\b)BSD)[/ ](\\d+)\\.(\\d+)(?:\\.(\\d+)|)(?:\\.(\\d+)|)"]}],U=navigator.userAgent,W=function(){return U},K=function(t){return J(t||U,z)},q=function(t){return J(t||U,Z)};function G(t,e){try{var i=new RegExp(e).exec(t);return i?{name:i[1]||"Other",major:i[2]||"0",minor:i[3]||"0",patch:i[4]||"0"}:null}catch(Ps){return null}}function J(t,e){for(var i=null,n=null,o=-1,s=!1;++o0||navigator.msMaxTouchPoints>0),e=!1,i&&(e=["iOS","Windows Phone","Windows Mobile","Android","BlackBerry OS"].indexOf(i.name)>=0),t&&e),this.dpr=function(){return window.devicePixelRatio||1},this.mobile&&i&&"Windows"===i.family&&n.indexOf("touch")<0&&(this.mobile=!1),this.os="iOS"===i.family?"ios":"Android"===i.family?"android":"Mac OS X"===i.family?"mac":"Windows"===i.family?"windows":"Linux"===i.family?"linux":i.family.toLowerCase(),this.version=function(){if(!i)return"unknown";var t=i.major;return i.minor&&(t+="."+i.minor),i.patch&&(t+="."+i.patch),t}()},tt={Browser:X,System:Q,supportsPAT:function(){return("mac"===Q.os||"ios"===Q.os)&&"safari"===X.type&&X.version>=16.2}},et={CHALLENGE_PASSED:"challenge-passed",CHALLENGE_ESCAPED:"challenge-escaped",CHALLENGE_CLOSED:"challenge-closed",CHALLENGE_EXPIRED:"challenge-expired",CHALLENGE_ALREADY_CLOSED:"challenge-already-closed",AUTHENTICATION_DONE:"authentication-done",AUTHENTICATION_PASSED:"authentication-passed"},it={INVALID_DATA:"invalid-data",BUNDLE_ERROR:"bundle-error",NETWORK_ERROR:"network-error",RATE_LIMITED:"rate-limited",CHALLENGE_ERROR:"challenge-error",INCOMPLETE_ANSWER:"incomplete-answer",MISSING_CAPTCHA:"missing-captcha",MISSING_SITEKEY:"missing-sitekey",INVALID_CAPTCHA_ID:"invalid-captcha-id",AUTHENTICATION_ERROR:"authentication-error"},nt="https://hcaptcha.com",ot="https://api.hcaptcha.com",st="https://api2.hcaptcha.com",rt="https://cloudflare.hcaptcha.com",at=[nt,st,rt],lt={__proto__:null,CaptchaEvent:et,CaptchaError:it,DEFAULT_ENDPOINT:nt,FALLBACK_ENDPOINT:ot,API2_ENDPOINT:st,CF_ENDPOINT:rt,MAIN_ENDPOINTS:at},ct={host:null,file:null,sitekey:null,a11y_tfe:null,pingdom:"safari"===tt.Browser.type&&"windows"!==tt.System.os&&"mac"!==tt.System.os&&"ios"!==tt.System.os&&"android"!==tt.System.os,assetDomain:"https://newassets.hcaptcha.com",assetUrl:"https://newassets.hcaptcha.com/captcha/v1/096d3a6/static",width:null,height:null,mobile:null,orientation:"portrait",challenge_type:null},ht={theme:{contrast:{hcolor:"#FFF",hfcolor:"#000"},light:{hcolor:"#00838F",hfcolor:"#FFF"}},text:"#555555",accent:"#926FC1",warn:{base:"#EB5757",hover:"#DE3F3F"},link:{base:"#00838f",hover:"#00838f"},white:"#fff",grey:{base:"#333",placeholder:"#f0eff0",selected:"#5C6F8A"},purple:"#65549b",hoverOff:"#00838f",skipHoverOff:"#737373",hoverOn:"#00838f",error:"#fc481e",outline:"#262D38"},ut={se:null,custom:!1,tplinks:"on",language:null,reportapi:"https://accounts.hcaptcha.com",endpoint:nt,pstIssuer:"https://pst-issuer.hcaptcha.com",size:"normal",theme:"light",assethost:null,imghost:null,recaptchacompat:"true",pat:"on",confirmNav:!1},dt="https://30910f52569b4c17b1081ead2dae43b4@sentry.hcaptcha.com/6",pt="096d3a6",ft="prod";function mt(t,e){t.style.width="304px",t.style.height="78px",t.style.backgroundColor="#f9e5e5",t.style.position="relative",t.innerHTML="";var i=document.createElement("div");i.style.width="284px",i.style.position="absolute",i.style.top="12px",i.style.left="10px",i.style.color="#7c0a06",i.style.fontSize="14px",i.style.fontWeight="normal",i.style.lineHeight="18px",i.innerHTML=e||"Please
        upgrade your browser to complete this captcha.",t.appendChild(i)}var yt=[];function gt(t){for(var e=[],i=/(https?|wasm):\/\//,n=/^at /,o=/:\d+:\d+/g,s=0,r=t.length;s=10)){var e=gt(t.trim().split("\n").slice(0,2));yt.push(e)}}(r),-1===r.indexOf("chrome-extension://")&&-1===r.indexOf("safari-extension://")&&-1===r.indexOf("moz-extension://")&&-1===r.indexOf("chrome://internal-")&&-1===r.indexOf("/hammerhead.js")&&-1===r.indexOf("eval at buildCode")&&-1===r.indexOf("u.c.b.r.o.w.s.e.r/ucbrowser_script.js")&&(Ct(t,"global","debug",{name:s,url:e,line:i,column:n,stack:r}),xt("global",o,{message:t}))})}}function wt(t,e,i,n){if(e=e||"error",ut.sentry){var o="warn"===e?"warning":e;window.Raven&&Raven.captureMessage(t,{level:o,logger:i,extra:n})}}function xt(t,e,i){return(i=i||{}).error=e,wt(e.message||"Missing error message","error",t,i)}function Ct(t,e,i,n){ut.sentry&&window.Raven&&Raven.captureBreadcrumb({message:t,category:e,level:i,data:n})}var kt={getCookie:function(t){var e=document.cookie.replace(/ /g,"").split(";");try{for(var i="",n=e.length;n--&&!i;)e[n].indexOf(t)>=0&&(i=e[n]);return i}catch(Ps){return""}},hasCookie:function(t){return!!kt.getCookie(t)},supportsAPI:function(){try{return"hasStorageAccess"in document&&"requestStorageAccess"in document}catch(Ps){return!1}},hasAccess:function(){return new Promise((function(t){document.hasStorageAccess().then((function(){t(!0)}))["catch"]((function(){t(!1)}))}))},requestAccess:function(){try{return document.requestStorageAccess()}catch(Ps){return Promise.resolve()}}},_t={array:function(t){if(0===t.length)return t;for(var e,i,n=t.length;--n>-1;)i=Math.floor(Math.random()*(n+1)),e=t[n],t[n]=t[i],t[i]=e;return t}};function Et(t){this.r=255,this.g=255,this.b=255,this.a=1,this.h=1,this.s=1,this.l=1,this.parseString(t)}function St(t,e,i){return i<0&&(i+=1),i>1&&(i-=1),i<1/6?t+6*(e-t)*i:i<.5?e:i<2/3?t+(e-t)*(2/3-i)*6:t}Et.hasAlpha=function(t){return"string"==typeof t&&(-1!==t.indexOf("rgba")||9===t.length&&"#"===t[0])},Et.prototype.parseString=function(t){t&&(0===t.indexOf("#")?this.fromHex(t):0===t.indexOf("rgb")&&this.fromRGBA(t))},Et.prototype.fromHex=function(t){var e=1;9===t.length&&(e=parseInt(t.substr(7,2),16)/255);var i=(t=t.substr(1,6)).replace(/^([a-f\d])([a-f\d])([a-f\d])?$/i,(function(t,e,i,n){return e+e+i+i+n+n})),n=parseInt(i,16),o=n>>16,s=n>>8&255,r=255&n;this.setRGBA(o,s,r,e)},Et.prototype.fromRGBA=function(t){var e=t.indexOf("rgba"),i=t.substr(e).replace(/rgba?\(/,"").replace(/\)/,"").replace(/[\s+]/g,"").split(","),n=Math.floor(parseInt(i[0])),o=Math.floor(parseInt(i[1])),s=Math.floor(parseInt(i[2])),r=parseFloat(i[3]);this.setRGBA(n,o,s,r)},Et.prototype.setRGB=function(t,e,i){this.setRGBA(t,e,i,1)},Et.prototype.setRGBA=function(t,e,i,n){this.r=t,this.g=e,this.b=i,this.a=isNaN(n)?this.a:n,this.updateHSL()},Et.prototype.hsl2rgb=function(t,e,i){if(0===e){var n=Math.round(255*i);return this.setRGB(n,n,n),this}var o=i<=.5?i*(1+e):i+e-i*e,s=2*i-o;return this.r=Math.round(255*St(s,o,t+1/3)),this.g=Math.round(255*St(s,o,t)),this.b=Math.round(255*St(s,o,t-1/3)),this.h=t,this.s=e,this.l=i,this},Et.prototype.updateHSL=function(){var t,e=this.r/255,i=this.g/255,n=this.b/255,o=Math.max(e,i,n),s=Math.min(e,i,n),r=null,a=(o+s)/2;if(o===s)r=t=0;else{var l=o-s;switch(t=a>.5?l/(2-o-s):l/(o+s),o){case e:r=(i-n)/l+(i1&&(t/=100),this.hsl2rgb(this.h,this.s,t),this},Et.prototype.saturation=function(t){return t>1&&(t/=100),this.hsl2rgb(this.h,t,this.l),this},Et.prototype.hue=function(t){return this.hsl2rgb(t/360,this.s,this.l),this};var At={decode:function(t){try{var e=t.split(".");return{header:JSON.parse(atob(e[0])),payload:JSON.parse(atob(e[1])),signature:atob(e[2].replace(/_/g,"/").replace(/-/g,"+")),raw:{header:e[0],payload:e[1],signature:e[2]}}}catch(Ps){throw new Error("Token is invalid.")}},checkExpiration:function(t){if(new Date(1e3*t)<=new Date(Date.now()))throw new Error("Token is expired.");return!0}},Lt={_setup:!1,_af:null,_fps:60,_singleFrame:1/60,_lagThreshold:500,_adjustedLag:1/60*2,_startTime:0,_lastTime:0,_nextTime:1/60,_elapsed:0,_difference:0,_renders:[],_paused:!1,_running:!1,_tick:!1,frame:0,time:0,requestFrame:null,cancelFrame:null,_init:function(){for(var t,e=window.requestAnimationFrame,i=window.cancelAnimationFrame,n=["ms","moz","webkit","o"],o=n.length;--o>-1&&!e;)e=window[n[o]+"RequestAnimationFrame"],i=window[n[o]+"CancelAnimationFrame"]||window[n[o]+"CancelRequestAnimationFrame"];e?(Lt.requestFrame=e.bind(window),Lt.cancelFrame=i.bind(window)):(Lt.requestFrame=(t=Date.now(),function(e){window.setTimeout((function(){e(Date.now()-t)}),1e3*Lt._singleFrame)}),Lt.cancelFrame=function(t){return clearTimeout(t),null}),Lt._setup=!0,Lt._startTime=Lt._lastTime=Date.now()},add:function(t,e){Lt._renders.push({callback:t,paused:!1==!e||!1}),!1==!e&&Lt.start()},remove:function(t){for(var e=Lt._renders.length;--e>-1;)Lt._renders[e].callback===t&&(Lt._renders[e].paused=!0,Lt._renders.splice(e,1))},start:function(t){if(!1===Lt._setup&&Lt._init(),t)for(var e=Lt._renders.length;--e>-1;)Lt._renders[e].callback===t&&(Lt._renders[e].paused=!1);!0!==Lt._running&&(Lt._paused=!1,Lt._running=!0,Lt._af=Lt.requestFrame(Lt._update))},stop:function(t){if(t)for(var e=Lt._renders.length;--e>-1;)Lt._renders[e].callback===t&&(Lt._renders[e].paused=!0);else!1!==Lt._running&&(Lt._af=Lt.cancelFrame(Lt._af),Lt._paused=!0,Lt._running=!1)},elapsed:function(){return Date.now()-Lt._startTime},fps:function(t){return arguments.length?(Lt._fps=t,Lt._singleFrame=1/(Lt._fps||60),Lt._adjustedLag=2*Lt._singleFrame,Lt._nextTime=Lt.time+Lt._singleFrame,Lt._fps):Lt._fps},isRunning:function(){return Lt._running},_update:function(){if(!Lt._paused&&(Lt._elapsed=Date.now()-Lt._lastTime,Lt._tick=!1,Lt._elapsed>Lt._lagThreshold&&(Lt._startTime+=Lt._elapsed-Lt._adjustedLag),Lt._lastTime+=Lt._elapsed,Lt.time=(Lt._lastTime-Lt._startTime)/1e3,Lt._difference=Lt.time-Lt._nextTime,Lt._difference>0&&(Lt.frame++,Lt._nextTime+=Lt._difference+(Lt._difference>=Lt._singleFrame?Lt._singleFrame/4:Lt._singleFrame-Lt._difference),Lt._tick=!0),Lt._af=Lt.requestFrame(Lt._update),!0===Lt._tick&&Lt._renders.length>0))for(var t=Lt._renders.length;--t>-1;)Lt._renders[t]&&!1===Lt._renders[t].paused&&Lt._renders[t].callback(Lt.time)}},Bt=function(t){for(var e,i,n,o={},s=t?t.indexOf("&")>=0?t.split("&"):[t]:[],r=0;r=0){if(e=s[r].split("="),i=decodeURIComponent(e[0]),"false"!==(n=decodeURIComponent(e[1]))&&"true"!==n||(n="true"===n),"theme"===i||"themeConfig"===i)try{n=JSON.parse(n)}catch(Ps){}o[i]=n}return o},Ht=function(t){var e=[];for(var i in t){var n=t[i];n="object"==typeof n?JSON.stringify(n):n,e.push([encodeURIComponent(i),encodeURIComponent(n)].join("="))}return e.join("&")},Tt={__proto__:null,Decode:Bt,Encode:Ht};function Mt(t,e,i){return Math.min(Math.max(t,e),i)}function Ot(t,e,i,n,o,s){var r=(t-e)*(o-n)/(i-e)+n;return!1===s?r:Mt(r,Math.min(n,o),Math.max(n,o))}function Vt(t){return t*(Math.PI/180)}function Rt(t){return 180*t/Math.PI}var Pt={__proto__:null,clamp:Mt,range:Ot,toRadians:Vt,toDegrees:Rt};function Ft(t){var e=[].slice.call(arguments,1);"string"==typeof t?window[t]?"function"==typeof window[t]?window[t].apply(null,e):console.log("[hCaptcha] Callback '"+t+"' is not a function."):console.log("[hCaptcha] Callback '"+t+"' is not defined."):"function"==typeof t?t.apply(null,e):console.log("[hcaptcha] Invalid callback '"+t+"'.")}function Dt(){try{Ft.apply(null,arguments)}catch(Rs){console.error("[hCaptcha] There was an error in your callback."),console.error(Rs)}}var It={UUID:function(t){return/^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i.test(t)||!1},UUIDv4:function(t){return/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(t)||!1},URL:function(t){var e=new RegExp("^(http|https)://"),i=new RegExp("^((?!(data|javascript):).)*$");return e.test(t)&&i.test(t)}};function $t(t,e){var i,n="attempts"in(e=e||{})?e.attempts:1,o=e.delay||0,s=e.onFail;return i=function(e,i,r){t().then(e,(function(t){var e=n-- >0;s&&(e=!1!==s(t)&&e),e?setTimeout(r,o):i(t)}))},new Promise((function(t,e){i(t,e,(function n(){i(t,e,n)}))}))}function jt(){var t=this;this._bottom=0,this._top=0,this.storage={},this.add=function(e){return t.storage[t._top]=e,t._top++,e},this.remove=function(){if(!t.empty()){var e=t._bottom,i=t.storage[e];return t.storage[e]=null,t._bottom++,i}},this.empty=function(){return t._top===t._bottom},this.size=function(){return t._top-t._bottom}}var Nt={queue:jt,depth:function Fs(t,e,i){if("object"==typeof t&&t[e]&&t[e].length>0)for(var n=t[e].length;--n>-1;)Fs(t[e][n],e,i);t!==undefined&&i(t)},breathe:function(t,e,i){var n=new jt,o=null;for(n.add(t),o=n.remove();o;){for(var s=0;s-1;)t.children[i]===e&&t.children.splice(i,1);e._destroy&&e._destroy(),e=null}))}catch(Ps){throw new Error("Trouble destroying nodes: "+Ps)}return null},zt.prototype._destroy=function(){this.onDestroy&&this.onDestroy();for(var t=this._events.length||0;--t>-1;)this._events.splice(t,1);this.children=null,this._destroy=null,this._events=null,this.destroy=null,this.emit=null,this.on=null,this.off=null,this.initComponent=null},zt.prototype.on=function(t,e){for(var i=this._events.length,n=!1;--i>-1&&!1===n;)this._events[i].event===t&&(n=this._events[i]);!1===n&&(n={event:t,listeners:[]},this._events.push(n)),n.listeners.push(e)},zt.prototype.off=function(t,e){for(var i=this._events.length;--i>-1;)if(this._events[i].event===t){for(var n=this._events[i].listeners.length;--n>-1;)this._events[i].listeners[n]===e&&this._events[i].listeners[n].splice(n,1);0===this._events[i].listeners.length&&this._events[i].splice(i,1)}},zt.prototype.emit=function(t){for(var e=Array.prototype.slice.call(arguments,1),i=this._events.length;--i>-1;)if(this._events[i].event===t)for(var n=this._events[i].listeners.length;--n>-1;)this._events[i].listeners[n].apply(this,e)};var Zt={eventName:function(t){var e=t;return"down"===t||"up"===t||"move"===t||"over"===t||"out"===t?e=!tt.System.mobile||"down"!==t&&"up"!==t&&"move"!==t?"mouse"+t:"down"===t?"touchstart":"up"===t?"touchend":"touchmove":"enter"===t&&(e="keydown"),e},actionName:function(t){var e=t;return"touchstart"===e||"mousedown"===e?e="down":"touchmove"===e||"mousemove"===e?e="move":"touchend"===e||"mouseup"===e?e="up":"mouseover"===e?e="over":"mouseout"===e&&(e="out"),e},eventCallback:function(t,e,i){var n=Zt.actionName(t);return function(o){if(o=o||window.event,"down"===n||"move"===n||"up"===n||"over"===n||"out"===n||"click"===n){var s=Zt.eventCoords(o);if(!s)return;var r=i.getBoundingClientRect();o.windowX=s.x,o.windowY=s.y,o.elementX=o.windowX-(r.x||r.left),o.elementY=o.windowY-(r.y||r.top)}o.keyNum=o.which||o.keyCode||0,"enter"===t&&13!==o.keyNum&&32!==o.keyNum||(o.action=n,o.targetElement=i,e(o))}},eventCoords:function(t){if(!t)return null;var e=t;if(t.touches||t.changedTouches){var i=t.touches&&t.touches.length>=1?t.touches:t.changedTouches;i&&i[0]&&(e=i[0])}return"number"==typeof e.pageX&&"number"==typeof e.pageY?{x:e.pageX,y:e.pageY}:"number"==typeof e.clientX&&"number"==typeof e.clientY?{x:e.clientX,y:e.clientY}:null}},Ut=["Webkit","Moz","ms"],Wt=document.createElement("div").style,Kt={};function qt(t){var e=Kt[t];return e||(t in Wt?t:Kt[t]=function(t){for(var e=t[0].toUpperCase()+t.slice(1),i=Ut.length;i--;)if((t=Ut[i]+e)in Wt)return t}(t)||t)}function Gt(t,e,i){if(this.dom=null,this._clss=[],this._nodes=[],this._listeners=[],this._frag=null,t&&"object"==typeof t){this.dom=t;var n=[],o=[];"string"==typeof t.className&&(o=t.className.split(" "));for(var s=0;s=0||t.indexOf(".")>=0))&&(t&&(e=t),t="div"),this.dom=document.createElement(t),e&&(e.indexOf("#")>=0?this.dom.id=e.split("#")[1]:(e.indexOf(".")>=0&&(e=e.split(".")[1]),this.addClass.call(this,e)));!0===i&&(this._frag=document.createDocumentFragment(),this._frag.appendChild(this.dom))}Gt.prototype.createElement=function(t,e){try{var i=new Gt(t,e,!1);return this.appendElement.call(this,i),this._nodes.push(i),i}catch(Ps){return xt("element",Ps),null}},Gt.prototype.appendElement=function(t){if(t===undefined)return vt({name:"DomElement Add Child",message:"Child Element is undefined"});var e;e=t._frag!==undefined&&null!==t._frag?t._frag:t.dom!==undefined?t.dom:t;try{t instanceof Gt&&(t._parent=this),this.dom.appendChild(e)}catch(Ps){vt({name:"DomElement Add Child",message:"Failed to append child."})}return this},Gt.prototype.removeElement=function(t){try{var e;if(t._nodes)for(e=t._nodes.length;e--;)t.removeElement(t._nodes[e]);for(e=this._nodes.length;--e>-1;)this._nodes[e]===t&&this._nodes.splice(e,1);var i=t instanceof Gt?t.dom:t,n=i.parentNode===this.dom?this.dom:i.parentNode;if(n.removeChild&&n.removeChild(i),!n)throw new Error("Child component does not have correct setup");t.__destroy&&t.__destroy()}catch(Ps){vt({name:"DomElement Remove Child",message:Ps.message||"Failed to remove child."})}},Gt.prototype.addClass=function(t){return!1===this.hasClass.call(this,t)&&(this._clss.push(t),this.dom.className=this._clss.join(" ")),this},Gt.prototype.hasClass=function(t){for(var e=-1!==this.dom.className.split(" ").indexOf(t),i=this._clss.length;i--&&!e;)e=this._clss[i]===t;return e},Gt.prototype.removeClass=function(t){for(var e=this._clss.length;--e>-1;)this._clss[e]===t&&this._clss.splice(e,1);return this.dom.className=this._clss.join(" "),this},Gt.prototype.text=function(t){if(this&&this.dom){if(!t)return this.dom.textContent;for(var e,i,n,o,s=/&(.*?);/g,r=/<[a-z][\s\S]*>/i;null!==(e=s.exec(t));){!1===r.test(e[0])?(n=e[0],o=void 0,(o=document.createElement("div")).innerHTML=n,i=o.textContent,t=t.replace(new RegExp(e[0],"g"),i)):t=t.replace(e[0],"")}return this.dom.textContent=t,this}},Gt.prototype.content=Gt.prototype.text,Gt.prototype.css=function(t){var e,i="ie"===tt.Browser.type&&8===tt.Browser.version,n="safari"===tt.Browser.type&&12===Math.floor(tt.Browser.version);for(var o in t){e=t[o];try{if("transition"===o&&n)continue;"opacity"!==o&&"zIndex"!==o&&"fontWeight"!==o&&isFinite(e)&&parseFloat(e)===e&&(e+="px");var s=qt(o);i&&"opacity"===o?this.dom.style.filter="alpha(opacity="+100*e+")":i&&Et.hasAlpha(e)?this.dom.style[s]=new Et(e).getHex():this.dom.style[s]=e}catch(Rs){}}return this},Gt.prototype.backgroundImage=function(t,e,i,n){var o=e!==undefined&&i!==undefined,s={"-ms-high-contrast-adjust":"none"};if("object"==typeof e&&(n=e),n===undefined&&(n={}),o){var r=t.width/t.height,a=e,l=a/r;n.cover&&li&&(a=(l=i)*r),s.width=a,s.height=l,n.center&&(s.marginLeft=-a/2,s.marginTop=-l/2,s.position="absolute",s.left="50%",s.top="50%"),(n.left||n.right)&&(s.left=n.left||0,s.top=n.top||0)}"ie"===tt.Browser.type&&8===tt.Browser.version?s.filter="progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+t.src+"',sizingMethod='scale')":(s.background="url("+t.src+")",s.backgroundPosition="50% 50%",s.backgroundRepeat="no-repeat",s.backgroundSize=o?a+"px "+l+"px":n.cover?"cover":n.contain?"contain":"100%"),this.css.call(this,s)},Gt.prototype.setAttribute=function(t,e){var i;if("object"==typeof t)for(var n in t)i=t[n],this.dom.setAttribute(n,i);else this.dom.setAttribute(t,e)},Gt.prototype.removeAttribute=function(t,e){var i;if("object"==typeof t)for(var n in t)i=t[n],this.dom.removeAttribute(n,i);else this.dom.removeAttribute(t,e)},Gt.prototype.addEventListener=function(t,e,i){var n={event:Zt.eventName(t),handler:Zt.eventCallback(t,e,this.dom),callback:e};this._listeners.push(n),this.dom.addEventListener?this.dom.addEventListener(n.event,n.handler,i):this.dom.attachEvent("on"+n.event,n.handler)},Gt.prototype.removeEventListener=function(t,e,i){for(var n,o=this._listeners.length;--o>-1;)(n=this._listeners[o]).event===t&&n.callback===e&&(this._listeners.splice(o,1),this.dom.removeEventListener?this.dom.removeEventListener(n.event,n.handler,i):this.dom.detachEvent("on"+n.event,n.handler))},Gt.prototype.focus=function(){this.dom.focus()},Gt.prototype.blur=function(){this.dom.blur()},Gt.prototype.html=function(t){return t&&(this.dom.innerHTML=t),this.dom.innerHTML},Gt.prototype.__destroy=function(){for(var t,e=this._listeners.length;--e>-1;)t=this._listeners[e],this._listeners.splice(e,1),this.dom.removeEventListener?this.dom.removeEventListener(t.event,t.handler):this.dom.detachEvent("on"+t.event,t.handler);return this.dom=null,this._clss=[],this._nodes=[],this._listeners=[],this._frag=null,t=null,null},Gt.prototype.isConnected=function(){return!!this.dom&&("isConnected"in this.dom?this.dom.isConnected:!(this.dom.ownerDocument&&this.dom.ownerDocument.compareDocumentPosition(this.dom)&this.dom.DOCUMENT_POSITION_DISCONNECTED))};var Jt={self:function(t,e){var i={},n=Array.prototype.slice.call(arguments,2);for(var o in e.apply(t,n),t)i[o]=t[o]},proto:function(t,e){t.prototype=Object.create(e.prototype),t.prototype.constructor=t}};function Yt(t,e){Jt.self(this,Gt,e||"div",t),this.children=[],this._events=[]}function Xt(t){if(null===t)return"";var e=[];return Qt(t,e),e.join("&")}function Qt(t,e){var i,n;if("object"==typeof t)for(n in t)!0===te(i=t[n])?Qt(i,e):e[e.length]=ee(n,i);else if(!0===Array.isArray(t))for(var o=0;o-1;)if(this.children[e]===t){this.children.splice(e,1);break}t._destroy&&t._destroy(),t=null},Yt.prototype.removeAllComponents=function(){for(var t=this.children.length;--t>-1;)this.children[t]._destroy&&this.children[t]._destroy();this.children=[]},Yt.prototype.destroy=function(){var t=this;try{Nt.depth(this,"children",(function(e){if(t!==e)for(var i=t.children.length;--i>-1;)t.children[i]===e&&t.children.splice(i,1);e._destroy&&e._destroy(),e=null}))}catch(Ps){throw new Error("Trouble destroying nodes: "+Ps)}return null},Yt.prototype._destroy=function(){try{this.onDestroy&&this.onDestroy(),this._parent.removeElement&&this._parent.removeElement(this);for(var t=this._events.length;--t>-1;)this._events.splice(t,1);this.children=null,this._destroy=null,this._events=null,this.destroy=null,this.emit=null,this.on=null,this.off=null,this.initComponent=null}catch(Ps){vt({name:"DomComponent",message:"Failed to destroy."})}},Yt.prototype.on=function(t,e){for(var i=this._events.length,n=!1;--i>-1&&!1===n;)this._events[i].event===t&&(n=this._events[i]);!1===n&&(n={event:t,listeners:[]},this._events.push(n)),n.listeners.push(e)},Yt.prototype.off=function(t,e){for(var i=this._events.length;--i>-1;)if(this._events[i].event===t){for(var n=this._events[i].listeners.length;--n>-1;)this._events[i].listeners[n]===e&&this._events[i].listeners.splice(n,1);0===this._events[i].listeners.length&&this._events.splice(i,1)}},Yt.prototype.emit=function(t){for(var e=Array.prototype.slice.call(arguments,1),i=this._events.length;--i>-1&&this._events;)if(this._events[i].event===t)for(var n=this._events[i].listeners.length;--n>-1;)this._events[i].listeners[n].apply(this,e)};var ie={af:"Afrikaans",sq:"Albanian",am:"Amharic",ar:"Arabic",hy:"Armenian",az:"Azerbaijani",eu:"Basque",be:"Belarusian",bn:"Bengali",bg:"Bulgarian",bs:"Bosnian",my:"Burmese",ca:"Catalan",ceb:"Cebuano",zh:"Chinese","zh-CN":"Chinese Simplified","zh-TW":"Chinese Traditional",co:"Corsican",hr:"Croatian",cs:"Czech",da:"Danish",nl:"Dutch",en:"English",eo:"Esperanto",et:"Estonian",fi:"Finnish",fr:"French",fy:"Frisian",gd:"Gaelic",gl:"Galacian",ka:"Georgian",de:"German",el:"Greek",gu:"Gujurati",ht:"Haitian",ha:"Hausa",haw:"Hawaiian",he:"Hebrew",hi:"Hindi",hmn:"Hmong",hu:"Hungarian",is:"Icelandic",ig:"Igbo",id:"Indonesian",ga:"Irish",it:"Italian",ja:"Japanese",jw:"Javanese",kn:"Kannada",kk:"Kazakh",km:"Khmer",rw:"Kinyarwanda",ky:"Kirghiz",ko:"Korean",ku:"Kurdish",lo:"Lao",la:"Latin",lv:"Latvian",lt:"Lithuanian",lb:"Luxembourgish",mk:"Macedonian",mg:"Malagasy",ms:"Malay",ml:"Malayalam",mt:"Maltese",mi:"Maori",mr:"Marathi",mn:"Mongolian",ne:"Nepali",no:"Norwegian",ny:"Nyanja",or:"Oriya",fa:"Persian",pl:"Polish","pt-BR":"Portuguese (Brazil)",pt:"Portuguese (Portugal)",ps:"Pashto",pa:"Punjabi",ro:"Romanian",ru:"Russian",sm:"Samoan",sn:"Shona",sd:"Sindhi",si:"Singhalese",sr:"Serbian",sk:"Slovak",sl:"Slovenian",so:"Somani",st:"Southern Sotho",es:"Spanish",su:"Sundanese",sw:"Swahili",sv:"Swedish",tl:"Tagalog",tg:"Tajik",ta:"Tamil",tt:"Tatar",te:"Teluga",th:"Thai",tr:"Turkish",tk:"Turkmen",ug:"Uyghur",uk:"Ukrainian",ur:"Urdu",uz:"Uzbek",vi:"Vietnamese",cy:"Welsh",xh:"Xhosa",yi:"Yiddish",yo:"Yoruba",zu:"Zulu"},ne={zh:{"I am human":"我是人"},ar:{"I am human":"أنا الإنسان"},af:{"I am human":"Ek is menslike"},am:{"I am human":"እኔ ሰዠáŠáŠ"},hy:{"I am human":"ÔµÕ½ Õ´Õ¡Ö€Õ¤ Õ¥Õ´"},az:{"I am human":"MÉ™n insanam"},eu:{"I am human":"Gizakia naiz"},bn:{"I am human":"আমি মানব নই"},bg:{"I am human":"Ðз Ñъм човек"},ca:{"I am human":"Sóc humà"},hr:{"I am human":"Ja sam Äovjek"},cs:{"I am human":"Jsem ÄlovÄ›k"},da:{"I am human":"Jeg er et menneske"},nl:{"I am human":"Ik ben een mens"},et:{"I am human":"Ma olen inimeste"},fi:{"I am human":"Olen ihminen"},fr:{"I am human":"Je suis humain"},gl:{"I am human":"Eu son humano"},ka:{"I am human":"მე ვáƒáƒ  áƒáƒ“áƒáƒ›áƒ˜áƒáƒœáƒ˜"},de:{"I am human":"Ich bin ein Mensch"},el:{"I am human":"Είμαι άνθÏωπος"},gu:{"I am human":"હà«àª‚ માનવ છà«àª‚"},iw:{"I am human":". ×× ×™ ×נושי"},hi:{"I am human":"मैं मानव हूं"},hu:{"I am human":"Nem vagyok robot"},is:{"I am human":"Ég er manneskja"},id:{"I am human":"Aku manusia"},it:{"I am human":"Sono un essere umano"},ja:{"I am human":"ç§ã¯äººé–“ã§ã™"},kn:{"I am human":"ನಾನೠಮಾನವನà³"},ko:{"I am human":"사람입니다"},lo:{"I am human":"ຂ້ອàºà»€àº›àº±àº™àº¡àº°àº™àº¸àº”"},lv:{"I am human":"Es esmu cilvÄ“ks"},lt:{"I am human":"AÅ¡ esu žmogaus"},ms:{"I am human":"Saya manusia"},ml:{"I am human":"ഞാൻ മനàµà´·àµà´¯à´¨à´¾à´£àµ"},mr:{"I am human":"मी मानवी आहे"},mn:{"I am human":"Би бол хүн"},no:{"I am human":"Jeg er menneskelig"},fa:{"I am human":"من انسانی هستم"},pl:{"I am human":"Jestem czÅ‚owiekiem"},pt:{"I am human":"Sou humano"},ro:{"I am human":"Eu sunt om"},ru:{"I am human":"Я человек"},sr:{"I am human":"Ja sam ljudski"},si:{"I am human":"මම මිනිස්සු"},sk:{"I am human":"Ja som Älovek"},sl:{"I am human":"Jaz sem ÄloveÅ¡ki"},es:{"I am human":"Soy humano"},sw:{"I am human":"Mimi ni binadamu"},sv:{"I am human":"Jag är människa"},ta:{"I am human":"நான௠மனித"},te:{"I am human":"నేనౠమనిషిని"},th:{"I am human":"ผมมนุษย์"},tr:{"I am human":"Ben bir insanım"},uk:{"I am human":"Я людини"},ur:{"I am human":"میں انسان ÛÙˆÚº"},vi:{"I am human":"Tôi là con ngÆ°á»i"},zu:{"I am human":"Ngingumuntu"}},oe=null,se={translate:function(t,e){var i=se.getBestTrans(ne),n=i&&i[t];if(n=n||t,e)for(var o=Object.keys(e),s=o.length;s--;)n=n.replace(new RegExp("{{"+o[s]+"}}","g"),e[o[s]]);return n},getBestTrans:function(t){var e=se.getLocale();return e in t?t[e]:se.getShortLocale(e)in t?t[se.getShortLocale(e)]:"en"in t?t.en:null},resolveLocale:function(t){var e=se.getShortLocale(t);return"in"===e&&(t="id"),"iw"===e&&(t="he"),"nb"===e&&(t="no"),"ji"===e&&(t="yi"),"zh-CN"===t&&(t="zh"),"jv"===e&&(t="jw"),ie[t]?t:ie[e]?e:"en"},getLocale:function(){return se.resolveLocale(oe||window.navigator.userLanguage||window.navigator.language)},setLocale:function(t){"zh-Hans"===t?t="zh-CN":"zh-Hant"===t&&(t="zh-TW"),oe=t},getShortLocale:function(t){return t.indexOf("-")>=0?t.substring(0,t.indexOf("-")):t},isShortLocale:function(t){return 2===t.length||3===t.length},addTable:function(t,e){if(e||(e=Object.create(null)),ne[t]){var i=ne[t];for(var n in e)i[n]=e[n]}else ne[t]=e;return ne[t]},getTable:function(t){return ne[t]},addTables:function(t){for(var e in t)se.addTable(e,t[e]);return ne},getTables:function(){return ne}},re={400:"Rate limited or network error. Please retry.",429:"Your computer or network has sent too many requests.",500:"Cannot contact hCaptcha. Check your connection and try again."},ae=function(t){try{return se.translate(re[t])}catch(Ps){return!1}},le="undefined"!=typeof XDomainRequest&&!("withCredentials"in XMLHttpRequest.prototype);function ce(t,e,i){i=i||{};var n={url:e,method:t.toUpperCase(),responseType:i.responseType||"string",dataType:i.dataType||null,withCredentials:i.withCredentials||!1,headers:i.headers||null,data:i.data||null,timeout:i.timeout||null,pst:i.pst||null};n.legacy=n.withCredentials&&le,n.data&&("json"===n.dataType&&"object"==typeof n.data&&(n.data=JSON.stringify(n.data)),"query"===n.dataType&&(n.data=Xt(n.data)));var o="fetch"in window&&n.pst?ue:he;return i.retry?$t((function(){return o(n)}),i.retry):o(n)}function he(t){var e=t.legacy?new XDomainRequest:new XMLHttpRequest,i="function"==typeof t.url?t.url():t.url;return new Promise((function(n,o){var s,r=function(s){return function(){var r=e.response||e.responseText,a=e.statusText||"",l=e.status,c=e.readyState;if(4===c||t.legacy){if("json"===t.responseType&&r)try{r=JSON.parse(r)}catch(h){}if("error"===s||l>=400&&l<=511)return void o({event:it.NETWORK_ERROR,endpoint:i,response:r,state:c,status:l,message:ae(l||400)||a});n({state:c,status:l,body:r,message:a})}}};if((e.onload=r("complete"),e.onerror=e.ontimeout=r("error"),e.open(t.method,i),"arraybuffer"===t.responseType&&(e.responseType="arraybuffer"),t.timeout&&(e.timeout=t.timeout),!t.legacy)&&(e.withCredentials=t.withCredentials,t.headers))for(var a in t.headers)s=t.headers[a],e.setRequestHeader(a,s);setTimeout((function(){e.send(t.data)}),0)}))}function ue(t){var e,i="function"==typeof t.url?t.url():t.url,n=new Headers;if("json"===t.responseType&&n.set("content-type","application/json"),t.headers)for(var o in t.headers)e=t.headers[o],n.set(o,e);var s={method:t.method,credentials:"include",body:t.data,headers:n};if(t.pst){var r={};"token-request"===t.pst?r={version:1,operation:"token-request"}:"token-redemption"===t.pst?r={version:1,operation:"token-redemption",refreshPolicy:"refresh"}:"send-redemption-record"===t.pst&&(r={version:1,operation:"send-redemption-record",issuers:[ut.pstIssuer]}),s.privateToken=r}return new Promise((function(e,n){fetch(i,s).then((function(o){return 200!==o.status?n({event:it.NETWORK_ERROR,endpoint:i,response:o,state:4,status:o.status,message:ae(o.status||400)}):("json"===t.responseType?o.json():o.text()).then((function(t){e({state:4,status:o.status,body:t,message:ae(o.status||400)})}))}))["catch"]((function(t){n({event:it.NETWORK_ERROR,endpoint:i,response:t.error,state:4,status:400,message:ae(400)})}))}))}var de=function(t,e){if("object"==typeof t&&e===undefined&&(t=(e=t).url),null===t)throw new Error("Url missing");return ce("GET",t,e)},pe=function(t,e){if("object"==typeof t&&e===undefined&&(t=(e=t).url),null===t)throw new Error("Url missing");return ce("POST",t,e)},fe=function(t){return t.toLowerCase().match(/\.(?:jpg|gif|png|jpeg|svg)$/g)?"image":t.toLowerCase().match(/\.(?:js)$/g)?"script":"file"},me=function(t){if(ut.assethost&&t.indexOf(ct.assetDomain)>=0)return ut.assethost+t.replace(ct.assetDomain,"");if(ut.imghost&&t.indexOf("imgs")>=0){var e=t.indexOf(".ai")>=0?t.indexOf(".ai")+3:t.indexOf(".com")+4;return ut.imghost+t.substr(e,t.length)}return t},ye=["svg","gif","png"];function ge(t,e){e=e||{};var i,n=t;if(0===n.indexOf("data:image"))for(var o=!1,s=ye.length,r=-1;r++=0)&&(i=ye[r]);else i=n.substr(n.lastIndexOf(".")+1,n.length);!!(!document.createElementNS||!document.createElementNS("http://www.w3.org/2000/svg","svg").createSVGRect)&&e.fallback&&(e.fallback.indexOf(".")>=0?i=(n=e.fallback).substr(n.lastIndexOf(".")+1,n.length):(n=t.substr(0,t.indexOf(i))+e.fallback,i=e.fallback)),e.prefix&&(n=e.prefix+"/"+n),this.attribs={crossOrigin:e.crossOrigin||null},this.id=n,this.src=me(n),this.ext=i,this.width=0,this.height=0,this.aspect=0,this.loaded=!1,this.error=!1,this.element=null,this.cb={load:[],error:[]}}function ve(t,e,i){for(var n=t[e],o=n.length,s=null;--o>-1;)s=n[o],n.splice(o,1),s(i);"error"===e?t.load=[]:t.error=[]}function be(t,e){var i=t;e||(e={}),e.prefix&&(i=e.prefix+"/"+t),this.attribs={defer:e.defer||null,async:e.async||null,crossOrigin:e.crossOrigin||null,integrity:e.integrity||null},this.id=i,this.src=me(i),this.loaded=!1,this.error=!1,this.element=null,this.cb={load:[],error:[]}}function we(t,e,i){for(var n=t[e],o=n.length,s=null;--o>-1;)s=n[o],n.splice(o,1),s(i);"error"===e?t.load=[]:t.error=[]}function xe(t,e){var i=t;e||(e={}),e.prefix&&(i=e.prefix+"/"+t),this.responseType=e.responseType,this.id=i,this.src=me(i),this.loaded=!1,this.error=!1,this.cb={load:[],error:[]},this.data=null}function Ce(t,e,i){for(var n=t[e],o=n.length,s=null;--o>-1;)s=n[o],n.splice(o,1),s(i);"error"===e?t.load=[]:t.error=[]}ge.prototype.load=function(){return("svg"===this.ext?this._loadSvg():this._loadImg())["catch"]((function(t){throw wt("Asset failed","error","assets",{error:t}),t}))},ge.prototype._loadSvg=function(){var t,e=this,i=this.src,n=this.id;if(0===i.indexOf("data:image/svg+xml")){var o=i.slice("data:image/svg+xml,".length);t=Promise.resolve(decodeURIComponent(o))}else t=de(i).then((function(t){return t.body}));return t.then((function(t){var i=(new DOMParser).parseFromString(t,"image/svg+xml").documentElement,n=parseInt(i.getAttribute("width")),o=parseInt(i.getAttribute("height"));return e._imgLoaded(i,n,o),e}))["catch"]((function(t){e.error=!0;var i=(t&&t.message?t.message:t||"Loading Error")+": "+n;throw ve(e.cb,"error",i),i}))},ge.prototype._loadImg=function(){var t=this,e=this.attribs,i=this.src,n=this.id;return new Promise((function(o,s){var r=new Image;e.crossOrigin&&(r.crossOrigin=e.crossOrigin),r.onerror=function(){t.error=!0,r.onload=r.onerror=null;var e="Loading Error: "+n;ve(t.cb,"error",e),s(e)},r.onload=function(){t.loaded||(t._imgLoaded(r,r.width,r.height),r.onload=r.onerror=null,o(t))},r.src=i,r.complete&&r.onload()}))},ge.prototype._imgLoaded=function(t,e,i){this.element=new Gt(t),this.width=e,this.height=i,this.aspect=e/i,this.loaded=!0,ve(this.cb,"load",this)},ge.prototype.onload=function(t){this.error||(this.loaded?t(this):this.cb.load.push(t))},ge.prototype.onerror=function(t){this.loaded&&!this.error||(this.error?t(this):this.cb.error.push(t))},be.prototype.load=function(){var t=this,e=this.attribs,i=this.src,n=this.id;return new Promise((function(o,s){var r=document.createElement("script");t.element=r,r.onerror=function(){t.error=!0,r.onload=r.onreadystatechange=r.onerror=null;var e="Loading Error: "+n;we(t.cb,"error",e),s(e)},r.onload=r.onreadystatechange=function(){this.loaded||r.readyState&&"loaded"!==r.readyState&&"complete"!==r.readyState||(t.loaded=!0,r.onload=r.onreadystatechange=r.onerror=null,document.body.removeChild(r),we(t.cb,"load",t),o(t))},r.type="text/javascript",r.src=i,e.crossOrigin&&(r.crossorigin=e.crossOrigin),e.async&&(r.async=!0),e.defer&&(r.defer=!0),e.integrity&&(r.integrity=e.integrity),document.body.appendChild(r),r.complete&&r.onload()}))},be.prototype.onload=function(t){this.error||(this.loaded?t(this):this.cb.load.push(t))},be.prototype.onerror=function(t){this.loaded&&!this.error||(this.error?t(this):this.cb.error.push(t))},xe.prototype.load=function(){var t=this,e=this.src,i=this.id;return new Promise((function(n,o){var s={};"arraybuffer"===t.responseType?s.responseType="arraybuffer":e.indexOf("json")>=0&&(s.responseType="json"),de(e,s).then((function(e){t.loaded=!0,t.data=e.body,Ce(t.cb,"load",t),n(t)}))["catch"]((function(e){t.error=!0;var n=(e&&e.message?e.message:"Loading Error")+": "+i;Ce(t.cb,"error",n),o(n)}))}))},xe.prototype.onload=function(t){this.error||(this.loaded?t(this):this.cb.load.push(t))},xe.prototype.onerror=function(t){this.loaded&&!this.error||(this.error?t(this):this.cb.error.push(t))};var ke=[],_e={add:function(t,e){var i=fe(t);return _e[i]?_e[i](t,e):Promise.resolve(null)},batch:function(t,e){for(var i=[],n=-1;++n-1&&!o;)o=(s=ke[n]).id===t||-1!==s.id.indexOf("/"===t[0]?"":"/"+t);if(!o)return e(null);s.onload(e),s.onerror(i)}))}},Ee=[],Se=!1,Ae=!1;function Le(t){var e=Array.prototype.slice.call(arguments,1);!0!==Ae&&"interactive"!==document.readyState&&"loaded"!==document.readyState&&"complete"!==document.readyState?(Ee.push({fn:t,args:e}),!1===Se&&Be()):setTimeout((function(){t(e)}),1)}function Be(){document.addEventListener?(document.addEventListener("DOMContentLoaded",Te),window.addEventListener("load",Te)):(document.attachEvent("onreadystatechange",He),window.attachEvent("onload",Te)),Se=!0}function He(){"interactive"!==document.readyState&&"loaded"!==document.readyState&&"complete"!==document.readyState||Te()}function Te(){if(!1===Ae){for(var t=0;t=0&&(n=document.getElementById(e.replace("#",""))),e.indexOf(".")>=0)if(null===n&&(n=document),o){for(var a=[],l=0;l=this._period&&(this._date.push(t),this._data.push(e)),!i){var n=t-this._prevTimestamp;this._meanPeriod=(this._meanPeriod*this._meanCounter+n)/(this._meanCounter+1),this._meanCounter++}this._prevTimestamp=t},Re.prototype._cleanStaleData=function(){for(var t=Date.now(),e=this._date.length-1;e>=0;e--){if(t-this._date[e]>=this._interval){this._date.splice(0,e+1),this._data.splice(0,e+1);break}}};var Pe={touchstart:"ts",touchend:"te",touchmove:"tm",touchcancel:"tc"},Fe={mousedown:"md",mouseup:"mu",mousemove:"mm"},De={keydown:"kd",keyup:"ku"},Ie={devicemotion:"dm"},$e=function(t,e){var i=Fe[t],n=null;return function(t){n=function(t){return[t.windowX,t.windowY,Date.now()]}(t),e(i,n)}},je=function(t,e){var i=Pe[t],n=null;return function(t){n=function(t){var e=[];try{var i,n;if(t.touches&&t.touches.length>=1?i=t.touches:t.changedTouches&&t.changedTouches.length>=1&&(i=t.changedTouches),i){for(var o=0;ot.y&&Je(e,e.next,t)>0)return 1}else if(e.next.y<=t.y&&Je(e,e.next,t)<0)return-1;return 0}function Je(t,e,i){return(e.x-t.x)*(i.y-t.y)-(i.x-t.x)*(e.y-t.y)}function Ye(t){Jt.self(this,qe),this.bounding={left:0,top:0,width:0,height:0},this.svgData=function(t){if("string"!=typeof t)return null;var e=decodeURIComponent(t),i=e.indexOf('d="')+3,n=e.indexOf('"',i),o=t.slice(i,n),s=function(t){var e=0,i=0,n=0,o=0,s=t.match(/]*width=['"]([^'"]+)['"]/),r=t.match(/]*height=['"]([^'"]+)['"]/);if(s&&r&&(n=parseFloat(s[1]),o=parseFloat(r[1]),!isNaN(n)&&!isNaN(o)))return{left:e,top:i,width:n,height:o};var a=t.match(/]*viewBox=['"]([^'"]+)['"]/);if(a){var l=a[1].split(" ");if(e=parseFloat(l[0]),i=parseFloat(l[1]),n=parseFloat(l[2]),o=parseFloat(l[3]),!(isNaN(e)||isNaN(i)||isNaN(n)||isNaN(o)))return{left:e,top:i,width:n,height:o}}return{left:0,top:0,width:0,height:0}}(e);return{pathCommands:Xe(o),viewport:s}}(t),this.svgData&&(this.bounding.width=this.svgData.viewport.width,this.bounding.height=this.svgData.viewport.height)}function Xe(t){for(var e=t.match(/[a-df-zA-DF-Z][^a-df-zA-DF-Z]*/g)||[],i=[],n=0;n=this.getDistance(t)},We.prototype.restrict=function(t,e,i,n){if("x"!==t&&"y"!==t)throw new Error("Point.restrict requires a value: x or y");return e+this[t]n&&(e=n-this[t]),this[t]+e},We.prototype.draw=function(t){t.ctx.beginPath(),t.ctx.arc(this.x,this.y,this.radius/t.scale,0,2*Math.PI,!1),this.fill&&(t.ctx.fillStyle=this.fillColor,t.ctx.fill()),this.stroke&&(t.ctx.strokeStyle=this.strokeColor,t.ctx.lineWidth=this.strokeWidth/t.scale,t.ctx.stroke())},Jt.proto(Ke,We),Ke.prototype.set=function(t,e,i){this.x=t.x||this.x,this.y=t.y||this.y,e===undefined?(this.handleIn.x=this.x,this.handleIn.y=this.y):(this.handleIn.x=e.x,this.handleIn.y=e.y),i===undefined?(this.handleOut.x=this.x,this.handleOut.y=this.y):(this.handleOut.x=i.x,this.handleOut.y=i.y)},Ke.prototype.clone=function(){var t={x:this.x,y:this.y},e={x:this.handleIn.x,y:this.handleIn.y},i={x:this.handleOut.x,y:this.handleOut.y},n=new Ke;return e.x===i.x&&e.y===i.y?n.set(t):n.set(t,e,i),n.index=this.index,n.prev=this.prev,n.next=this.next,n.radius=this.radius,n.tolerance=this.tolerance,n.fill=this.fill,n.stroke=this.stroke,n.fillColor=this.fillColor,n.strokeColor=this.strokeColor,n.strokeWidth=this.strokeWidth,n},Ke.prototype.move=function(t,e){this.x+=t,this.y+=e,this.handleIn.x+=t,this.handleIn.y+=e,this.handleOut.x+=t,this.handleOut.y+=e},Ke.prototype.render=function(t){this.handleIn.x!==this.x&&this.handleIn.y!==this.y&&this.handleIn.draw(t),this.handleOut.x!==this.x&&this.handleOut.y!==this.y&&this.handleOut.draw(t),this.draw(t)},qe.prototype.addPoint=function(t){var e;return t instanceof Ke?e=t.clone():((e=new Ke).set(t),e.radius=this.pointRadius),e.index=this.segments.length,null===this._head?(this._head=e,this._tail=e):(e.prev=this._tail,this._tail.next=e,this._tail=e),this._head.prev=this._tail,this._tail.next=this._head,this.segments.push(e),e},qe.prototype.addPoints=function(t){for(var e=0;e-1&&null===i;)i=this.segments[e],t.x===i.x&&t.y===i.y&&(this.segments.splice(e,1),i===this._head&&i===this._tail?(this._head=null,this._tail=null):i===this.head?(this._head=this._head.next,this._head.prev=null):i===this._tail?(this._tail=this._tail.prev,this._tail.next=null):(i.prev.next=i.next,i.next.prev=i.prev));return i},qe.prototype.forEachPoint=function(t,e){if(0!==this.segments.length&&this.segments)for(var i,n=!1,o=this.segments.length;--o>-1&&!n;)i=this.segments[e?this.segments.length-1-o:o],t&&(n=t(i))},qe.prototype.close=function(t){this._closed=t},qe.prototype.isClosed=function(){return this._closed},qe.prototype.start=function(){return this._head},qe.prototype.end=function(){return this._tail},qe.prototype.rotate=function(t,e){e===undefined&&(e=this.getCenter());for(var i,n=-1;++ni)&&(i=t.x),(null===n||t.yo)&&(o=t.y);return{left:e,top:n,bottom:o,right:i}},qe.prototype.draw=function(t){t.ctx.beginPath();for(var e=-1,i=this.segments.length;++e-1;)this.segments.splice(t,1);return this._head=null,this._tail=null,this.segments=[],null},Jt.proto(Ye,qe),Ye.prototype.size=function(t,e){t?(e||(e=t),this.bounding.width=t,this.bounding.height=e):(this.bounding.width=this.svgData.viewport.width,this.bounding.height=this.svgData.viewport.height)},Ye.prototype.move=function(t,e){t&&(e||(e=t),this.bounding.left=t,this.bounding.top=e)},Ye.prototype.destroy=function(){this.bounding={left:0,top:0,width:0,height:0},this.svgData=null},Ye.prototype.getBounding=function(){return this.bounding},Ye.prototype.drawSVG=function(t){Qe(t,this.svgData,this.bounding)};var Qe=function(t,e,i){if(t&&e&&i){var n=t.ctx,o=0,s=0,r=e.pathCommands,a=e.viewport,l=i.width/a.width,c=i.height/a.height,h=Math.min(l,c);n.translate(i.left+a.left,i.top+a.top),n.beginPath();for(var u=0;u0?s:-s,a=o>0?s:-s;t.ctx.beginPath(),t.ctx.moveTo(e+r,i),t.ctx.lineTo(e+n-r,i),t.ctx.quadraticCurveTo(e+n,i,e+n,i+a),t.ctx.lineTo(e+n,i+o-a),t.ctx.quadraticCurveTo(e+n,i+o,e+n-r,i+o),t.ctx.lineTo(e+r,i+o),t.ctx.quadraticCurveTo(e,i+o,e,i+o-a),t.ctx.lineTo(e,i+a),t.ctx.quadraticCurveTo(e,i,e+r,i),t.ctx.closePath()}}function ii(){this._events=Object.create(null)}function ni(t,e,i,n){this._events[t]||(this._events[t]=[]),this._events[t].unshift({fn:e,once:n,context:i})}function oi(t,e,i){this.target=t,this.setTargetOrigin(i),this.id=e,this.messages=[],this.incoming=[],this.waiting=[],this.isReady=!0,this.queue=[]}Jt.proto(ti,We),ti.prototype.draw=function(t){var e=this.radius/t.scale;if(this.complete)t.ctx.save(),t.ctx.beginPath(),t.ctx.arc(this.x,this.y,e+2,0,2*Math.PI),t.ctx.strokeStyle=ht.white,t.ctx.fillStyle=ht.white,t.ctx.lineWidth=2,t.ctx.stroke(),t.ctx.fill(),t.ctx.beginPath(),t.ctx.arc(this.x,this.y,e+3,0,2*Math.PI),t.ctx.strokeStyle=ht.outline,t.ctx.lineWidth=1,t.ctx.stroke(),t.ctx.restore(),this.hovered&&(t.ctx.beginPath(),t.ctx.arc(this.x,this.y,e+9,0,2*Math.PI),t.ctx.strokeStyle=ht.white,t.ctx.lineWidth=2,t.ctx.stroke(),t.ctx.beginPath(),t.ctx.arc(this.x,this.y,e+10,0,2*Math.PI),t.ctx.strokeStyle=ht.outline,t.ctx.lineWidth=1,t.ctx.stroke());else{var i=2.5,n=[2.5,4];t.ctx.beginPath(),t.ctx.arc(this.x,this.y,1.5,0,2*Math.PI,!1),t.ctx.strokeStyle=ht.white,t.ctx.lineWidth=.5,t.ctx.fillStyle=this.fillColor,t.ctx.fill(),t.ctx.strokeStyle=ht.outline,t.ctx.lineWidth=.5,t.ctx.stroke(),t.ctx.beginPath(),t.ctx.arc(this.x,this.y,e+2,0,2*Math.PI),t.ctx.strokeStyle=ht.white,t.ctx.lineWidth=2,t.ctx.stroke(),t.ctx.beginPath(),t.ctx.arc(this.x,this.y,e+3,0,2*Math.PI),t.ctx.strokeStyle=ht.outline,t.ctx.lineWidth=1,t.ctx.stroke(),t.ctx.fillStyle=ht.outline,t.ctx.roundedRect(this.x-(e+5),this.y-2,n[0],n[1],2),t.ctx.fill(),t.ctx.fillStyle=ht.white,t.ctx.roundedRect(this.x-(e+5-1),this.y-1,n[0],n[1]/2,1),t.ctx.fill(),t.ctx.fillStyle=ht.outline,t.ctx.roundedRect(this.x+e+i,this.y-2,n[0],n[1],2),t.ctx.fill(),t.ctx.fillStyle=ht.white,t.ctx.roundedRect(this.x+e+i-1,this.y-1,n[0],n[1]/2,1),t.ctx.fill(),t.ctx.fillStyle=ht.outline,t.ctx.roundedRect(this.x-2,this.y-e-5,n[1],n[0],2),t.ctx.fill(),t.ctx.fillStyle=ht.white,t.ctx.roundedRect(this.x-1,this.y-e-5+1,n[1]/2,n[0],1),t.ctx.fill(),t.ctx.fillStyle=ht.outline,t.ctx.roundedRect(this.x-2,this.y+e+i,n[1],n[0],2),t.ctx.fill(),t.ctx.fillStyle=ht.white,t.ctx.roundedRect(this.x-1,this.y+e+i-1,n[1]/2,n[0],1),t.ctx.fill()}},Jt.proto(ei,Gt),ei.prototype.dimensions=function(t,e){this.css({width:t,height:e}),this.element.width=Math.round(t/this.scale)*this.dpr,this.element.height=Math.round(e/this.scale)*this.dpr,this.ctx.scale(this.dpr,this.dpr),this.width=Math.round(t/this.scale),this.height=Math.round(e/this.scale)},ei.prototype.clear=function(){this.ctx&&this.ctx.clearRect(0,0,this.element.width,this.element.height)},ei.prototype.draw=function(){this.ctx&&(this.ctx.fillStyle=this.clearColor,this.ctx.fillRect(0,0,this.element.width,this.element.height))},ei.prototype._destroy=function(){this.__destroy(),this.element=null,this.ctx=null,this.width=null,this.height=null},ii.prototype.on=function(t,e,i){ni.call(this,t,e,i,!1)},ii.prototype.once=function(t,e,i){ni.call(this,t,e,i,!0)},ii.prototype.off=function(t,e){var i=this._events[t];if(i){for(var n=i.length;--n>-1;)i[n].fn===e&&i.splice(n,1);0===i.length&&delete this._events[t]}},ii.prototype.emit=function(t){var e=this._events[t];if(e){for(var i,n=Array.prototype.slice.call(arguments,1),o=e.length;--o>-1;)(i=e[o]).fn.apply(i.context,n),i.once&&e.splice(o,1);0===e.length&&delete this._events[t]}},ii.prototype.removeAllListeners=function(){this._events=Object.create(null)},oi.prototype._sendMessage=function(t,e){var i=t instanceof HTMLIFrameElement;try{i?t.contentWindow.postMessage(JSON.stringify(e),this.targetOrigin):t.postMessage(JSON.stringify(e),this.targetOrigin)}catch(Rs){xt("messaging",Rs),"*"!==this.targetOrigin&&(this.setTargetOrigin("*"),this._sendMessage(t,e))}},oi.prototype.setReady=function(t){var e=this;e.isReady=t,e.isReady&&e.queue.length&&(e.queue.forEach((function(t){e._sendMessage.apply(e,t)})),e.clearQueue())},oi.prototype.clearQueue=function(){this.queue=[]},oi.prototype.setID=function(t){this.id=t},oi.prototype.setTargetOrigin=function(t){this.targetOrigin="*"},oi.prototype.contact=function(t,e){if(!this.id)throw new Error("Chat requires unique id to communicate between windows");var i=this,n=Math.random().toString(36).substr(2),o={source:"hcaptcha",label:t,id:this.id,promise:"create",lookup:n};if(e){if("object"!=typeof e)throw new Error("Message must be an object.");o.contents=e}return new Promise((function(e,s){i.waiting.push({label:t,reject:s,resolve:e,lookup:n}),i._addToQueue(i.target,o)}))},oi.prototype.listen=function(t,e){if(!this.id)throw new Error("Chat requires unique id to communicate between windows");for(var i=this.messages.length,n=!1;--i>-1&&!1===n;)this.messages[i].label===t&&(n=this.messages[i]);!1===n&&(n={label:t,listeners:[]},this.messages.push(n)),n.listeners.push(e)},oi.prototype.answer=function(t,e){if(!this.id)throw new Error("Chat requires unique id to communicate between windows");for(var i=this.incoming.length,n=!1;--i>-1&&!1===n;)this.incoming[i].label===t&&(n=this.incoming[i]);!1===n&&(n={label:t,listeners:[]},this.incoming.push(n)),n.listeners.push(e)},oi.prototype.send=function(t,e){var i=this;if(!i.id)throw new Error("Chat requires unique id to communicate between windows");var n={source:"hcaptcha",label:t,id:i.id};if(e){if("object"!=typeof e)throw new Error("Message must be an object.");n.contents=e}i._addToQueue(i.target,n)},oi.prototype.check=function(t,e){for(var i=[].concat.apply([],[this.messages,this.incoming,this.waiting]),n=[],o=-1;++o-1&&!1===l;)this.waiting[a].label===e.label&&this.waiting[a].lookup===e.lookup&&(l=!0,this.waiting.splice(a,1));continue}for(o=0;o-1&&!1===e;)t.id===si.chats[i].id&&t.target===si.chats[i].target&&(e=si.chats[i],si.chats.splice(i,1));return e},handleGlobal:function(t){if(si.globalEnabled){var e=si.messages;if(e.length>=10)si.globalEnabled=!1;else{var i=e.some((function(e){return JSON.stringify(e.data)===JSON.stringify(t.data)}));i||e.push(t)}}},handle:function(t){var e=t.data,i="string"==typeof e&&e.indexOf("hcaptcha")>=0;try{if(!i)return void si.handleGlobal(t);e=JSON.parse(e);for(var n,o=si.chats,s=-1;++s-1;)this.children[e].style(t)},Li.prototype.parseText=function(t){var e,i,n=[{type:"BOLD",regex:/\*\*([^*]*)\*\*/g},{type:"LINK",regex:/\[([^[]+)]\(([^)]*)\)/g}],o=[];for(e=n.length;--e>-1;)for(;null!=(i=n[e].regex.exec(t));)i.type=n[e].type,o.push(i);o=o.sort((function(t,e){return t.index-e.index})),this.removeAllComponents();var s=0;for(e=0;e1,qi=!!window.navigator.msPointerEnabled,Gi="onkeydown"in document;function Ji(t){this.state={pause:!1,action:null,position:{x:0,y:0},delta:{x:0,y:0},created:!1},this.config={arrowScrolling:!1!==t.arrowScrolling,keyStep:t.keyStep||120,firefoxMult:t.firefoxMult||15,touchMult:t.touchMult||2,mouseMult:t.mouseMult||1};var e=t.element||document.body;e instanceof Gt||(e=new Gt(e),this.state.created=!0),this.element=e,this.msBodyTouch=null,this.clamp={enabled:!1,min:{x:0,y:0},max:{x:0,y:0}},this.onWheel=this.onWheel.bind(this),this.onKey=this.onKey.bind(this),this.onTouch=this.onTouch.bind(this),this.destroy=this.destroy.bind(this),this._addListeners()}function Yi(){Jt.self(this,Yt,"scroll-container")}function Xi(t){Jt.self(this,Yt,t.selector||"list-custom"),this.state={skipAnimationOnce:!1},this.scroll=new Ji({element:this,arrowScrolling:!1,mouseMult:.5,keyStep:46}),this._container=this.initComponent(Yi),this._handle=this.createElement("div"),this.on("scroll-update",this._onScrollUpdate.bind(this))}function Qi(t){var e=t.palette,i=t.component;return Ci.merge({main:{fill:e.common.transparent,line:e.grey[200],text:e.grey[700]},hover:{fill:e.grey[200],text:e.grey[900],border:e.primary.main+"b3"},selected:{fill:"#5C6F8A",text:e.grey[100]}},i.listItem)}function tn(t){Jt.self(this,Yt,t.option.selector||".option");var e=this;this.state={style:Qi(t.theme.get()),selected:!1,usingKb:!1,isLast:!1,size:t.size,option:t.option,theme:t.theme,isMenu:t.isMenu,height:t.height},this.text=this.text.bind(this),this._text=this.createElement("span"),this._separator=this.createElement("div"),this.addEventListener("click",this.select.bind(this)),this.addEventListener("enter",this.select.bind(this)),this.addEventListener("over",this._onHover.bind(this,!0)),this.addEventListener("out",this._onHover.bind(this,!1)),this.addEventListener("blur",(function(){e.updateStyle(!1),e.emit("blur")})),this.addEventListener("focus",(function(){e.updateStyle(e.state.usingKb),e.emit("focus")})),this.setAttribute("tabindex",0),this.setAttribute("aria-selected",this.state.selected),this.setAttribute("aria-setsize",this.state.size),this.setAttribute("role",this.state.isMenu?"menuitem":"option"),this.setCopy()}function en(t){Jt.self(this,Xi,{selector:t.selector||"list-custom"}),this.state={theme:t.theme,isMenu:t.isMenu,usingKb:!1,visible:!1,centerOnce:!1,search:"",focusedId:-1,selected:null,optionStyle:null,searchTimer:null,optionsVisible:t.optionsVisible||6,optionHeight:46},this._options=[],this.setAttribute("tabindex",-1),this.setAttribute("aria-expanded",!1),this.setAttribute("role",this.state.isMenu?"presentation":"listbox"),this.addEventListener("keydown",this.onKeyPress.bind(this))}function nn(t){Jt.self(this,Yt,(t=t||{}).selector||".box-container"),this._theme=t.theme,this.boxState={ariaLabel:t.ariaLabel,visible:!0,css:{boxSizing:t.boxSizing,width:t.width,height:t.height,padding:t.padding,margin:t.margin,borderWidth:t.borderWidth,borderStyle:t.borderStyle,borderRadius:t.borderRadius,borderColor:t.borderColor,backgroundColor:t.backgroundColor,cursor:t.cursor}},this.setStyle(this.boxState),this.setAriaLabel(),this.setVisible(!0)}function on(t){Jt.self(this,Yt,t.selector||".border"),this.state={visible:t.visible===undefined||t.visible,thickness:t.thickness||1,color:t.color||"#000000",rounded:t.rounded||0},this.$top=this.createElement("div"),this.$right=this.createElement("div"),this.$left=this.createElement("div"),this.$bottom=this.createElement("div")}function sn(t){var e=t.palette,i=t.component;return Ci.merge({focus:{outline:e.primary.main}},i.link)}function rn(t){Jt.self(this,Yt,"logo","a"),this.state={theme:t.theme,url:t.url||"",width:t.width||0,height:t.height||0},this.setAttribute("tabindex",0),this.setAttribute("target","_blank"),this.setAttribute("href",this.state.url);var e={selector:".logo-graphic",src:t.src,fallback:t.fallback,autoLoad:t.autoLoad};this.graphic=this.initComponent(Bi,e),this.graphic.css({cursor:"pointer","-ms-high-contrast-adjust":"none"}),this.onSelect=this.onSelect.bind(this),this.onFocus=this.onFocus.bind(this),this.onBlur=this.onBlur.bind(this),this.addEventListener("click",this.onSelect),this.addEventListener("enter",this.onSelect),this.addEventListener("focus",this.onFocus),this.addEventListener("blur",this.onBlur)}function an(t){var e=t.palette,i=t.component;return Ci.merge({main:{fill:e.primary.main,icon:e.common.white}},i.badge)}function ln(t){if(Jt.self(this,Yt,t.selector||".badge"),t||(t={}),this._theme=t.theme,this._style=an(this._theme.get()),this._timer=null,this.state={loaded:!1,visible:!1},this.config={icon:t.icon,value:t.value,size:t.size||0},t.icon){var e,i=t.icon;"object"==typeof t.icon&&(i=t.icon.src,e=t.icon.fallback),this.$wrapper=this.createElement(".badge-icon-wrapper"),this.icon=this.initComponent(Bi,{selector:".icon",src:i,fallback:e},this.$wrapper)}this.$fill=this.createElement(".badge-fill"),this.$radial=this.createElement(".badge-radial")}Ji.prototype.pause=function(t){this.state.pause=t},Ji.prototype.update=function(t){if(!this.state.pause){var e=this.state.position,i=this.state.delta,n=this.state.action;e.x+=i.x,e.y+=i.y,this.clamp.enabled?(e.x=Mt(e.x,this.clamp.min.x,this.clamp.max.x),e.y=Mt(e.y,this.clamp.min.y,this.clamp.max.y)):console.log(e.y,this.element.dom.scrollHeight),this.element.emit("scroll-update",{x:e.x,y:e.y,delta:i,action:n,original:t})}},Ji.prototype._addListeners=function(){var t={passive:!1};("ie"!==tt.Browser.type||"ie"===tt.Browser.type&&8!==tt.Browser.version)&&(this.element.addEventListener("DOMMouseScroll",this.onWheel),this.element.addEventListener("wheel",this.onWheel,t)),this.element.addEventListener("mousewheel",this.onWheel,t),Wi&&(this.element.addEventListener("touchstart",this.onTouch),this.element.addEventListener("touchmove",this.onTouch)),qi&&Ki&&(this.msBodyTouch=document.body.style.msTouchAction,document.body.style.msTouchAction="none",this.element.addEventListener("MSPointerDown",this.onTouch,!0),this.element.addEventListener("MSPointerMove",this.onTouch,!0)),this.config.arrowScrolling&&Gi&&this.element.addEventListener("keydown",this.onKey)},Ji.prototype._removeListeners=function(){var t={passive:!1};("ie"!==tt.Browser.type||"ie"===tt.Browser.type&&8!==tt.Browser.version)&&(this.element.removeEventListener("DOMMouseScroll",this.onWheel),this.element.removeEventListener("wheel",this.onWheel,t)),this.element.removeEventListener("mousewheel",this.onWheel,t),Wi&&(this.element.removeEventListener("touchstart",this.onTouch),this.element.removeEventListener("touchmove",this.onTouch)),qi&&Ki&&(this.msBodyTouch=document.body.style.msTouchAction,document.body.style.msTouchAction="none",this.element.removeEventListener("MSPointerDown",this.onTouch,!0),this.element.removeEventListener("MSPointerMove",this.onTouch,!0)),this.config.arrowScrolling&&Gi&&this.element.removeEventListener("keydown",this.onKey)},Ji.prototype.onWheel=function(t){if(!this.state.pause){(t=window.event||t).preventDefault&&t.preventDefault();var e=this.state.delta,i=this.config.mouseMult,n=this.config.firefoxMult;"detail"in t&&"wheel"!==t.type&&0!==t.detail?(e.y=-1*t.detail,e.y*=n):"wheelDelta"in t&&!("wheelDeltaY"in t)?e.y=-1*t.wheelDelta:(e.x=-1*(t.deltaX||t.wheelDeltaX),e.y=-1*(t.deltaY||t.wheelDeltaY),"firefox"===tt.Browser.type&&1===t.deltaMode&&n&&(e.x*=n,e.y*=n)),i&&(e.x*=i,e.y*=i),this.state.action="wheel",this.update.call(this,t)}},Ji.prototype.onTouch=function(t){if(!this.state.pause){var e=this.state.position,i=this.state.delta,n=this.config.touchMult,o=t.targetTouches[0];"move"===t.action?(i.x=(o.pageX-e.x)*n,i.y=(o.pageY-e.y)*n):(i.x=0,i.y=0),this.state.action="touch",this.update.call(this,t)}},Ji.prototype.onKey=function(t){if(!this.state.pause&&!t.metaKey){var e=this.state.delta,i=this.config.keyStep;switch(t.keyCode){case Ui:t.preventDefault&&t.preventDefault(),e.x=0,e.y=-i;break;case Zi:t.preventDefault&&t.preventDefault(),e.x=0,e.y=i;break;case Ni:e.x=-i,e.y=0;break;case zi:e.x=i,e.y=0;break;default:return e.x=0,void(e.y=0)}this.state.action="keypress",this.update.call(this,t)}},Ji.prototype.clampX=function(t,e,i){this.clamp.enabled=t,this.clamp.min.x=e||0,this.clamp.max.x=i||0},Ji.prototype.clampY=function(t,e,i){this.clamp.enabled=t,this.clamp.min.y=e||0,this.clamp.max.y=i||0},Ji.prototype.reset=function(){this.state.position={x:0,y:0},this.state.delta={x:0,y:0}},Ji.prototype.setPosX=function(t){this.setPos(t,this.state.position.y)},Ji.prototype.setPosY=function(t){this.setPos(this.state.position.x,t)},Ji.prototype.moveYBy=function(t){this.setPos(this.state.position.x,this.state.position.y+t)},Ji.prototype.getY=function(){return this.state.position.y},Ji.prototype.setPos=function(t,e){this.clamp.enabled&&(t=Mt(t,this.clamp.min.x,this.clamp.max.x),e=Mt(e,this.clamp.min.y,this.clamp.max.y)),this.state.position={x:t,y:e},this.state.delta={x:0,y:0},this.element.emit("scroll-update",{x:t,y:e,delta:this.state.delta,action:null})},Ji.prototype.destroy=function(){var t=this.state.created;this._removeListeners(),this.state={pause:!1,action:null,position:{x:0,y:0},delta:{x:0,y:0},created:!1},t&&(this.element=this.element.destroy())},Jt.proto(Yi,Yt),Jt.proto(Xi,Yt),Xi.prototype.getContainer=function(){return this._container},Xi.prototype.scrollInView=function(t,e,i){this.dom.scrollTop=0,this.state.skipAnimationOnce=i;var n=-t.offsetTop,o=t.offsetHeight,s=this.dom.clientHeight,r=this._container.dom.scrollHeight,a=this.scroll.getY(),l=a-s;this._handle.css({display:r<=s?"none":"block"}),this.scroll.clampY(!0,s-r,0),e?this.scroll.setPosY(n+s/2-o/2):n>a?this.scroll.setPosY(n):n-o=t.length/2?this.$right:this.$left,(e=this.initComponent(Di,{theme:_i,text:n.text,value:n.value},i)).setCopy(),e.on("select",this.handeSelect),this.options.push(e)}Jt.proto(bn,Yt),bn.prototype.style=function(t){this.copy.style(t,"left")},bn.prototype.setCopy=function(){this.copy.translate()},Jt.proto(wn,Yt),wn.prototype.style=function(t,e,i){var n=Math.floor(Ot(t,250,275,12,14));this.confirmation.style(n),this.confirmation.css({marginBottom:10}),this.actions.style(t,i),this.actions.css({width:i?200:220,height:35,position:"relative",margin:"10px auto 0px"})},wn.prototype.setCopy=function(){this.confirmation.setCopy(),this.actions.setCopy()},Jt.proto(xn,Yt),xn.prototype.style=function(t,e){var i=Math.floor(t/2);this.$left.css({width:"50%",display:"inline-block"}),this.$right.css({width:"50%",display:"inline-block"});for(var n=0;n=0?n=Mn:t.indexOf("accessibility")>=0&&(n=In,o="Accessibility"),this.state.visible&&(this.modal.destroy(),this.modal=this.initComponent(pn,null,this.$container),this.modal.load(),this.modal.on("close",this.close)),this.modalContent=this.initComponent(n,e,this.modal.$content),this.modal.setTitle(o),this.modalContent.setCopy(),this.modalContent.on("close",this.close),this.modalContent.on("change",this.display),this.modalContent.on("report",(function(t){i.emit("report",t)})),this._styleContent(),this.css({visibility:"visible",display:"table",zIndex:200}),this.modal.focus(),this.state.visible=!0,this.emit("open")},$n.prototype.isOpen=function(){return this.state.visible},Jt.proto(jn,Yt),jn.prototype.style=function(t,e){this.css({width:t,height:e,position:"relative",zIndex:0})},jn.prototype.mount=function(t){var e=this;this.appendElement(t),this.handleResize=function(){e.emit("resize")},this.handleCheck=function(i){var n="skip";i?n=t.breadcrumbs&&t.served=0&&"check"===this.state.type&&(t="Verify"),this.copy.text(t),this.setAttribute("title",e),this.setAttribute("aria-label",e)},Gn.prototype.getElement=function(){return this&&this.dom||null},Jt.proto(Jn,Yt),Jn.prototype.removeCrumbs=function(){this.breadcrumbs.removeCrumbs()},Jn.prototype.style=function(t,e,i){var n="landscape"===ct.orientation&&"image_label_binary"===ct.challenge_type,o=n?e:16;this.breadcrumbs.display&&(this.breadcrumbs.style(t,i),this.breadcrumbs.css({position:"absolute",top:(o-this.breadcrumbs.size)/2}));var s=i?11:12;return this.text.style(s),this.text.css({position:"absolute",height:o,top:0,right:n?"auto":0,left:n?0:"auto",width:n?140:"100%"}),this.submit.style(i),this.submit.css({position:"absolute",right:0,bottom:0,zIndex:100}),this.css({width:t,height:e}),{width:t,height:e}},Jn.prototype.setAction=function(t){this.state.action=t,this.submit.action(t)},Jn.prototype.getAction=function(){return this.state.action},Jn.prototype.displayTryAgain=function(t){this.text.display(t)},Jn.prototype.setWhiteLabelEnabled=function(t){this.state.whiteLabel=t},Jn.prototype.translate=function(){this.text.setCopy(),this.submit.setLabel()},Jn.prototype.setLock=function(t){this.state.locked=t,this.submit.setLock(t)},Jn.prototype.isLocked=function(){return this.state.locked},Jt.proto(Yn,Ri),Yn.prototype.setLocale=function(t){this.setText(se.getShortLocale(t).toUpperCase())},Yn.prototype.style=function(){var t=function(t){var e=t.palette,i=t.component;return Ci.merge({focus:{outline:e.primary.main}},i.button)}(this._theme.get()),e="landscape"===ct.orientation&&"image_label_binary"===ct.challenge_type,i=e?14:11,n=e?35:26,o=e?35:16;this.state.width=n,this.state.height=o,this.css({display:"table",cursor:"pointer",textAlign:"center",fontWeight:600,width:n,height:o,fontSize:i,outlineColor:t.focus.outline,borderRadius:4}),this.$text.css({display:"table-cell",verticalAlign:"middle"})},Jt.proto(Xn,Yt),Xn.prototype.style=function(t){var e="landscape"===ct.orientation&&"image_label_binary"===ct.challenge_type;this.display.style(),this.css({position:"relative",display:"inline-block",top:t?5:10,left:0,zIndex:100}),this.list.style(),this.list.css({bottom:e?-128:30,left:e?45:"auto"})},Xn.prototype.getDimensions=function(){return{width:this.display.getWidth(),height:this.display.getHeight()}},Xn.prototype.setLabel=function(){var t=this.list.getSelected().text,e=se.translate("Select a language {{language}}",{language:t});this.display.setLabel(e),this.display.setTitle(se.translate("Language"))},Xn.prototype.updateLocale=function(){this.list.select(se.getLocale())},Xn.prototype.setVisible=function(t){this.css({display:t?"block":"none "})},Xn.prototype.setLock=function(t){this.state.locked=t,t?this.list.setAttribute("disabled",t):this.list.removeAttribute("disabled")};function Qn(t){Jt.self(this,Yt,"hcaptcha-logo"),this.mobile=!1,this.charity=t;var e=this.charity?"data:image/svg+xml,%3csvg id='logo_charity' role='img' aria-hidden='true' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 500 500'%3e%3crect x='306.25' y='418.75' width='56.25' height='56.25' style='fill:%230074bf%3bopacity:0.5%3bisolation:isolate'/%3e%3crect x='250' y='418.75' width='56.25' height='56.25' style='fill:%230074bf%3bopacity:0.699999988079071%3bisolation:isolate'/%3e%3crect x='193.75' y='418.75' width='56.25' height='56.25' style='fill:%230074bf%3bopacity:0.699999988079071%3bisolation:isolate'/%3e%3crect x='137.5' y='418.75' width='56.25' height='56.25' style='fill:%230074bf%3bopacity:0.5%3bisolation:isolate'/%3e%3crect x='362.5' y='362.5' width='56.25' height='56.25' style='fill:%230082bf%3bopacity:0.699999988079071%3bisolation:isolate'/%3e%3crect x='306.25' y='362.5' width='56.25' height='56.25' style='fill:%230082bf%3bopacity:0.800000011920929%3bisolation:isolate'/%3e%3crect x='250' y='362.5' width='56.25' height='56.25' style='fill:%230082bf'/%3e%3crect x='193.75' y='362.5' width='56.25' height='56.25' style='fill:%230082bf'/%3e%3crect x='137.5' y='362.5' width='56.25' height='56.25' style='fill:%230082bf%3bopacity:0.800000011920929%3bisolation:isolate'/%3e%3crect x='81.25' y='362.5' width='56.25' height='56.25' style='fill:%230082bf%3bopacity:0.699999988079071%3bisolation:isolate'/%3e%3crect x='418.75' y='306.25' width='56.25' height='56.25' style='fill:%23008fbf%3bopacity:0.5%3bisolation:isolate'/%3e%3crect x='362.5' y='306.25' width='56.25' height='56.25' style='fill:%23008fbf%3bopacity:0.800000011920929%3bisolation:isolate'/%3e%3crect x='306.25' y='306.25' width='56.25' height='56.25' style='fill:%23008fbf'/%3e%3crect x='250' y='306.25' width='56.25' height='56.25' style='fill:%23008fbf'/%3e%3crect x='193.75' y='306.25' width='56.25' height='56.25' style='fill:%23008fbf'/%3e%3crect x='137.5' y='306.25' width='56.25' height='56.25' style='fill:%23008fbf'/%3e%3crect x='81.25' y='306.25' width='56.25' height='56.25' style='fill:%23008fbf%3bopacity:0.800000011920929%3bisolation:isolate'/%3e%3crect x='25' y='306.25' width='56.25' height='56.25' style='fill:%23008fbf%3bopacity:0.5%3bisolation:isolate'/%3e%3crect x='418.75' y='250' width='56.25' height='56.25' style='fill:%23009dbf%3bopacity:0.699999988079071%3bisolation:isolate'/%3e%3crect x='362.5' y='250' width='56.25' height='56.25' style='fill:%23009dbf'/%3e%3crect x='306.25' y='250' width='56.25' height='56.25' style='fill:%23009dbf'/%3e%3crect x='250' y='250' width='56.25' height='56.25' style='fill:%23009dbf'/%3e%3crect x='193.75' y='250' width='56.25' height='56.25' style='fill:%23009dbf'/%3e%3crect x='137.5' y='250' width='56.25' height='56.25' style='fill:%23009dbf'/%3e%3crect x='81.25' y='250' width='56.25' height='56.25' style='fill:%23009dbf'/%3e%3crect x='25' y='250' width='56.25' height='56.25' style='fill:%23009dbf%3bopacity:0.699999988079071%3bisolation:isolate'/%3e%3crect x='418.75' y='193.75' width='56.25' height='56.25' style='fill:%2300abbf%3bopacity:0.699999988079071%3bisolation:isolate'/%3e%3crect x='362.5' y='193.75' width='56.25' height='56.25' style='fill:%2300abbf'/%3e%3crect x='306.25' y='193.75' width='56.25' height='56.25' style='fill:%2300abbf'/%3e%3crect x='250' y='193.75' width='56.25' height='56.25' style='fill:%2300abbf'/%3e%3crect x='193.75' y='193.75' width='56.25' height='56.25' style='fill:%2300abbf'/%3e%3crect x='137.5' y='193.75' width='56.25' height='56.25' style='fill:%2300abbf'/%3e%3crect x='81.25' y='193.75' width='56.25' height='56.25' style='fill:%2300abbf'/%3e%3crect x='25' y='193.75' width='56.25' height='56.25' style='fill:%2300abbf%3bopacity:0.699999988079071%3bisolation:isolate'/%3e%3crect x='418.75' y='137.5' width='56.25' height='56.25' style='fill:%2300b9bf%3bopacity:0.5%3bisolation:isolate'/%3e%3crect x='362.5' y='137.5' width='56.25' height='56.25' style='fill:%2300b9bf%3bopacity:0.800000011920929%3bisolation:isolate'/%3e%3crect x='306.25' y='137.5' width='56.25' height='56.25' style='fill:%2300b9bf'/%3e%3crect x='250' y='137.5' width='56.25' height='56.25' style='fill:%2300b9bf'/%3e%3crect x='193.75' y='137.5' width='56.25' height='56.25' style='fill:%2300b9bf'/%3e%3crect x='137.5' y='137.5' width='56.25' height='56.25' style='fill:%2300b9bf'/%3e%3crect x='81.25' y='137.5' width='56.25' height='56.25' style='fill:%2300b9bf%3bopacity:0.800000011920929%3bisolation:isolate'/%3e%3crect x='25' y='137.5' width='56.25' height='56.25' style='fill:%2300b9bf%3bopacity:0.5%3bisolation:isolate'/%3e%3crect x='362.5' y='81.25' width='56.25' height='56.25' style='fill:%2300c6bf%3bopacity:0.699999988079071%3bisolation:isolate'/%3e%3crect x='306.25' y='81.25' width='56.25' height='56.25' style='fill:%2300c6bf%3bopacity:0.800000011920929%3bisolation:isolate'/%3e%3crect x='250' y='81.25' width='56.25' height='56.25' style='fill:%2300c6bf'/%3e%3crect x='193.75' y='81.25' width='56.25' height='56.25' style='fill:%2300c6bf'/%3e%3crect x='137.5' y='81.25' width='56.25' height='56.25' style='fill:%2300c6bf%3bopacity:0.800000011920929%3bisolation:isolate'/%3e%3crect x='81.25' y='81.25' width='56.25' height='56.25' style='fill:%2300c6bf%3bopacity:0.699999988079071%3bisolation:isolate'/%3e%3crect x='306.25' y='25' width='56.25' height='56.25' style='fill:%2300d4bf%3bopacity:0.5%3bisolation:isolate'/%3e%3crect x='250' y='25' width='56.25' height='56.25' style='fill:%2300d4bf%3bopacity:0.699999988079071%3bisolation:isolate'/%3e%3crect x='193.75' y='25' width='56.25' height='56.25' style='fill:%2300d4bf%3bopacity:0.699999988079071%3bisolation:isolate'/%3e%3crect x='137.5' y='25' width='56.25' height='56.25' style='fill:%2300d4bf%3bopacity:0.5%3bisolation:isolate'/%3e%3cpath d='M190.87%2c158.6c36.33%2c0%2c46.52%2c26.05%2c59.6%2c34.41%2c12.11-8.36%2c22.29-34.41%2c59.59-34.41%2c36.34%2c0%2c65.18%2c29.8%2c66%2c67%2c2.78%2c54-90.26%2c135.93-125.63%2c159.19-36.34-23.26-128.42-105.16-126.6-159.19C125.69%2c188.4%2c153.56%2c158.6%2c190.87%2c158.6Z' style='fill:white'/%3e%3c/svg%3e":"data:image/svg+xml,%3csvg width='32' height='32' viewBox='0 0 32 32' role='img' aria-hidden='true' fill='none' xmlns='http://www.w3.org/2000/svg'%3e%3cpath opacity='0.5' d='M24 28H20V32H24V28Z' fill='%230074BF'/%3e%3cpath opacity='0.7' d='M20 28H16V32H20V28Z' fill='%230074BF'/%3e%3cpath opacity='0.7' d='M16 28H12V32H16V28Z' fill='%230074BF'/%3e%3cpath opacity='0.5' d='M12 28H8V32H12V28Z' fill='%230074BF'/%3e%3cpath opacity='0.7' d='M28 24H24V28H28V24Z' fill='%230082BF'/%3e%3cpath opacity='0.8' d='M24 24H20V28H24V24Z' fill='%230082BF'/%3e%3cpath d='M20 24H16V28H20V24Z' fill='%230082BF'/%3e%3cpath d='M16 24H12V28H16V24Z' fill='%230082BF'/%3e%3cpath opacity='0.8' d='M12 24H8V28H12V24Z' fill='%230082BF'/%3e%3cpath opacity='0.7' d='M8 24H4V28H8V24Z' fill='%230082BF'/%3e%3cpath opacity='0.5' d='M32 20H28V24H32V20Z' fill='%23008FBF'/%3e%3cpath opacity='0.8' d='M28 20H24V24H28V20Z' fill='%23008FBF'/%3e%3cpath d='M24 20H20V24H24V20Z' fill='%23008FBF'/%3e%3cpath d='M20 20H16V24H20V20Z' fill='%23008FBF'/%3e%3cpath d='M16 20H12V24H16V20Z' fill='%23008FBF'/%3e%3cpath d='M12 20H8V24H12V20Z' fill='%23008FBF'/%3e%3cpath opacity='0.8' d='M8 20H4V24H8V20Z' fill='%23008FBF'/%3e%3cpath opacity='0.5' d='M4 20H0V24H4V20Z' fill='%23008FBF'/%3e%3cpath opacity='0.7' d='M32 16H28V20H32V16Z' fill='%23009DBF'/%3e%3cpath d='M28 16H24V20H28V16Z' fill='%23009DBF'/%3e%3cpath d='M24 16H20V20H24V16Z' fill='%23009DBF'/%3e%3cpath d='M20 16H16V20H20V16Z' fill='%23009DBF'/%3e%3cpath d='M16 16H12V20H16V16Z' fill='%23009DBF'/%3e%3cpath d='M12 16H8V20H12V16Z' fill='%23009DBF'/%3e%3cpath d='M8 16H4V20H8V16Z' fill='%23009DBF'/%3e%3cpath opacity='0.7' d='M4 16H0V20H4V16Z' fill='%23009DBF'/%3e%3cpath opacity='0.7' d='M32 12H28V16H32V12Z' fill='%2300ABBF'/%3e%3cpath d='M28 12H24V16H28V12Z' fill='%2300ABBF'/%3e%3cpath d='M24 12H20V16H24V12Z' fill='%2300ABBF'/%3e%3cpath d='M20 12H16V16H20V12Z' fill='%2300ABBF'/%3e%3cpath d='M16 12H12V16H16V12Z' fill='%2300ABBF'/%3e%3cpath d='M12 12H8V16H12V12Z' fill='%2300ABBF'/%3e%3cpath d='M8 12H4V16H8V12Z' fill='%2300ABBF'/%3e%3cpath opacity='0.7' d='M4 12H0V16H4V12Z' fill='%2300ABBF'/%3e%3cpath opacity='0.5' d='M32 8H28V12H32V8Z' fill='%2300B9BF'/%3e%3cpath opacity='0.8' d='M28 8H24V12H28V8Z' fill='%2300B9BF'/%3e%3cpath d='M24 8H20V12H24V8Z' fill='%2300B9BF'/%3e%3cpath d='M20 8H16V12H20V8Z' fill='%2300B9BF'/%3e%3cpath d='M16 8H12V12H16V8Z' fill='%2300B9BF'/%3e%3cpath d='M12 8H8V12H12V8Z' fill='%2300B9BF'/%3e%3cpath opacity='0.8' d='M8 8H4V12H8V8Z' fill='%2300B9BF'/%3e%3cpath opacity='0.5' d='M4 8H0V12H4V8Z' fill='%2300B9BF'/%3e%3cpath opacity='0.7' d='M28 4H24V8H28V4Z' fill='%2300C6BF'/%3e%3cpath opacity='0.8' d='M24 4H20V8H24V4Z' fill='%2300C6BF'/%3e%3cpath d='M20 4H16V8H20V4Z' fill='%2300C6BF'/%3e%3cpath d='M16 4H12V8H16V4Z' fill='%2300C6BF'/%3e%3cpath opacity='0.8' d='M12 4H8V8H12V4Z' fill='%2300C6BF'/%3e%3cpath opacity='0.7' d='M8 4H4V8H8V4Z' fill='%2300C6BF'/%3e%3cpath opacity='0.5' d='M24 0H20V4H24V0Z' fill='%2300D4BF'/%3e%3cpath opacity='0.7' d='M20 0H16V4H20V0Z' fill='%2300D4BF'/%3e%3cpath opacity='0.7' d='M16 0H12V4H16V0Z' fill='%2300D4BF'/%3e%3cpath opacity='0.5' d='M12 0H8V4H12V0Z' fill='%2300D4BF'/%3e%3cpath d='M10.5141 14.9697L11.6379 12.4572C12.0459 11.8129 11.9958 11.0255 11.5449 10.5745C11.4876 10.5173 11.416 10.46 11.3444 10.4171C11.0366 10.2238 10.6572 10.1808 10.3065 10.2954C9.91993 10.4171 9.58349 10.6748 9.36875 11.0184C9.36875 11.0184 7.82972 14.6046 7.26421 16.2153C6.69871 17.8259 6.92062 20.7822 9.12536 22.987C11.4661 25.3277 14.8448 25.8575 17.0066 24.2397C17.0997 24.1967 17.1784 24.1395 17.2572 24.0751L23.9072 18.5202C24.2293 18.2554 24.7089 17.7042 24.2794 17.0743C23.8642 16.4586 23.0697 16.881 22.7404 17.0886L18.9107 19.8731C18.8391 19.9304 18.7318 19.9232 18.6673 19.8517C18.6673 19.8517 18.6673 19.8445 18.6602 19.8445C18.56 19.7228 18.5456 19.4079 18.696 19.2862L24.5657 14.304C25.074 13.8459 25.1456 13.1802 24.7304 12.7292C24.3295 12.2854 23.6924 12.2997 23.1842 12.7578L17.9157 16.881C17.8155 16.9597 17.6652 16.9454 17.5864 16.8452L17.5793 16.838C17.4719 16.7235 17.4361 16.5231 17.5506 16.4014L23.535 10.596C24.0074 10.1522 24.036 9.4149 23.5922 8.94245C23.3775 8.72054 23.084 8.59169 22.7762 8.59169C22.4612 8.59169 22.1606 8.70623 21.9387 8.92813L15.8255 14.6691C15.6823 14.8122 15.396 14.6691 15.3602 14.4973C15.3459 14.4328 15.3674 14.3684 15.4103 14.3255L20.0918 8.99972C20.5571 8.56306 20.5858 7.83292 20.1491 7.36763C19.7124 6.90234 18.9823 6.87371 18.517 7.31036C18.4955 7.32468 18.4812 7.34615 18.4597 7.36763L11.3659 15.2203C11.1082 15.478 10.736 15.4851 10.557 15.342C10.4425 15.2489 10.4282 15.0843 10.5141 14.9697Z' fill='white'/%3e%3c/svg%3e",i="https://newassets.hcaptcha.com/captcha/v1/096d3a6/static/images"+(this.charity?"/icon-charity":"/icon")+".png";this.color=this.initComponent(Bi,{selector:".logo",src:e,width:32,fallback:i,autoLoad:!1})}function to(t){Jt.self(this,Yt,"hcaptcha-logo"),t||(t={}),this.state={label:"hCaptcha"},this.mobile=!1,this.link="https://www.hcaptcha.com/what-is-hcaptcha-about?ref="+ct.host+"&utm_campaign="+ct.sitekey+"&utm_medium=challenge",this.icon=this.initComponent(Qn,!!t.charity),this.onClick=this.onClick.bind(this),this.addEventListener("click",this.onClick)}Jt.proto(Qn,Yt),Qn.prototype.load=function(){this.color.load()},Qn.prototype.style=function(t){this.mobile=t;var e=32;return this.css({width:e,height:e,position:"absolute",top:0,left:0}),this.color.css({"-ms-high-contrast-adjust":"none",width:e,height:e,position:"absolute",top:0,left:0,zIndex:5}),{width:e,height:e}},Jt.proto(to,Yt),to.prototype.load=function(){this.icon.load()},to.prototype.style=function(t,e){t!==undefined&&(this.mobile=t);var i=this.icon.style(this.mobile);this.css({display:e?"block":"none",width:i.width,height:i.height,left:"50%",marginLeft:-i.width/2,cursor:"pointer",position:"absolute"}),this.height=i.height},to.prototype.onClick=function(){null!==this.fireEvent&&li(this.link)},to.prototype.setLabel=function(){var t=this.state.label;this.setAttribute("title",t),this.setAttribute("aria-label",t)};var eo=[{text:"Accessibility",value:"accessibility",selector:"#accessibility",type:"modal"},{text:"Report Image",value:"report_image",selector:"#report_image",type:"custom",warn:!0},{text:"Report Bug",value:"report_bug",selector:"#report_bug",type:"modal"},{text:"Information",value:"info",selector:"#info",type:"modal"}];function io(){Jt.self(this,en,{isMenu:!0,theme:_i,selector:"#menu",optionsVisible:-1}),this.state.a11yChallenge=!1,this.options=[],this.on("select",(function(t){t&&("link"===t.type?li(t.value):"modal"===t.type?this.emit("display",t.value):"challenge"===t.type&&("text_challenge"===t.value&&(ct.a11y_tfe=!0,this.emit("refresh")),"visual_challenge"===t.value&&(ct.a11y_tfe=!1,this.emit("refresh"))))}))}function no(){Jt.self(this,Yt,"interface-user");var t=this;this.state={isRq:!1,loaded:!1,locked:!1,visible:!1,whiteLabel:!1},this.language=this.initComponent(Xn),this.menu=this.initComponent(Un),this.menuList=this.initComponent(io),this.refresh=this.initComponent(Wn),this.logo=this.initComponent(to),this.menu.controlsMenu(this.menuList),this.menu.on("click",(function(e){t.menuList.usingKb(e.usingKb),t.menuList.visible(e.selected,t.state.isRq)})),this.menuList.on("hide",(function(){t.menu.reset()})),this.refresh.on("click",(function(){t.emit("refresh")})),this.menuList.on("select",(function(e){e&&"report_image"===e.value&&t.emit("report")})),this.menuList.on("refresh",(function(){t.refresh.dom.click()})),this.menuList.on("display",(function(e){t.emit("display",e)}))}Jt.proto(io,en),io.prototype.setA11yChallenge=function(t){this.state.a11yChallenge=t},io.prototype._setOptions=function(t){var e;for(this.options=[],e=0;e1&&(a.breadcrumbs.createCrumbs(oo.breadcrumbs),a.breadcrumbs.setIndex(oo.served))}catch(Rs){r.isMounted||(oo=null),n({event:it.CHALLENGE_ERROR,message:"Creating challenge failed.",reason:Rs.toString()})}}))},i.show=function(e){if(!o.create)return Promise.reject(new Error(et.CHALLENGE_ALREADY_CLOSED));o.visible=!0,t.removeAttribute("aria-hidden"),Ue.resetData(),Ue.record(!0,!0,!0,!1),Ue.setData("dct",Date.now());var n=i.setup(e.bundle,e.bundleData),s=i.style(e.width,e.height).then((function(n){o.visible&&(a.setLock(!1),l.setLock(!1),ri.contact("challenge-ready",n).then((function(){var n="info"===o.focus,s=e.challengeType.indexOf("text")>=0,r=t.hasClass("using-kb");ct.a11y_tfe||!n||!r&&s?i.focus():(l.menu.focus(!r),o.focus="challenge")})))}));return new Promise((function(t,i){s["catch"](i),n.then(t,i),o.timer&&clearTimeout(o.timer),o.timer=setTimeout((function(){o.timerExpired=!0,o.preventClose||i({event:et.CHALLENGE_EXPIRED})}),e.expiration)}))},i.style=function(e,i){return oo?new Promise((function(n,o){try{oo.style(e,i).then((function(e){var i=10,o=e.mobile?60:70,h=e.width,u=e.height+i+o;r.style(e.width,e.height,i),"landscape"===ct.orientation&&"image_label_binary"===ct.challenge_type?(o=35,h=e.width+o+i,u=e.height+o+i,a.style(e.width,o),a.css({position:"absolute",right:0,bottom:0}),l.style(o,u),l.css({position:"absolute",left:0,bottom:0}),r.css({position:"absolute",top:0,left:o+i})):(a.style(e.width,o),a.css({position:"absolute",left:0,bottom:0}),l.style(e.width,o),l.css({position:"absolute",left:0,bottom:0}),r.css({position:"relative",marginBottom:i,right:"auto"})),s.css({width:h,height:u,margin:i,position:"relative"}),t.css({width:h+20,height:u+20}),c.style(h,u,e.mobile),c.load(),n({width:h+20,height:u+20,mobile:e.mobile})}))["catch"]((function(t){o({event:it.CHALLENGE_ERROR,message:"Error occurred in promise of .style()",reason:t.toString()})}))}catch(Rs){o({event:it.CHALLENGE_ERROR,message:"Error when calling .style()",reason:Rs.toString()})}})):Promise.resolve({width:0,height:0,mobile:!1})},i.submit=function(){return i.hasBreadcrumbs()&&i.getTotalServed()!==i.getTotalBreadcrumbs()||"skip"!==a.getAction()?new Promise((function(t,e){try{if(oo&&oo.submit(),i.hasBreadcrumbs()){var n=i.getTotalServed();a.breadcrumbs.setIndex(n)}t("challenge-complete"),null!==o._timer&&"check"===a.getAction()&&(clearTimeout(o._timer),o._timer=null)}catch(Rs){e(Rs)}})):Promise.resolve("challenge-skip")},i.displayReport=function(t){return new Promise((function(e,n){try{if(!i.isMounted())return e();if(!i.canReport()){var o;if("fallback"===t.request_type)o=t.key;else{var s=i.hasBreadcrumbs()?i.getTotalServed()-1:0;o=t.tasklist[s].task_key}return e(o)}i.report().then(e),a.breadcrumbs&&a.breadcrumbs.hide()}catch(Ps){n(Ps)}}))},i.hideReport=function(){oo&&oo.report&&oo.report(!1)},i.close=function(){oo&&(oo=r.unmount(oo)),o.timer&&clearTimeout(o.timer),o.timer=null,t.setAttribute("aria-hidden",!0),a.displayTryAgain(!1),a.removeCrumbs(),c.close(),o.visible=!1,o.create=!1},i.translateInterface=function(t){if(t&&t.locale&&t.table)try{t.table&&(se.setLocale(t.locale),se.addTable(t.locale,t.table)),oo&&oo.translate&&oo.translate(),a.translate(),l.translate(),document.documentElement.setAttribute("lang",se.getLocale())}catch(Rs){xt("translation",Rs)}},i.translateBundle=function(){oo&&oo.translate&&oo.translate()},i.isVisible=function(){return o.visible},i.setFocus=function(t){o.focus=t},i.triggerFocus=function(t,e){"submit"===t?a.submit.focus():i.focus(e)},i.isInterfaceLocked=function(){return o.locked},i.lockInterface=function(t){o.locked=t,a.setLock(t),l.setLock(t)},i.hasActiveElement=function(){return document.activeElement===a.submit.dom||document.activeElement===l.refresh.dom||document.activeElement===l.menu.dom},i.getActiveElement=function(){return document.activeElement===a.submit.dom?"submit":document.activeElement===l.refresh.dom?"refresh":document.activeElement===l.menu.dom?"menu":null},i.getModal=function(){return c},i.getTotalServed=function(){return oo.served},i.getTotalBreadcrumbs=function(){return oo?oo.breadcrumbs:0},i.hasBreadcrumbs=function(){return!(!oo||!oo.breadcrumbs)},i.canReport=function(){return oo.report&&"function"==typeof oo.report},i.report=function(){return new Promise((function(t){var e=function(i){oo.off("report-image",e),t(i)};oo.report(!0),oo.on("report-image",e)}))},i.focus=function(t){oo&&oo.setFocus&&oo.setFocus(t||0,o.interaction)},i.displayTryAgain=function(t){a.displayTryAgain(t)},i.enableA11yChallenge=function(t){l.menuList.setA11yChallenge(t)},i}var ro=Object.create(null),ao=null,lo=null,co=[],ho=null;function uo(t){if(Array.isArray(t)&&0!==t.length){t.forEach((function(t){-1===co.indexOf(t)&&co.push(t)}))}}var po=function(t){if(Ct("Set spec","proof","info",t),t){ao=t,lo=null;try{yo(lo=At.decode(t.req))["catch"]((function(){}))}catch(Rs){xt("proof",Rs)}}},fo=function(t){t&&(ho=t)},mo=function(){return new Promise((function(t){var e=ao,i=lo;if(e)try{if(Ct("Solve Proof","proof","info",e),!(-1!==["hsw","hsj","hsl"].indexOf(e.type)&&(!("n"in i.payload)||i.payload.n===e.type)))return wt("Asset script invalid file","error","proof",{seen:e.type,wanted:i.n}),void t({solved:null,spec:e});Promise.resolve().then((function(){return yo(i)})).then((function(t){if("function"!=typeof t)return Promise.reject(new Error("Script is not a function"));var i={assethost:ut.assethost,fetchAsset:function(t){return _e.retrieve(t).then((function(e){return e||_e.file(t,{responseType:"arraybuffer"})})).then((function(t){return t.data}))},href:ho};return co.length&&(i.errors=co),si.messages.length&&(i.messages=si.messages),t(e.req,i)})).then((function(i){t({solved:i,spec:e})}))["catch"]((function(i){"string"==typeof i&&-1!==i.indexOf("http")?wt("Asset Script Failed","error","proof",{error:i}):xt("proof",i),t({solved:"fail",spec:e})}))}catch(Rs){xt("proof",Rs),t({solved:null,spec:e})}else t({solved:null,spec:null})}))};function yo(t){var e=t.payload.l,i=t.payload.i,n=t.payload.n,o=ro[n];if(o&&o.location===e)return o.promise;var s=e+"/"+n+".js",r=_e.script(s,{integrity:i}).then((function(){var t=window[n];try{t("IiI=.eyJzIjowLCJmIjowLCJjIjowfQ==.")["catch"]((function(){}))}catch(Ps){}return t}));return ro[n]={location:e,promise:r},r}var go=null,vo=null;function bo(t){vo&&(vo.e=Date.now()-vo.s,vo.r=t,go=vo)}function wo(){try{return Object.keys(window).sort().join(",")}catch(Ps){return null}}var xo,Co,ko,_o,Eo,So,Ao,Lo=null,Bo=null,Ho=null,To=null,Mo={},Oo=null,Vo=null,Ro=!1,Po={logAction:function(t){Oo=t},getTaskData:function(t,e,i,n){t===undefined&&(t={});var o=e.proof,s={v:"096d3a6",sitekey:ct.sitekey,host:ct.host,hl:se.getLocale()};return ut.se&&(s.se=ut.se),!0===ct.a11y_tfe&&(s.a11y_tfe=!0),null!==Oo&&(s.action=Oo,Oo=null),null!==To&&(s.extraData=JSON.stringify(To),To=null),t&&(s.motionData=JSON.stringify(t)),i&&(s.pd=JSON.stringify(i)),n&&(s.pdc=JSON.stringify(n)),null!==Lo&&(Bo=Lo,s.old_ekey=Lo),null!==Vo&&(s.rqdata=Vo),o&&(s.n=o.solved||null,s.c=o.spec?JSON.stringify(o.spec):null),e.authToken&&(s.auth_token=e.authToken),e.hasPst!==undefined&&(s.pst=e.hasPst),new Promise((function(t,i){try{pe({url:ut.endpoint+"/getcaptcha/"+s.sitekey,data:s,dataType:"query",responseType:"json",withCredentials:!0,pst:e.hasPst?"send-redemption-record":null,headers:{Accept:"application/json","Content-type":"application/x-www-form-urlencoded"}}).then((function(e){var i=e.body||null;if(!i)throw new Error("Missing response body.");if(!1===i.success)return-1!==(i["error-codes"]||[]).indexOf("invalid-data")&&wt("invalid-data","error","api",{motionData:s.motionData}),void t(i);Po.setData(i),t(i)}))["catch"](i)}catch(Ps){i(Ps)}}))},loadBundle:function(t){return new Promise((function(e,i){if(Mo[t])e(Mo[t]);else{var n=Po.createBundleUrl(t);_e.script(n).then((function(){Mo[t]=window[t],e(Mo[t])}))["catch"]((function(t){i({event:it.BUNDLE_ERROR,message:"Failed to get challenge bundle.",reason:t})}))}}))},createBundleUrl:function(t){return(ut.assethost||ct.assetDomain)+"/captcha/challenge/"+t+"/096d3a6/challenge.js"},checkAnswers:function(t,e,i){var n={v:"096d3a6",job_mode:To.request_type,answers:t,serverdomain:ct.host,sitekey:ct.sitekey,motionData:JSON.stringify(e)};return ut.se&&(n.se=ut.se),i&&(n.n=i.solved,n.c=JSON.stringify(i.spec)),new Promise((function(t,e){try{pe({url:ut.endpoint+"/checkcaptcha/"+n.sitekey+"/"+To.key,data:n,dataType:"json",responseType:"json",withCredentials:!0,headers:{"Content-type":"application/json;charset=UTF-8"}}).then((function(e){var i=e.body||null;if(!i)throw new Error("Missing response body.");if(!1===i.success){var o=i["error-codes"]||[""];-1!==o.indexOf("invalid-data")&&wt("invalid-data","error","api",{motionData:n.motionData});var s=o.join(", ");throw new Error(s)}t(i)}))["catch"](e)}catch(Ps){e(Ps)}}))},reportIssue:function(t,e,i){var n={taskdata:To,on_url:ct.url,report_category:t,sid:Ho};if(e&&(n.user_comments=e),Bo&&(n.last_ekey=Bo),i&&To&&"fallback"!==To.request_type){for(var o=To.tasklist,s=null,r=-1;++r=10)return;var e=t.stack;if("string"!=typeof e)return;var i=e.trim().split("\n");"Error"===i[0]&&(i=i.slice(1));var n=gt(i=i.slice(-2));n&&-1===xo.indexOf(n)&&xo.push(n)}catch(t){return}},So=function(){if(ko)try{for(var t=0;t<_o.length;t++)_o[t]();null!==Co&&clearTimeout(Co)}catch(e){Eo(e)}finally{_o=[],Co=null,ko=!1}},Ao=function(t,e){var i=Object.getOwnPropertyDescriptor(t,e);if(!i||!1!==i.writable){var n=Object.prototype.hasOwnProperty.call(t,e),o=t[e];t[e]=function(){return ko&&(xo.length>=10&&So(),Eo(new Error)),o.apply(t,arguments)},_o.push((function(){n?t[e]=o:delete t[e]}))}},{run:function(t){if(!ko){ko=!0,isFinite(t)&&(Co=setTimeout((function(){So()}),t));try{Ao(document,"getElementsByClassName"),Ao(document,"getElementById"),Ao(document,"querySelector"),Ao(document,"querySelectorAll")}catch(e){So(),Eo(e)}}},collect:function(){return xo.concat(yt)}});function Do(t){if("en"===t)return Promise.resolve();var e=t+".json";return new Promise((function(i,n){_e.retrieve(e).then((function(i){return i||_e.file(e,{prefix:"https://newassets.hcaptcha.com/captcha/v1/096d3a6/static/i18n"}).then((function(e){return se.addTable(t,e.data),e}))})).then((function(t){i(t.data)}))["catch"]((function(t){n(t)}))}))}var Io=["10000000-ffff-ffff-ffff-000000000001","20000000-ffff-ffff-ffff-000000000002","30000000-ffff-ffff-ffff-000000000003"],$o={sitekey:function(t){return It.UUIDv4(t)||"00000000-0000-0000-0000-000000000000"===t||-1!==Io.indexOf(t)},dummykey:function(t){return-1!==Io.indexOf(t)}};function jo(){var t,e,i,n,o,s,r=$o.dummykey(ct.sitekey);if("localhost"===ct.host&&!r){var a="Warning: localhost detected. Please use a valid host.";return console.error(a),Promise.reject(new Error(a))}return(t=ct.sitekey,e=ct.host,i={attempts:3,delay:5e3,onFail:function(t){return Ct("challenge","api","debug",t),t&&0===t.status&&-1!==at.indexOf(ut.endpoint)?(ut.endpoint=ot,!0):(wt("api:checksiteconfig failed","error","challenge",{error:t}),t instanceof Error||400===t.status)}},n=tt.Browser.supportsCanvas()>>>0,o=tt.Browser.supportsWebAssembly()>>>0,s=tt.Browser.supportsPST()>>>0,new Promise((function(r,a){var l={v:"096d3a6",host:e,sitekey:t,sc:n,swa:o,spst:s};ut.se&&(l.se=ut.se),pe({url:function(){var e=ut.endpoint;return e===nt&&("78c843a4-f80d-4a14-b3e5-74b492762487"===t||Math.random()<.2)&&(e=st),e+"/checksiteconfig?"+Ht(l)},responseType:"json",withCredentials:!0,timeout:5e3,headers:{Accept:"application/json","Content-Type":"text/plain"},retry:i}).then((function(t){var e=t.body||null;if(e)if(!1===e.success){var i=(e["error-codes"]||[]).join(", ");a(new Error(i))}else!e.pass&&e.error?a(new Error(e.error)):r(e);else a(new Error("Missing response body."))}))["catch"](a)}))).then((function(t){return Ct("/checksiteconfig success","request","info",t),ut.endpoint===nt&&t.endpoint?(ut.endpoint=t.endpoint,jo()):(t.endpoint&&-1!==at.indexOf(ut.endpoint)&&(ut.endpoint=t.endpoint),delete t.endpoint,t)}))}var No=new Ci;No.add("contrast",{}),No.add("grey-red",{component:{checkbox:{main:{border:"#6a6a6a"}}}});function zo(){Jt.self(this,Yt,"#a11y-label"),this.state={ticked:!1},this.setAttribute("aria-hidden",!0),this.css({display:"none"}),this.translate()}function Zo(t){var e=t.get(),i=e.palette,n=e.component,o="light"===i.mode;return Ci.merge({main:{fill:i.grey[100],border:i.grey[o?600:200]},focus:{fill:i.grey[200],border:i.grey[o?800:100],outline:"dark"===t.active()?i.secondary.main:i.primary.main}},n.input)}function Uo(){Jt.self(this,Yt,"#checkbox"),this.state={focused:!1,visible:!0,passed:!1},this._style=Zo(No),this.setAttribute("aria-haspopup",!0),this.setAttribute("aria-checked",!1),this.setAttribute("role","checkbox"),this.setAttribute("tabindex","0"),this.setAttribute("aria-live","assertive"),this.setAttribute("aria-labelledby","a11y-label"),this.onOver=this.onOver.bind(this),this.onOut=this.onOut.bind(this),this.onFocus=this.onFocus.bind(this),this.onBlur=this.onBlur.bind(this),this.addEventListener("over",this.onOver),this.addEventListener("out",this.onOut),this.addEventListener("focus",this.onFocus),this.addEventListener("blur",this.onBlur)}Jt.proto(zo,Yt),zo.prototype.setState=function(t){this.state.ticked="passed"===t,this.translate()},zo.prototype.translate=function(){var t=this.state.ticked?"hCaptcha checkbox. You are verified":"hCaptcha checkbox. Select in order to trigger the challenge, or to bypass it if you have an accessibility cookie.";this.content(se.translate(t))},Jt.proto(Uo,Yt),Uo.prototype.style=function(){this._style=Zo(No);var t=this.state.visible?this._style.main.fill:"transparent",e=this.state.focused?this._style.focus.border:this._style.main.border,i=this.state.visible?e:"transparent";this.css({position:"absolute",width:28,height:28,borderWidth:"1px",borderStyle:"solid",borderColor:i,borderRadius:4,backgroundColor:t,outlineColor:this._style.focus.outline,top:0,left:0})},Uo.prototype.onOver=function(t){this.state.focused||(this.state.focused="focus"===t.action,this.css({borderColor:this._style.focus.border}))},Uo.prototype.onOut=function(t){if("blur"===t.action)this.state.focused=!1;else if(this.state.focused)return;this.css({borderColor:this._style.main.border})},Uo.prototype.onFocus=function(t){var e=this._style.focus.outline;this.css({outline:"2px solid "+e})},Uo.prototype.onBlur=function(t){this.css({outline:"none"})},Uo.prototype.display=function(t){this.state.visible=t,this.setAttribute("tabindex",t?0:-1),this.style()},Uo.prototype.setState=function(t){this.state.passed="passed"===t,this.state.visible="loading"!==t&&"passed"!==t,this.setAttribute("tabindex","loading"===t||"solving"===t?-1:0),this.setAttribute("aria-checked",this.state.passed),this.style()};function Wo(){Jt.self(this,Bi,{selector:".pulse",src:"data:image/svg+xml,%3c%3fxml version='1.0' encoding='utf-8'%3f%3e%3c!-- Generator: Adobe Illustrator 21.0.2%2c SVG Export Plug-In . SVG Version: 6.00 Build 0) --%3e%3csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 44 44' style='enable-background:new 0 0 44 44%3b' xml:space='preserve'%3e%3cstyle type='text/css'%3e .st0%7bfill:none%3bstroke:%23FF7B00%3bstroke-width:2%3b%7d%3c/style%3e%3cg%3e %3ccircle class='st0' cx='22' cy='22' r='1'%3e %3canimate accumulate='none' additive='replace' attributeName='r' begin='0s' calcMode='spline' dur='1.8s' fill='remove' keySplines='0.165%2c 0.84%2c 0.44%2c 1' keyTimes='0%3b 1' repeatCount='indefinite' restart='always' values='1%3b 20'%3e %3c/animate%3e %3canimate accumulate='none' additive='replace' attributeName='stroke-opacity' begin='0s' calcMode='spline' dur='1.8s' fill='remove' keySplines='0.3%2c 0.61%2c 0.355%2c 1' keyTimes='0%3b 1' repeatCount='indefinite' restart='always' values='1%3b 0'%3e %3c/animate%3e %3c/circle%3e %3ccircle class='st0' cx='22' cy='22' r='1'%3e %3canimate accumulate='none' additive='replace' attributeName='r' begin='-0.9s' calcMode='spline' dur='1.8s' fill='remove' keySplines='0.165%2c 0.84%2c 0.44%2c 1' keyTimes='0%3b 1' repeatCount='indefinite' restart='always' values='1%3b 20'%3e %3c/animate%3e %3canimate accumulate='none' additive='replace' attributeName='stroke-opacity' begin='-0.9s' calcMode='spline' dur='1.8s' fill='remove' keySplines='0.3%2c 0.61%2c 0.355%2c 1' keyTimes='0%3b 1' repeatCount='indefinite' restart='always' values='1%3b 0'%3e %3c/animate%3e %3c/circle%3e%3c/g%3e%3c/svg%3e",width:30,fallback:"https://newassets.hcaptcha.com/captcha/v1/096d3a6/static/images/pulse.png"}),this.state={visible:!1}}Jt.proto(Wo,Bi),Wo.prototype.style=function(){this.size(),this.css({display:this.state.visible?"block":"none",position:"absolute",top:0,left:0})},Wo.prototype.display=function(t){this.state.visible=t,this.style()};function Ko(){Jt.self(this,Bi,{selector:".check",src:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABmJLR0QA/wD/AP+gvaeTAAAC00lEQVR4nO2aTU8TQRyHn39bIdXEm3jwLQhefPkAJorYLYslIF64ohwM8eQH0A/gzYSLIRooxBORKJr4Ultq4smz8YgQb3ow4YAmUHY8IEpgd7vQ3e0smee4+5/uPL+daXdmCwaDwWAwGAwGg8FgMBgM+wBr0u7JFe17QWrTUXcmbqxJuwdhTpDejsHO7Ne5hbJf/b4KYFMeJAuAcKleCPsmgB3ymwiX2m901BZfLHx0a5eKpXcR4ykPgPqdEvnk1Vai7Fgc1JMXkevlm+88p0CiA2hUHhIcQBjykNAAwpKHBAYQpjwkLICw5SFBAUQhDwkJICp5SEAAUcqD5gFELQ8aBxCHPGgaQFzyoGEAccpDwNXgxZmhLCr6sPJTvXk/eRSDYcpDgAAGxgcOZleW31hF+1GUIViTdo9S6qXfna+MlN6HfV3fAApjhdZfrauzInIFkdGoQoh72G/FM4ChmaGW1cPOM+Dav4MRhNBMefAJ4OfK8hjQv+OEyKhV7H0YRgjNmPPb8QxgndQDYMn1pHC30ZHQrDm/Hc8APoy8XVK1dDew6FrQwHTIFe0uRJ43a9hvpW7nc0/6TklmvQq0uxYoNV65VbqDoIJcMFe0uwR5DRxy+bBY5SHgg1B+On9SOZkqqNOuBQFD0E0edvEkuBFCeh7ocC2oE4KO8rCL9wLl4fK3tKOuAguuBT7fCbrKwx7WAvaEfcJJybyCTteCbSNBZ3nY42Ko+2nheKbmVOuFkJuyL+ssDw2sBnNT/cdErVWBMx4ls6D6/B5y4vidr0dDT3PWY+soBzLzwNngrfS485s09HK0crvynbVaDvgSrIVe8hDShsjfkVABznlX6ScPIe4I2dN2W82RisD5nWf1lIeQt8Tsabtt3aEMcuH/UX3lIeQ/SJSGSz9anLQF6vPGEb3lIaJN0cJE4ciaOK9IcV9n+WiJYRPVYDAYDAaDoRH+ALzfixyrasnFAAAAAElFTkSuQmCC",width:30}),this.state={visible:!1}}function qo(){Jt.self(this,Yt,"#anchor-wr"),this.state={loading:!1,checked:!1};var t=this.createElement("#anchor-td"),e=t.createElement("#anchor-tc"),i=e.createElement("#anchor-state");this.a11y=this.initComponent(zo),this.input=this.initComponent(Uo,null,i),this.loading=this.initComponent(Wo,null,i),this.checked=this.initComponent(Ko,null,i),this.table=t,this.cell=e,this.wrapper=i}Jt.proto(Ko,Bi),Ko.prototype.style=function(){this.size(),this.css({display:this.state.visible?"block":"none",position:"absolute",top:0,left:0,animation:this.state.visible?"pop 0.4s linear":"auto"})},Ko.prototype.display=function(t){this.state.visible=t,this.style()},Jt.proto(qo,Yt),qo.prototype.style=function(t){var e=t?60:"100%",i=t?"0px 12px":"0px 15px";this.css({position:"relative",display:"inline-block",height:e,"-ms-high-contrast-adjust":"none"}),this.table.css({position:"relative",display:"table",top:0,height:"100%"}),this.cell.css({display:"table-cell",verticalAlign:"middle"});this.wrapper.css({position:"relative",width:30,height:30,margin:i}),this.input.style(),this.loading.style(),this.checked.style()},qo.prototype.describeBy=function(t){t&&t.dom&&t.dom.id?this.input.setAttribute("aria-describedby",t.dom.id):this.input.removeAttribute("aria-describedby")},qo.prototype.setState=function(t){var e="loading"===t,i="passed"===t;this.checked.display(i),this.loading.display(e),this.a11y.setState(t),this.input.setState(t),this.state.loading=e,this.state.checked=i},qo.prototype.focus=function(){this.input.focus()},qo.prototype.getLocation=function(){var t=this.input.dom.getBoundingClientRect(),e=t.bottom-t.top,i=t.right-t.left;return{left:t.left,right:t.right,top:t.top,bottom:t.bottom,width:i,height:e,x:t.left+i/2,y:t.top+e/2}},qo.prototype.translate=function(){this.a11y.translate()};function Go(){Jt.self(this,Yt,"label-container"),this.table=this.createElement("label-td"),this.cell=this.table.createElement("label-tc"),this.text=this.cell.createElement("#label"),this.translate()}Jt.proto(Go,Yt),Go.prototype.style=function(t){var e=t?60:"100%",i=t?100:170,n=No.get().palette;this.css({position:"relative",display:"inline-block",height:e,width:i}),this.table.css({position:"relative",display:"table",top:0,height:"100%"}),this.cell.css({display:"table-cell",verticalAlign:"middle"}),this.text.css({color:n.text.body,fontSize:14})},Go.prototype.translate=function(){var t=se.translate("I am human");this.text.content(t)};var Jo="Privacy",Yo="https://hcaptcha.com/privacy",Xo="hCaptcha Privacy Policy",Qo="Terms",ts="https://hcaptcha.com/terms",es="hCaptcha Terms of Service";function is(t){Jt.self(this,Yt,"anchor-links"),this.state={theme:t.theme,size:t.size},this.privacy=this.initComponent(Si,{theme:No,text:Jo,url:(t.privacyUrl||Yo)+"?ref="+ct.host+"&utm_campaign="+ct.sitekey+"&utm_medium=checkbox"}),this.hyphen=this.initComponent(Ai,{text:" - "}),this.terms=this.initComponent(Si,{theme:No,text:Qo,url:(t.termsUrl||ts)+"?ref="+ct.host+"&utm_campaign="+ct.sitekey+"&utm_medium=checkbox"}),this.translate()}Jt.proto(is,Yt),is.prototype.style=function(){var t=function(t){var e=t.palette,i=t.component,n="light"===e.mode;return Ci.merge({main:e.grey[n?700:200]},i.link)}(No.get()),e={fontSize:8,color:t.main};this.privacy.style(e),this.hyphen.style(e),this.terms.style(e)},is.prototype.translate=function(){this.privacy.translate(),this.terms.translate(),this.privacy.setAttribute("aria-label",se.translate(Xo)),this.terms.setAttribute("aria-label",se.translate(es))};var ns="https://www.hcaptcha.com/what-is-hcaptcha-about",os="Visit hcaptcha.com to learn more about the service and its accessibility options.";function ss(t){Jt.self(this,Yt,"anchor-brand"),this.state={url:t.logoUrl||ns+"?ref="+ct.host+"&utm_campaign="+ct.sitekey+"&utm_medium=checkbox",theme:"dark"===t.theme?"dark":"light",display:t.displayLogo,label:"hCaptcha"};var e="light"===this.state.theme?"data:image/svg+xml,%3csvg width='44' height='46' viewBox='0 0 44 46' role='img' aria-hidden='true' fill='none' xmlns='http://www.w3.org/2000/svg'%3e%3cpath opacity='0.5' d='M30 28H26V32H30V28Z' fill='%230074BF'/%3e%3cpath opacity='0.7' d='M26 28H22V32H26V28Z' fill='%230074BF'/%3e%3cpath opacity='0.7' d='M22 28H18V32H22V28Z' fill='%230074BF'/%3e%3cpath opacity='0.5' d='M18 28H14V32H18V28Z' fill='%230074BF'/%3e%3cpath opacity='0.7' d='M34 24H30V28H34V24Z' fill='%230082BF'/%3e%3cpath opacity='0.8' d='M30 24H26V28H30V24Z' fill='%230082BF'/%3e%3cpath d='M26 24H22V28H26V24Z' fill='%230082BF'/%3e%3cpath d='M22 24H18V28H22V24Z' fill='%230082BF'/%3e%3cpath opacity='0.8' d='M18 24H14V28H18V24Z' fill='%230082BF'/%3e%3cpath opacity='0.7' d='M14 24H10V28H14V24Z' fill='%230082BF'/%3e%3cpath opacity='0.5' d='M38 20H34V24H38V20Z' fill='%23008FBF'/%3e%3cpath opacity='0.8' d='M34 20H30V24H34V20Z' fill='%23008FBF'/%3e%3cpath d='M30 20H26V24H30V20Z' fill='%23008FBF'/%3e%3cpath d='M26 20H22V24H26V20Z' fill='%23008FBF'/%3e%3cpath d='M22 20H18V24H22V20Z' fill='%23008FBF'/%3e%3cpath d='M18 20H14V24H18V20Z' fill='%23008FBF'/%3e%3cpath opacity='0.8' d='M14 20H10V24H14V20Z' fill='%23008FBF'/%3e%3cpath opacity='0.5' d='M10 20H6V24H10V20Z' fill='%23008FBF'/%3e%3cpath opacity='0.7' d='M38 16H34V20H38V16Z' fill='%23009DBF'/%3e%3cpath d='M34 16H30V20H34V16Z' fill='%23009DBF'/%3e%3cpath d='M30 16H26V20H30V16Z' fill='%23009DBF'/%3e%3cpath d='M26 16H22V20H26V16Z' fill='%23009DBF'/%3e%3cpath d='M22 16H18V20H22V16Z' fill='%23009DBF'/%3e%3cpath d='M18 16H14V20H18V16Z' fill='%23009DBF'/%3e%3cpath d='M14 16H10V20H14V16Z' fill='%23009DBF'/%3e%3cpath opacity='0.7' d='M10 16H6V20H10V16Z' fill='%23009DBF'/%3e%3cpath opacity='0.7' d='M38 12H34V16H38V12Z' fill='%2300ABBF'/%3e%3cpath d='M34 12H30V16H34V12Z' fill='%2300ABBF'/%3e%3cpath d='M30 12H26V16H30V12Z' fill='%2300ABBF'/%3e%3cpath d='M26 12H22V16H26V12Z' fill='%2300ABBF'/%3e%3cpath d='M22 12H18V16H22V12Z' fill='%2300ABBF'/%3e%3cpath d='M18 12H14V16H18V12Z' fill='%2300ABBF'/%3e%3cpath d='M14 12H10V16H14V12Z' fill='%2300ABBF'/%3e%3cpath opacity='0.7' d='M10 12H6V16H10V12Z' fill='%2300ABBF'/%3e%3cpath opacity='0.5' d='M38 8H34V12H38V8Z' fill='%2300B9BF'/%3e%3cpath opacity='0.8' d='M34 8H30V12H34V8Z' fill='%2300B9BF'/%3e%3cpath d='M30 8H26V12H30V8Z' fill='%2300B9BF'/%3e%3cpath d='M26 8H22V12H26V8Z' fill='%2300B9BF'/%3e%3cpath d='M22 8H18V12H22V8Z' fill='%2300B9BF'/%3e%3cpath d='M18 8H14V12H18V8Z' fill='%2300B9BF'/%3e%3cpath opacity='0.8' d='M14 8H10V12H14V8Z' fill='%2300B9BF'/%3e%3cpath opacity='0.5' d='M10 8H6V12H10V8Z' fill='%2300B9BF'/%3e%3cpath opacity='0.7' d='M34 4H30V8H34V4Z' fill='%2300C6BF'/%3e%3cpath opacity='0.8' d='M30 4H26V8H30V4Z' fill='%2300C6BF'/%3e%3cpath d='M26 4H22V8H26V4Z' fill='%2300C6BF'/%3e%3cpath d='M22 4H18V8H22V4Z' fill='%2300C6BF'/%3e%3cpath opacity='0.8' d='M18 4H14V8H18V4Z' fill='%2300C6BF'/%3e%3cpath opacity='0.7' d='M14 4H10V8H14V4Z' fill='%2300C6BF'/%3e%3cpath opacity='0.5' d='M30 0H26V4H30V0Z' fill='%2300D4BF'/%3e%3cpath opacity='0.7' d='M26 0H22V4H26V0Z' fill='%2300D4BF'/%3e%3cpath opacity='0.7' d='M22 0H18V4H22V0Z' fill='%2300D4BF'/%3e%3cpath opacity='0.5' d='M18 0H14V4H18V0Z' fill='%2300D4BF'/%3e%3cpath d='M16.5141 14.9697L17.6379 12.4572C18.0459 11.8129 17.9958 11.0255 17.5449 10.5745C17.4876 10.5173 17.416 10.46 17.3444 10.4171C17.0366 10.2238 16.6572 10.1808 16.3065 10.2954C15.9199 10.4171 15.5835 10.6748 15.3687 11.0184C15.3687 11.0184 13.8297 14.6046 13.2642 16.2153C12.6987 17.8259 12.9206 20.7822 15.1254 22.987C17.4661 25.3277 20.8448 25.8575 23.0066 24.2397C23.0997 24.1967 23.1784 24.1395 23.2572 24.0751L29.9072 18.5202C30.2293 18.2554 30.7089 17.7042 30.2794 17.0743C29.8642 16.4586 29.0697 16.881 28.7404 17.0886L24.9107 19.8731C24.8391 19.9304 24.7318 19.9232 24.6673 19.8517C24.6673 19.8517 24.6673 19.8445 24.6602 19.8445C24.56 19.7228 24.5456 19.4079 24.696 19.2862L30.5657 14.304C31.074 13.8459 31.1456 13.1802 30.7304 12.7292C30.3295 12.2854 29.6924 12.2997 29.1842 12.7578L23.9157 16.881C23.8155 16.9597 23.6652 16.9454 23.5864 16.8452L23.5793 16.838C23.4719 16.7235 23.4361 16.5231 23.5506 16.4014L29.535 10.596C30.0074 10.1522 30.036 9.4149 29.5922 8.94245C29.3775 8.72054 29.084 8.59169 28.7762 8.59169C28.4612 8.59169 28.1606 8.70623 27.9387 8.92813L21.8255 14.6691C21.6823 14.8122 21.396 14.6691 21.3602 14.4973C21.3459 14.4328 21.3674 14.3684 21.4103 14.3255L26.0918 8.99972C26.5571 8.56306 26.5858 7.83292 26.1491 7.36763C25.7124 6.90234 24.9823 6.87371 24.517 7.31036C24.4955 7.32468 24.4812 7.34615 24.4597 7.36763L17.3659 15.2203C17.1082 15.478 16.736 15.4851 16.557 15.342C16.4425 15.2489 16.4282 15.0843 16.5141 14.9697Z' fill='white'/%3e%3cpath d='M4.99195 43.6627H3.32946V40.8306C3.32946 40.1764 3.2488 39.6073 2.55423 39.6073C1.85966 39.6073 1.64905 40.2167 1.64905 41.0144V43.6627H0V36.112H1.64905V37.9045C1.64905 38.4512 1.64008 39.0427 1.64008 39.0427C1.89999 38.5632 2.38395 38.1689 3.13677 38.1689C4.61106 38.1689 4.99195 39.1637 4.99195 40.4766V43.6627Z' fill='%23555555'/%3e%3cpath d='M12.081 42.762C11.7181 43.1563 10.9652 43.7882 9.51337 43.7882C7.42069 43.7882 5.77612 42.3228 5.77612 39.8941C5.77612 37.4564 7.43861 36 9.50889 36C10.9742 36 11.7674 36.6453 11.9556 36.8514L11.4402 38.3167C11.3058 38.1285 10.544 37.5281 9.60299 37.5281C8.39757 37.5281 7.4655 38.3795 7.4655 39.8582C7.4655 41.337 8.43342 42.175 9.60299 42.175C10.4902 42.175 11.131 41.803 11.5209 41.3773L12.081 42.762Z' fill='%23555555'/%3e%3cpath d='M17.3016 43.6627H15.7242L15.6928 43.0936C15.4777 43.3221 15.0655 43.7837 14.2365 43.7837C13.3403 43.7837 12.3903 43.2684 12.3903 42.0674C12.3903 40.8665 13.4344 40.4587 14.3709 40.4139L15.6525 40.3601V40.2391C15.6525 39.67 15.2716 39.3743 14.6084 39.3743C13.9586 39.3743 13.3089 39.679 13.049 39.8538L12.6143 38.72C13.049 38.4915 13.8421 38.1733 14.7921 38.1733C15.7421 38.1733 16.2888 38.4019 16.6921 38.7962C17.082 39.1906 17.3016 39.7148 17.3016 40.6245V43.6627ZM15.657 41.2877L14.8414 41.3415C14.3351 41.3639 14.0348 41.5924 14.0348 41.9957C14.0348 42.4125 14.353 42.6634 14.8101 42.6634C15.2537 42.6634 15.5539 42.3587 15.657 42.1705V41.2877Z' fill='%23555555'/%3e%3cpath d='M21.6034 43.7792C20.8506 43.7792 20.3129 43.4835 19.9947 42.9816V45.6389H18.3456V38.2674H19.914L19.9051 38.9575H19.9275C20.2994 38.487 20.8461 38.1689 21.6213 38.1689C23.0867 38.1689 24.0142 39.3832 24.0142 40.9696C24.0142 42.5559 23.0777 43.7792 21.6034 43.7792ZM21.1284 39.549C20.4249 39.549 19.9409 40.1181 19.9409 40.9471C19.9409 41.7762 20.4249 42.3453 21.1284 42.3453C21.8409 42.3453 22.3249 41.7762 22.3249 40.9471C22.3249 40.1181 21.8409 39.549 21.1284 39.549Z' fill='%23555555'/%3e%3cpath d='M27.8321 39.6028H26.7074V41.5386C26.7074 42.0002 26.7701 42.1077 26.8508 42.2063C26.9225 42.296 27.0255 42.3363 27.2406 42.3363C27.4109 42.3318 27.5767 42.3004 27.738 42.2377L27.8187 43.6044C27.4378 43.7165 27.039 43.7747 26.6446 43.7792C26.0576 43.7792 25.6633 43.591 25.4079 43.2773C25.1524 42.9636 25.0449 42.511 25.0449 41.691V39.6028H24.3234V38.2809H25.0449V36.8156H26.7074V38.2809H27.8321V39.6028Z' fill='%23555555'/%3e%3cpath d='M32.7121 43.1339C32.6583 43.1787 32.1251 43.7792 30.7718 43.7792C29.3781 43.7792 28.0876 42.771 28.0876 40.9785C28.0876 39.1726 29.3961 38.1689 30.7897 38.1689C32.0892 38.1689 32.6762 38.738 32.6762 38.738L32.3133 40.0599C31.9458 39.7507 31.4843 39.5804 31.0048 39.5804C30.3013 39.5804 29.7456 40.0957 29.7456 40.9471C29.7456 41.7986 30.252 42.3363 31.0272 42.3363C31.8024 42.3363 32.3178 41.812 32.3178 41.812L32.7121 43.1339Z' fill='%23555555'/%3e%3cpath d='M38.3986 43.6627H36.7361V40.8306C36.7361 40.1764 36.6555 39.6073 35.9609 39.6073C35.2663 39.6073 35.0512 40.2212 35.0512 41.0188V43.6672H33.4067V36.112H35.0557V37.9045C35.0557 38.4512 35.0468 39.0427 35.0468 39.0427C35.3067 38.5632 35.7906 38.1689 36.5435 38.1689C38.0177 38.1689 38.3986 39.1637 38.3986 40.4766V43.6627Z' fill='%23555555'/%3e%3cpath d='M44 43.6627H42.4226L42.3913 43.0936C42.1762 43.3221 41.7639 43.7837 40.9349 43.7837C40.0387 43.7837 39.0887 43.2684 39.0887 42.0674C39.0887 40.8665 40.1328 40.4587 41.0693 40.4139L42.3509 40.3601V40.2391C42.3509 39.67 41.97 39.3743 41.3068 39.3743C40.6571 39.3743 40.0073 39.679 39.7474 39.8538L39.3127 38.7156C39.7474 38.487 40.5406 38.1689 41.4906 38.1689C42.4405 38.1689 42.9872 38.3974 43.3905 38.7917C43.7804 39.1861 44 39.7104 44 40.62V43.6627ZM42.3599 41.2877L41.5443 41.3415C41.038 41.3639 40.7377 41.5924 40.7377 41.9957C40.7377 42.4125 41.0559 42.6634 41.513 42.6634C41.9566 42.6634 42.2568 42.3587 42.3599 42.1705V41.2877V41.2877Z' fill='%23555555'/%3e%3c/svg%3e":"data:image/svg+xml,%3csvg width='44' height='46' viewBox='0 0 44 46' role='img' aria-hidden='true' fill='none' xmlns='http://www.w3.org/2000/svg'%3e%3cpath opacity='0.5' d='M30 28H26V32H30V28Z' fill='%230074BF'/%3e%3cpath opacity='0.7' d='M26 28H22V32H26V28Z' fill='%230074BF'/%3e%3cpath opacity='0.7' d='M22 28H18V32H22V28Z' fill='%230074BF'/%3e%3cpath opacity='0.5' d='M18 28H14V32H18V28Z' fill='%230074BF'/%3e%3cpath opacity='0.7' d='M34 24H30V28H34V24Z' fill='%230082BF'/%3e%3cpath opacity='0.8' d='M30 24H26V28H30V24Z' fill='%230082BF'/%3e%3cpath d='M26 24H22V28H26V24Z' fill='%230082BF'/%3e%3cpath d='M22 24H18V28H22V24Z' fill='%230082BF'/%3e%3cpath opacity='0.8' d='M18 24H14V28H18V24Z' fill='%230082BF'/%3e%3cpath opacity='0.7' d='M14 24H10V28H14V24Z' fill='%230082BF'/%3e%3cpath opacity='0.5' d='M38 20H34V24H38V20Z' fill='%23008FBF'/%3e%3cpath opacity='0.8' d='M34 20H30V24H34V20Z' fill='%23008FBF'/%3e%3cpath d='M30 20H26V24H30V20Z' fill='%23008FBF'/%3e%3cpath d='M26 20H22V24H26V20Z' fill='%23008FBF'/%3e%3cpath d='M22 20H18V24H22V20Z' fill='%23008FBF'/%3e%3cpath d='M18 20H14V24H18V20Z' fill='%23008FBF'/%3e%3cpath opacity='0.8' d='M14 20H10V24H14V20Z' fill='%23008FBF'/%3e%3cpath opacity='0.5' d='M10 20H6V24H10V20Z' fill='%23008FBF'/%3e%3cpath opacity='0.7' d='M38 16H34V20H38V16Z' fill='%23009DBF'/%3e%3cpath d='M34 16H30V20H34V16Z' fill='%23009DBF'/%3e%3cpath d='M30 16H26V20H30V16Z' fill='%23009DBF'/%3e%3cpath d='M26 16H22V20H26V16Z' fill='%23009DBF'/%3e%3cpath d='M22 16H18V20H22V16Z' fill='%23009DBF'/%3e%3cpath d='M18 16H14V20H18V16Z' fill='%23009DBF'/%3e%3cpath d='M14 16H10V20H14V16Z' fill='%23009DBF'/%3e%3cpath opacity='0.7' d='M10 16H6V20H10V16Z' fill='%23009DBF'/%3e%3cpath opacity='0.7' d='M38 12H34V16H38V12Z' fill='%2300ABBF'/%3e%3cpath d='M34 12H30V16H34V12Z' fill='%2300ABBF'/%3e%3cpath d='M30 12H26V16H30V12Z' fill='%2300ABBF'/%3e%3cpath d='M26 12H22V16H26V12Z' fill='%2300ABBF'/%3e%3cpath d='M22 12H18V16H22V12Z' fill='%2300ABBF'/%3e%3cpath d='M18 12H14V16H18V12Z' fill='%2300ABBF'/%3e%3cpath d='M14 12H10V16H14V12Z' fill='%2300ABBF'/%3e%3cpath opacity='0.7' d='M10 12H6V16H10V12Z' fill='%2300ABBF'/%3e%3cpath opacity='0.5' d='M38 8H34V12H38V8Z' fill='%2300B9BF'/%3e%3cpath opacity='0.8' d='M34 8H30V12H34V8Z' fill='%2300B9BF'/%3e%3cpath d='M30 8H26V12H30V8Z' fill='%2300B9BF'/%3e%3cpath d='M26 8H22V12H26V8Z' fill='%2300B9BF'/%3e%3cpath d='M22 8H18V12H22V8Z' fill='%2300B9BF'/%3e%3cpath d='M18 8H14V12H18V8Z' fill='%2300B9BF'/%3e%3cpath opacity='0.8' d='M14 8H10V12H14V8Z' fill='%2300B9BF'/%3e%3cpath opacity='0.5' d='M10 8H6V12H10V8Z' fill='%2300B9BF'/%3e%3cpath opacity='0.7' d='M34 4H30V8H34V4Z' fill='%2300C6BF'/%3e%3cpath opacity='0.8' d='M30 4H26V8H30V4Z' fill='%2300C6BF'/%3e%3cpath d='M26 4H22V8H26V4Z' fill='%2300C6BF'/%3e%3cpath d='M22 4H18V8H22V4Z' fill='%2300C6BF'/%3e%3cpath opacity='0.8' d='M18 4H14V8H18V4Z' fill='%2300C6BF'/%3e%3cpath opacity='0.7' d='M14 4H10V8H14V4Z' fill='%2300C6BF'/%3e%3cpath opacity='0.5' d='M30 0H26V4H30V0Z' fill='%2300D4BF'/%3e%3cpath opacity='0.7' d='M26 0H22V4H26V0Z' fill='%2300D4BF'/%3e%3cpath opacity='0.7' d='M22 0H18V4H22V0Z' fill='%2300D4BF'/%3e%3cpath opacity='0.5' d='M18 0H14V4H18V0Z' fill='%2300D4BF'/%3e%3cpath d='M16.5141 14.9697L17.6379 12.4572C18.0459 11.8129 17.9958 11.0255 17.5449 10.5745C17.4876 10.5173 17.416 10.46 17.3444 10.4171C17.0366 10.2238 16.6572 10.1808 16.3065 10.2954C15.9199 10.4171 15.5835 10.6748 15.3687 11.0184C15.3687 11.0184 13.8297 14.6046 13.2642 16.2153C12.6987 17.8259 12.9206 20.7822 15.1254 22.987C17.4661 25.3277 20.8448 25.8575 23.0066 24.2397C23.0997 24.1967 23.1784 24.1395 23.2572 24.0751L29.9072 18.5202C30.2293 18.2554 30.7089 17.7042 30.2794 17.0743C29.8642 16.4586 29.0697 16.881 28.7404 17.0886L24.9107 19.8731C24.8391 19.9304 24.7318 19.9232 24.6673 19.8517C24.6673 19.8517 24.6673 19.8445 24.6602 19.8445C24.56 19.7228 24.5456 19.4079 24.696 19.2862L30.5657 14.304C31.074 13.8459 31.1456 13.1802 30.7304 12.7292C30.3295 12.2854 29.6924 12.2997 29.1842 12.7578L23.9157 16.881C23.8155 16.9597 23.6652 16.9454 23.5864 16.8452L23.5793 16.838C23.4719 16.7235 23.4361 16.5231 23.5506 16.4014L29.535 10.596C30.0074 10.1522 30.036 9.4149 29.5922 8.94245C29.3775 8.72054 29.084 8.59169 28.7762 8.59169C28.4612 8.59169 28.1606 8.70623 27.9387 8.92813L21.8255 14.6691C21.6823 14.8122 21.396 14.6691 21.3602 14.4973C21.3459 14.4328 21.3674 14.3684 21.4103 14.3255L26.0918 8.99972C26.5571 8.56306 26.5858 7.83292 26.1491 7.36763C25.7124 6.90234 24.9823 6.87371 24.517 7.31036C24.4955 7.32468 24.4812 7.34615 24.4597 7.36763L17.3659 15.2203C17.1082 15.478 16.736 15.4851 16.557 15.342C16.4425 15.2489 16.4282 15.0843 16.5141 14.9697Z' fill='white'/%3e%3cpath d='M4.99195 43.6627H3.32946V40.8306C3.32946 40.1764 3.2488 39.6073 2.55423 39.6073C1.85966 39.6073 1.64905 40.2167 1.64905 41.0144V43.6627H0V36.112H1.64905V37.9045C1.64905 38.4512 1.64008 39.0427 1.64008 39.0427C1.89999 38.5632 2.38395 38.1689 3.13677 38.1689C4.61106 38.1689 4.99195 39.1637 4.99195 40.4766V43.6627Z' fill='white'/%3e%3cpath d='M12.081 42.762C11.7181 43.1563 10.9652 43.7882 9.51337 43.7882C7.42069 43.7882 5.77612 42.3228 5.77612 39.8941C5.77612 37.4564 7.43861 36 9.50889 36C10.9742 36 11.7674 36.6453 11.9556 36.8514L11.4402 38.3167C11.3058 38.1285 10.544 37.5281 9.60299 37.5281C8.39757 37.5281 7.4655 38.3795 7.4655 39.8582C7.4655 41.337 8.43342 42.175 9.60299 42.175C10.4902 42.175 11.131 41.803 11.5209 41.3773L12.081 42.762Z' fill='white'/%3e%3cpath d='M17.3016 43.6627H15.7242L15.6928 43.0936C15.4777 43.3221 15.0655 43.7837 14.2365 43.7837C13.3403 43.7837 12.3903 43.2684 12.3903 42.0674C12.3903 40.8665 13.4344 40.4587 14.3709 40.4139L15.6525 40.3601V40.2391C15.6525 39.67 15.2716 39.3743 14.6084 39.3743C13.9586 39.3743 13.3089 39.679 13.049 39.8538L12.6143 38.72C13.049 38.4915 13.8421 38.1733 14.7921 38.1733C15.7421 38.1733 16.2888 38.4019 16.6921 38.7962C17.082 39.1906 17.3016 39.7148 17.3016 40.6245V43.6627ZM15.657 41.2877L14.8414 41.3415C14.3351 41.3639 14.0348 41.5924 14.0348 41.9957C14.0348 42.4125 14.353 42.6634 14.8101 42.6634C15.2537 42.6634 15.5539 42.3587 15.657 42.1705V41.2877Z' fill='white'/%3e%3cpath d='M21.6035 43.7792C20.8506 43.7792 20.3129 43.4835 19.9948 42.9816V45.6389H18.3457V38.2674H19.9141L19.9051 38.9575H19.9275C20.2995 38.487 20.8462 38.1689 21.6214 38.1689C23.0867 38.1689 24.0143 39.3832 24.0143 40.9696C24.0143 42.5559 23.0778 43.7792 21.6035 43.7792ZM21.1285 39.549C20.4249 39.549 19.941 40.1181 19.941 40.9471C19.941 41.7762 20.4249 42.3453 21.1285 42.3453C21.841 42.3453 22.3249 41.7762 22.3249 40.9471C22.3249 40.1181 21.841 39.549 21.1285 39.549Z' fill='white'/%3e%3cpath d='M27.8322 39.6028H26.7074V41.5386C26.7074 42.0002 26.7702 42.1077 26.8508 42.2063C26.9225 42.296 27.0256 42.3363 27.2407 42.3363C27.411 42.3318 27.5768 42.3004 27.7381 42.2377L27.8188 43.6044C27.4379 43.7165 27.039 43.7747 26.6447 43.7792C26.0577 43.7792 25.6633 43.591 25.4079 43.2773C25.1525 42.9636 25.0449 42.511 25.0449 41.691V39.6028H24.3235V38.2809H25.0449V36.8156H26.7074V38.2809H27.8322V39.6028Z' fill='white'/%3e%3cpath d='M32.712 43.1339C32.6583 43.1787 32.125 43.7792 30.7717 43.7792C29.3781 43.7792 28.0875 42.771 28.0875 40.9785C28.0875 39.1726 29.396 38.1689 30.7896 38.1689C32.0892 38.1689 32.6762 38.738 32.6762 38.738L32.3132 40.0599C31.9458 39.7507 31.4842 39.5804 31.0047 39.5804C30.3012 39.5804 29.7455 40.0957 29.7455 40.9471C29.7455 41.7986 30.2519 42.3363 31.0271 42.3363C31.8024 42.3363 32.3177 41.812 32.3177 41.812L32.712 43.1339Z' fill='white'/%3e%3cpath d='M38.3986 43.6627H36.7361V40.8306C36.7361 40.1764 36.6554 39.6073 35.9608 39.6073C35.2663 39.6073 35.0512 40.2212 35.0512 41.0188V43.6672H33.4066V36.112H35.0557V37.9045C35.0557 38.4512 35.0467 39.0427 35.0467 39.0427C35.3066 38.5632 35.7906 38.1689 36.5434 38.1689C38.0177 38.1689 38.3986 39.1637 38.3986 40.4766V43.6627Z' fill='white'/%3e%3cpath d='M44 43.6627H42.4227L42.3913 43.0936C42.1762 43.3221 41.764 43.7837 40.935 43.7837C40.0387 43.7837 39.0887 43.2684 39.0887 42.0674C39.0887 40.8665 40.1328 40.4587 41.0694 40.4139L42.351 40.3601V40.2391C42.351 39.67 41.9701 39.3743 41.3069 39.3743C40.6571 39.3743 40.0074 39.679 39.7475 39.8538L39.3128 38.7156C39.7475 38.487 40.5406 38.1689 41.4906 38.1689C42.4406 38.1689 42.9873 38.3974 43.3906 38.7917C43.7805 39.1861 44 39.7104 44 40.62V43.6627ZM42.3599 41.2877L41.5444 41.3415C41.038 41.3639 40.7378 41.5924 40.7378 41.9957C40.7378 42.4125 41.0559 42.6634 41.513 42.6634C41.9566 42.6634 42.2569 42.3587 42.3599 42.1705V41.2877V41.2877Z' fill='white'/%3e%3c/svg%3e",i="https://newassets.hcaptcha.com/captcha/v1/096d3a6/static/images/logo_combination-"+this.state.theme+".png";t.logo&&(i="png",e="object"==typeof t.logo?t.logo[this.state.theme]||t.logo.light:t.logo);var n={theme:No,url:this.state.url,src:e,fallback:i,autoLoad:this.state.display};this.logo=this.initComponent(rn,n)}function rs(t){Jt.self(this,Yt,"anchor-info"),this.state={size:t.size},this.brand=this.initComponent(ss,t),t.linksOff||(this.links=this.initComponent(is,t))}function as(){Jt.self(this,Yt,"#status"),this.state={visible:!1,copy:""},this.translate(),this.setAttribute("aria-hidden",!0),this.setAttribute("aria-live","polite")}function ls(){Jt.self(this,Yt,"#warning"),this.state={visible:!1,copy:""},this.$copy=this.initComponent(Li,{selector:".warning-text",theme:No}),this.setAttribute("aria-hidden",!0),this.setAttribute("aria-live","polite")}function cs(t){var e=t.palette,i=t.component,n="light"===e.mode;return Ci.merge({main:{fill:e.grey[n?100:800],border:e.grey[n?300:200]},hover:{fill:e.grey[n?200:900]}},i.checkbox)}function hs(t){Jt.self(this,nn,{selector:"#anchor",theme:No}),this.state={selected:!1,warning:!1,error:!1,ticked:!1,defaultVisible:"invisible"!==t.size},this.config=t,this._style=cs(No.get()),this.setVisible(this.state.defaultVisible),this.onClick=this.onClick.bind(this),this.onHover=this.onHover.bind(this),this.anchor=this.initComponent(qo),this.label=this.initComponent(Go),this.info=this.initComponent(rs,this.config),this.status=this.initComponent(as),this.warning=this.initComponent(ls),this.addEventListener("enter",this.onClick),this.addEventListener("click",this.onClick),this.addEventListener("over",this.onHover),this.addEventListener("out",this.onHover)}function us(t,e){var i=this;t instanceof Gt||(t=new Gt(t)),ct.host=e.host?e.host:"",ct.sitekey=e.sitekey?e.sitekey:"";var n=new ii,o=new hs(e);return o.style(),o.reset(),t.appendElement(o),t.css({display:"block"}),t.addEventListener("down",(function(){t.hasClass("using-kb")&&t.removeClass("using-kb")})),t.addEventListener("keyup",(function(e){9===e.keyNum&&t.addClass("using-kb")})),o.on("select",(function(t){o.select(),n.emit("select",t.action)})),i.tick=function(){o.tick()},i.reset=function(){o.reset(),o.anchor.focus()},i.translate=function(){o.translate()},i.setStatus=function(t,e){t?(o.status.set(t,e),o.anchor.describeBy(o.status)):(o.status.reset(),o.anchor.describeBy(null))},i.setWarning=function(t){o.warning.set(t),o.warning.isVisible()?o.anchor.describeBy(o.warning):o.anchor.describeBy(null)},i.on=function(t,e){n.on(t,e)},i.off=function(t,e){n.off(t,e)},i.getLocation=function(){return o.anchor.getLocation()},i.setLoading=function(t){return o.setLoading(t)},i.getLogoUrl=function(){return o.getLogoUrl()},i.theme=function(t,e){e?(No.add(t,No.extend(No.active(),e)),No.use(t)):No.use(t),o.style()},i}function ds(t,e){this.cause=t,this.message=e}function ps(t){ds.call(this,it.INVALID_CAPTCHA_ID,"Invalid hCaptcha id: "+t)}function fs(){ds.call(this,it.MISSING_CAPTCHA,"No hCaptcha exists.")}function ms(){ds.call(this,it.MISSING_SITEKEY,"Missing sitekey - https://hcaptcha.com/docs/configuration#jsapi")}Jt.proto(ss,Yt),ss.prototype.style=function(){if(this.state.display){this.logo.size(44,50),this.logo.css({margin:"0 auto"})}},ss.prototype.translate=function(){this.logo.setAttribute("aria-label",se.translate(os)),this.setAttribute("title",this.state.label)},ss.prototype.getLogoUrl=function(){return this.state.url},Jt.proto(rs,Yt),rs.prototype.style=function(){var t=this.state.size,e={display:"inline-block",height:"100%",width:65},i={margin:"0 auto",top:this.links?6:10,position:"relative"},n={textAlign:"right",position:"fixed",bottom:9,right:12};"compact"===t&&(e.width="100%",e.height="auto",e.marginTop=5,i.top=this.links?0:10,n.textAlign="center",n.position="relative",n.bottom=5,n.right="auto"),this.css(e),this.links&&(this.links.style(),this.links.css(n)),this.brand.style(),this.brand.css(i)},rs.prototype.translate=function(){this.links&&this.links.translate(),this.brand.translate()},rs.prototype.getLogoUrl=function(){return this.brand.getLogoUrl()},Jt.proto(as,Yt),as.prototype.style=function(){var t=No.get().palette;this.css({display:this.state.visible?"block":"none",color:t.warn.main,fontSize:10,top:5,left:5,position:"absolute"})},as.prototype.set=function(t,e){if(t&&t.indexOf("invalid-challenge")>=0){var i=t.replace(/-/g," ");t=i.charAt(0).toUpperCase()+i.slice(1)+"."}this.state.visible=t&&""!==t&&!e,this.state.copy=t,this.state.visible?(this.translate(),this.setAttribute("aria-hidden",e||!t)):this.removeAttribute("aria-label"),this.css({display:this.state.visible?"block":"none"})},as.prototype.reset=function(){this.state.visible=!1,this.state.copy="",this.removeAttribute("aria-label"),this.setAttribute("aria-hidden",!0),this.css({display:"none"})},as.prototype.translate=function(){if(""!==this.state.copy){var t=se.translate(this.state.copy);this.setAttribute("aria-label",t),this.content(t)}},as.prototype.isVisible=function(){return this.state.visible},Jt.proto(ls,Yt),ls.prototype.style=function(t){var e=t?"95%":"75%",i=t?50:5,n=No.get().palette;this.css({display:this.state.visible?"block":"none",color:n.warn.main,fontSize:10,bottom:i,left:5,width:e,position:"absolute"})},ls.prototype.set=function(t){this.state.visible=t&&""!==t,this.state.copy=t,this.state.visible?this.translate():this.removeAttribute("aria-label"),this.css({display:this.state.visible?"block":"none"})},ls.prototype.translate=function(){if(""!==this.state.copy){var t=se.translate(this.state.copy);this.setAttribute("aria-label",t),this.$copy.parseText(t)}},ls.prototype.isVisible=function(){return this.state.visible},Jt.proto(hs,nn),hs.prototype.style=function(){var t="compact"===this.config.size;this._style=cs(No.get()),this.info.style(),this.anchor.style(t),this.label.style(t),this.status.style(),this.warning.style(t);var e=t?156:300,i=t?136:74,n={backgroundColor:this._style.main.fill,borderWidth:"1px",borderStyle:"solid",borderColor:this._style.main.border,borderRadius:4,cursor:this.state.ticked?"default":"pointer",width:e,height:i};this.setStyle(n)},hs.prototype.onHover=function(t){var e="over"===t.action?"hover":"main";this.css({backgroundColor:this._style[e].fill})},hs.prototype.onClick=function(t){var e=t.target||t.srcElement,i="string"==typeof e.className?e.className:"",n=i.indexOf("logo")>=0||i.indexOf("link")>=0;if(this.state.selected||t.defaultPrevented||n)return!0;this.emit("select",t)},hs.prototype.select=function(){this.state.selected=!0,this.setLoading(!0),this.setAttribute("aria-hidden",!0),this.anchor.setAttribute("aria-checked","mixed"),this.anchor.setAttribute("tabindex","-1")},hs.prototype.reset=function(){this.state.ticked=!1,this.state.selected=!1,this.setVisible(this.state.defaultVisible),this.anchor.setState(null),this.css({cursor:"pointer"})},hs.prototype.setLoading=function(t){this.state.loading=t;var e=t?"loading":this.state.selected?"solving":null;this.anchor.setState(e),this.css({cursor:"default"})},hs.prototype.tick=function(){this.state.ticked=!0,this.anchor.setState("passed"),this.css({cursor:"default"})},hs.prototype.translate=function(){this.anchor.translate(),this.info.translate(),this.label.translate(),this.status.translate(),this.warning.translate()},hs.prototype.getLogoUrl=function(){return this.info.getLogoUrl()},ds.prototype=Error.prototype;var ys=[],gs=[],vs={add:function(t){ys.push(t)},remove:function(t){for(var e=!1,i=ys.length;--i>-1&&!1===e;)ys[i].id===t.id&&(e=ys[i],ys.splice(i,1));return e},each:function(t){for(var e=-1;++e10&&gs.splice(0,gs.length-10)},getSession:function(){return gs}};function bs(t,e){"object"!=typeof t||e||(e=t,t=null);var i,n,o,s=!0===(e=e||{}).async,r=new Promise((function(t,e){n=t,o=e}));if(r.resolve=n,r.reject=o,i=t?vs.getById(t):vs.getByIndex(0))Ct("Execute called","hCaptcha","info"),Ue.setData("exec",!0),s&&i.setPromise(r),i.onReady(i.initChallenge,e);else if(t){if(!s)throw new ps(t);r.reject(it.INVALID_CAPTCHA_ID)}else{if(!s)throw new fs;r.reject(it.MISSING_CAPTCHA)}if(s)return r}function ws(t){var e="",i=null;i=t?vs.getById(t):vs.getByIndex(0);try{for(var n=vs.getSession(),o=n.length,s=!1;--o>-1&&!s;)(s=n[o][1]===i.id)&&(e=n[o][0])}catch(r){e=""}return e}function xs(t){var e=t?vs.getById(t):vs.getByIndex(0);if(!e)throw t?new ps(t):new fs;vs.remove(e),e.destroy(),e=null}var Cs=["light","dark","contrast","grey-red"],ks=new Ci;ks.add("contrast",{}),ks.add("grey-red",{component:{challenge:{main:{border:"#6a6a6a"}}}});function _s(t,e){var i=this;this.id=t,this.width=null,this.height=null,this.mobile=!1,this.ready=!1,this.listeners=[],this.config=e,this._visible=!1,this._selected=!1,this.$iframe=new Gt("iframe"),this._host=ct.host||window.location.hostname;var n=ct.assetUrl;ut.assethost&&(n=ut.assethost+ct.assetUrl.replace(ct.assetDomain,""));var o=n.match(/^.+\:\/\/[^\/]+/),s=o?o[0]:null,r=n+"/hcaptcha.html#frame=challenge&id="+this.id+"&host="+this._host+(e?"&"+Ht(this.config):""),a=tt.Browser.supportsPST();this.setupParentContainer(e),this.chat=si.createChat(this.$iframe.dom,t,s),this.chat.setReady(!1),this._timeoutFailedToInitialize=setTimeout((function(){i.$iframe&&i.$iframe.isConnected()?wt("Failed to initialize. Iframe attached","error","frame:challenge",{contentWindow:!!i.$iframe.dom.contentWindow,iframeSrc:r,supportsPST:a,customContainer:i._hasCustomContainer}):wt("Failed to initialize. Iframe detached","error","frame:challenge")}),6e4),this.$iframe.dom.src=r,this.$iframe.dom.frameBorder=0,this.$iframe.dom.scrolling="no",tt.Browser.supportsPST()&&(this.$iframe.dom.allow="private-state-token-issuance 'src'; private-state-token-redemption 'src'"),this.translate(),this._hasCustomContainer?(this._hideIframe(),this._parent.appendChild(this.$iframe.dom)):(this.$container=new Gt("div"),this.$wrapper=this.$container.createElement("div"),this.$overlay=this.$container.createElement("div"),this.$arrow=this.$container.createElement("div"),this.$arrow.fg=this.$arrow.createElement("div"),this.$arrow.bg=this.$arrow.createElement("div"),this.style.call(this),this.$wrapper.appendElement(this.$iframe),this._parent.appendChild(this.$container.dom),this.$container.setAttribute("aria-hidden",!0)),this.style()}_s.prototype.setupParentContainer=function(t){var e,i=t["challenge-container"];i&&(e="string"==typeof i?document.getElementById(i):i),e?(this._hasCustomContainer=!0,this._parent=e):(this._hasCustomContainer=!1,this._parent=document.body)},_s.prototype._hideIframe=function(){var t={};"ie"!==tt.Browser.type||"ie"===tt.Browser.type&&8!==tt.Browser.version?(t.opacity=0,t.visibility="hidden"):t.display="none",this.$iframe.setAttribute("aria-hidden",!0),this.$iframe.css(t)},_s.prototype._showIframe=function(){var t={};"ie"!==tt.Browser.type||"ie"===tt.Browser.type&&8!==tt.Browser.version?(t.opacity=1,t.visibility="visible"):t.display="block",this.$iframe.removeAttribute("aria-hidden"),this.$iframe.css(t)},_s.prototype.style=function(){var t=function(t){var e=t.palette,i=t.component;return Ci.merge({main:{fill:e.common.white,border:e.grey[400]}},i.challenge)}(ks.get());if(this._hasCustomContainer)this.$iframe.css({border:0,position:"relative",backgroundColor:t.main.fill});else{var e={backgroundColor:t.main.fill,border:"1px solid "+t.main.border,boxShadow:"rgba(0, 0, 0, 0.1) 0px 0px 4px",borderRadius:4,left:"auto",top:-1e4,zIndex:-9999999999999,position:"absolute"};"ie"!==tt.Browser.type||"ie"===tt.Browser.type&&8!==tt.Browser.version?(e.transition="opacity 0.15s ease-out",e.opacity=0,e.visibility="hidden"):e.display="none",this.$container.css(e),this.$wrapper.css({position:"relative",zIndex:1}),this.$overlay.css({width:"100%",height:"100%",position:"fixed",pointerEvents:"none",top:0,left:0,zIndex:0,backgroundColor:t.main.fill,opacity:.05}),this.$arrow.css({borderWidth:11,position:"absolute",pointerEvents:"none",marginTop:-11,zIndex:1,right:"100%"}),this.$arrow.fg.css({borderWidth:10,borderStyle:"solid",borderColor:"transparent rgb(255, 255, 255) transparent transparent",position:"relative",top:10,zIndex:1}),this.$arrow.bg.css({borderWidth:11,borderStyle:"solid",borderColor:"transparent "+t.main.border+" transparent transparent",position:"relative",top:-11,zIndex:0}),this.$iframe.css({border:0,zIndex:2e9,position:"relative"})}},_s.prototype.setup=function(t){return this.chat.send("create-challenge",t)},_s.prototype.sendTranslation=function(t){var e={locale:t,table:se.getTable(t)||{}};this.chat&&this.chat.send("challenge-translate",e),this.translate()},_s.prototype.translate=function(){this.$iframe.dom.title=se.translate("Main content of the hCaptcha challenge")},_s.prototype.isVisible=function(){return this._visible},_s.prototype.getDimensions=function(t,e){return this._visible?this.chat.contact("resize-challenge",{width:t,height:e}):Promise.resolve(null)},_s.prototype.show=function(){if(!0!==this._visible)if(this._visible=!0,this._hasCustomContainer)this._showIframe();else{var t={zIndex:9999999999999,display:"block"};("ie"!==tt.Browser.type||"ie"===tt.Browser.type&&8!==tt.Browser.version)&&(t.opacity=1,t.visibility="visible"),this.$container.css(t),this.$container.removeAttribute("aria-hidden"),this.$overlay.css({pointerEvents:"auto",cursor:"pointer"})}},_s.prototype.focus=function(){this.$iframe.dom.focus()},_s.prototype.close=function(t){if(!1!==this._visible){if(this._visible=!1,this._hasCustomContainer)return this._hideIframe(),void this.chat.send("close-challenge",{event:t});var e={left:"auto",top:-1e4,zIndex:-9999999999999};"ie"!==tt.Browser.type||"ie"===tt.Browser.type&&8!==tt.Browser.version?(e.opacity=0,e.visibility="hidden"):e.display="none",this.$container.css(e),this._hasCustomContainer||this.$overlay.css({pointerEvents:"none",cursor:"default"}),this.chat.send("close-challenge",{event:t}),this.$container.setAttribute("aria-hidden",!0)}},_s.prototype.size=function(t,e,i){this.width=t,this.height=e,this.mobile=i,this.$iframe.css({width:t,height:e}),this._hasCustomContainer||(this.$wrapper.css({width:t,height:e}),i?this.$overlay.css({opacity:.5}):this.$overlay.css({opacity:.05}))},_s.prototype.position=function(t){if(!this._hasCustomContainer&&t){var e=10,i=window.document.documentElement,n=tt.Browser.scrollY(),o=tt.Browser.width(),s=tt.Browser.height(),r=this.mobile||"invisible"===this.config.size||t.offset.left+t.tick.x<=t.tick.width/2,a=Math.round(t.bounding.top)+n!==t.offset.top,l=r?(o-this.width)/2:t.bounding.left+t.tick.right+10;(l+this.width+e>o||l<0)&&(l=(o-this.width)/2,r=!0);var c=(i.scrollHeight=n+s&&(h=n+s-(this.height+e)),h=Math.max(Math.min(h,c),10);var u=t.bounding.top+t.tick.y+n-h-10,d=this.height-10-30;u=Math.max(Math.min(u,d),e),this.$container.css({left:l,top:h}),this.$arrow.fg.css({display:r?"none":"block"}),this.$arrow.bg.css({display:r?"none":"block"}),this.$arrow.css({top:u}),this.top=h,this.$container.dom.getBoundingClientRect()}},_s.prototype.destroy=function(){this._timeoutFailedToInitialize&&(clearTimeout(this._timeoutFailedToInitialize),this._timeoutFailedToInitialize=null),this._visible&&this.close.call(this),si.removeChat(this.chat),this.chat=this.chat.destroy(),this._hasCustomContainer?this._parent.removeChild(this.$iframe.dom):(this._parent.removeChild(this.$container.dom),this.$container=this.$container.__destroy()),this.$iframe=this.$iframe.__destroy()},_s.prototype.setReady=function(){var t;this._timeoutFailedToInitialize&&(clearTimeout(this._timeoutFailedToInitialize),this._timeoutFailedToInitialize=null),this.chat&&this.chat.setReady(!0),this.ready=!0;for(var e=this.listeners.length;--e>-1;)t=this.listeners[e],this.listeners.splice(e,1),t()},_s.prototype.onReady=function(t){var e=Array.prototype.slice.call(arguments,1),i=function(){t.apply(null,e)};this.ready?i():this.listeners.push(i)},_s.prototype.onOverlayClick=function(t){this._hasCustomContainer||this.$overlay.addEventListener("click",t)},_s.prototype.setData=function(t){this.chat&&this.chat.send("challenge-data",t)};function Es(t,e,i){var n=this;this.id=e,this.response=null,this.location={tick:null,offset:null,bounding:null},this.config=i,this._ticked=!0,this.$container=t instanceof Gt?t:new Gt(t),this._host=ct.host||window.location.hostname,this.$iframe=new Gt("iframe");var o=ct.assetUrl;ut.assethost&&(o=ut.assethost+ct.assetUrl.replace(ct.assetDomain,""));var s=o.match(/^.+\:\/\/[^\/]+/),r=s?s[0]:null,a=o+"/hcaptcha.html#frame=checkbox&id="+this.id+"&host="+this._host+(i?"&"+Ht(this.config):"");this.chat=si.createChat(this.$iframe.dom,e,r),this.chat.setReady(!1),this._timeoutFailedToInitialize=setTimeout((function(){n.$iframe&&n.$iframe.isConnected()?wt("Failed to initialize. Iframe attached","error","frame:checkbox",{contentWindow:!!n.$iframe.dom.contentWindow,iframeSrc:a}):wt("Failed to initialize. Iframe detached","error","frame:checkbox")}),6e4),this.$iframe.dom.src=a,this.$iframe.dom.tabIndex=this.config.tabindex||0,this.$iframe.dom.frameBorder="0",this.$iframe.dom.scrolling="no",tt.Browser.supportsPST()&&(this.$iframe.dom.allow="private-state-token-issuance 'src'; private-state-token-redemption 'src'"),this.translate(),this.config.size&&"invisible"===this.config.size&&this.$iframe.setAttribute("aria-hidden","true"),this.$iframe.setAttribute("data-hcaptcha-widget-id",e),this.$iframe.setAttribute("data-hcaptcha-response",""),this.$container.appendElement(this.$iframe),"off"!==ut.recaptchacompat&&(this.$textArea0=this.$container.createElement("textarea","#g-recaptcha-response-"+e),this.$textArea0.dom.name="g-recaptcha-response",this.$textArea0.css({display:"none"})),this.$textArea1=this.$container.createElement("textarea","#h-captcha-response-"+e),this.$textArea1.dom.name="h-captcha-response",this.$textArea1.css({display:"none"}),this.ready=new Promise((function(t){n.chat.listen("checkbox-ready",t)})).then((function(){n._timeoutFailedToInitialize&&(clearTimeout(n._timeoutFailedToInitialize),n._timeoutFailedToInitialize=null),n.chat&&n.chat.setReady(!0)})),this.clearLoading=this.clearLoading.bind(this),this.style()}function Ss(t,e,i){this.id=e,this.response=null,this.location={tick:null,offset:null,bounding:null},this.config=i,this.$container=t instanceof Gt?t:new Gt(t),this.$iframe=new Gt("iframe"),this.$iframe.setAttribute("aria-hidden","true"),this.$iframe.css({display:"none"}),this.$iframe.setAttribute("data-hcaptcha-widget-id",e),this.$iframe.setAttribute("data-hcaptcha-response",""),this.$container.appendElement(this.$iframe),"off"!==ut.recaptchacompat&&(this.$textArea0=this.$container.createElement("textarea","#g-recaptcha-response-"+e),this.$textArea0.dom.name="g-recaptcha-response",this.$textArea0.css({display:"none"})),this.$textArea1=this.$container.createElement("textarea","#h-captcha-response-"+e),this.$textArea1.dom.name="h-captcha-response",this.$textArea1.css({display:"none"})}function As(t,e,i){if(!i.sitekey)throw new ms;this.id=e,this.visible=!1,this.overflow={override:!1,cssUsed:!0,value:null,scroll:0},this.onError=null,this.onPass=null,this.onExpire=null,this.onChalExpire=null,this.onOpen=null,this.onClose=null,this._ready=!1,this._active=!1,this._listeners=[],this.config=i,Cs.indexOf(i.theme)>=0&&ks.use(i.theme),this._state={escaped:!1,passed:!1,expiredChallenge:!1,expiredResponse:!1},this._origData=null,this._promise=null,this._responseTimer=null,this.initChallenge=this.initChallenge.bind(this),this.closeChallenge=this.closeChallenge.bind(this),this.displayChallenge=this.displayChallenge.bind(this),this.getGetCaptchaManifest=this.getGetCaptchaManifest.bind(this),this.challenge=new _s(e,i),"invisible"==this.config.size?(Ct("Invisible mode is set","hCaptcha","info"),this.checkbox=new Ss(t,e,i)):this.checkbox=new Es(t,e,i)}Es.prototype.setResponse=function(t){this.response=t,this.$iframe.dom.setAttribute("data-hcaptcha-response",t),"off"!==ut.recaptchacompat&&(this.$textArea0.dom.value=t),this.$textArea1.dom.value=t},Es.prototype.style=function(){switch(this.config.size){case"compact":this.$iframe.css({width:164,height:144});break;case"invisible":this.$iframe.css({display:"none"});break;default:this.$iframe.css({width:303,height:78,overflow:"hidden"})}},Es.prototype.reset=function(){this._ticked=!1,this.$iframe&&this.$iframe.dom.contentWindow&&this.chat&&this.chat.send("checkbox-reset")},Es.prototype.clearLoading=function(){this.chat&&this.chat.send("checkbox-clear")},Es.prototype.sendTranslation=function(t){var e={locale:t,table:se.getTable(t)||{}};this.chat&&this.chat.send("checkbox-translate",e),this.translate()},Es.prototype.translate=function(){this.$iframe.dom.title=se.translate("Widget containing checkbox for hCaptcha security challenge")},Es.prototype.status=function(t,e){this.$iframe&&this.$iframe.dom.contentWindow&&this.chat&&this.chat.send("checkbox-status",{text:t||null,a11yOnly:e||!1})},Es.prototype.tick=function(){this._ticked=!0,this.chat&&this.chat.send("checkbox-tick")},Es.prototype.getTickLocation=function(){return this.chat.contact("checkbox-location")},Es.prototype.getOffset=function(){var t=this.$iframe.dom;t.offsetParent||(t=t.parentElement);for(var e=0,i=0;t;)e+=t.offsetLeft,i+=t.offsetTop,t=t.offsetParent;return{top:i,left:e}},Es.prototype.getBounding=function(){return this.$iframe.dom.getBoundingClientRect()},Es.prototype.destroy=function(){this._timeoutFailedToInitialize&&(clearTimeout(this._timeoutFailedToInitialize),this._timeoutFailedToInitialize=null),this._ticked&&this.reset(),si.removeChat(this.chat),this.chat=this.chat.destroy(),this.$container.removeElement(this.$iframe),this.$container.removeElement(this.$textArea1),"off"!==ut.recaptchacompat&&(this.$container.removeElement(this.$textArea0),this.$textArea0=this.$textArea0.__destroy()),this.$textArea1=this.$textArea1.__destroy(),this.$container=this.$container.__destroy(),this.$iframe=this.$iframe.__destroy()},Ss.prototype.setResponse=function(t){this.response=t,this.$iframe.dom.setAttribute("data-hcaptcha-response",t),"off"!==ut.recaptchacompat&&(this.$textArea0.dom.value=t),this.$textArea1.dom.value=t},Ss.prototype.reset=function(){},Ss.prototype.clearLoading=function(){},Ss.prototype.sendTranslation=function(t){},Ss.prototype.status=function(t,e){},Ss.prototype.tick=function(){},Ss.prototype.getTickLocation=function(){return Promise.resolve({left:0,right:0,top:0,bottom:0,width:0,height:0,x:0,y:0})},Ss.prototype.getOffset=function(){var t=this.$iframe.dom;t.offsetParent||(t=t.parentElement);for(var e=0,i=0;t;)e+=t.offsetLeft,i+=t.offsetTop,t=t.offsetParent;return{top:i,left:e}},Ss.prototype.getBounding=function(){return this.$iframe.dom.getBoundingClientRect()},Ss.prototype.destroy=function(){this._ticked&&this.reset(),this.$container.removeElement(this.$iframe),this.$container.removeElement(this.$textArea1),"off"!==ut.recaptchacompat&&(this.$container.removeElement(this.$textArea0),this.$textArea0=this.$textArea0.__destroy()),this.$textArea1=this.$textArea1.__destroy(),this.$container=this.$container.__destroy(),this.$iframe=this.$iframe.__destroy()},As.prototype._resetTimer=function(){null!==this._responseTimer&&(clearTimeout(this._responseTimer),this._responseTimer=null)},As.prototype.initChallenge=function(t){t||(t={}),Ct("Initiate challenge","hCaptcha","info"),this._origData=t;var e=this.getGetCaptchaManifest(),i=t.charity||null,n=t.a11yChallenge||!1,o=t.link||null,s=t.action||"",r=t.rqdata||null,a=t.errors||[],l=tt.Browser.width(),c=tt.Browser.height();this._active=!0,this._resetTimer(),this._resetState(),this.checkbox.setResponse(""),this.challenge.setup({a11yChallenge:n,manifest:e,width:l,height:c,charity:i,link:o,action:s,rqdata:r,wdata:wo(),errors:a.concat(Fo.collect())})},As.prototype.getGetCaptchaManifest=function(){var t=(this._origData||{}).manifest||null;return t||((t=Object.create(null)).st=Date.now()),t.v=1,t.topLevel=Ue.getData(),t.session=vs.getSession(),t.widgetList=vs.getCaptchaIdList(),t.widgetId=this.id,t.href=window.location.href,t.prev=JSON.parse(JSON.stringify(this._state)),t},As.prototype.displayChallenge=function(t){if(this._active){var e=this;this.visible=!0;var i=this.checkbox,n=this.challenge,o=tt.Browser.height();if(!("ie"===tt.Browser.type&&8===tt.Browser.version)){var s=window.getComputedStyle(document.body).getPropertyValue("overflow-y");this.overflow.override="hidden"===s,this.overflow.override&&(this.overflow.cssUsed=""===document.body.style.overflow&&""===document.body.style.overflowY,this.overflow.cssUsed||(this.overflow.value=""===s?"auto":s),this.overflow.scroll=tt.Browser.scrollY(),document.body.style.overflowY="auto")}return new Promise((function(s){i.status(),i.getTickLocation().then((function(r){if(e._active){if(n.size(t.width,t.height,t.mobile),n.show(),i.clearLoading(),i.location.bounding=i.getBounding(),i.location.tick=r,i.location.offset=i.getOffset(),n.position(i.location),n.focus(),n.height>window.document.documentElement.clientHeight)(window.document.scrollingElement||document.getElementsByTagName("html")[0]).scrollTop=Math.abs(n.height-o)+n.top;s()}}))})).then((function(){Ct("Challenge is displayed","hCaptcha","info"),e.onOpen&&Dt(e.onOpen)}))}},As.prototype.resize=function(t,e,i){var n=this,o=this.checkbox,s=this.challenge;s.getDimensions(t,e).then((function(t){t&&s.size(t.width,t.height,t.mobile),o.location.bounding=o.getBounding(),o.location.offset=o.getOffset(),tt.System.mobile&&!i||s.position(o.location)}))["catch"]((function(t){n.closeChallenge.call(n,{event:it.CHALLENGE_ERROR,message:"Captcha resize caused error.",error:t})}))},As.prototype.position=function(){var t=this.checkbox,e=this.challenge;tt.System.mobile||(t.location.bounding=t.getBounding(),e.position(t.location))},As.prototype.reset=function(){Ct("Captcha Reset","hCaptcha","info");try{this.checkbox.reset(),this.checkbox.setResponse(""),this._resetTimer(),this._resetState()}catch(t){xt("hCaptcha",t)}},As.prototype._resetState=function(){for(var t in this._state)this._state[t]=!1},As.prototype.closeChallenge=function(t){this.visible=!1,this._active=!1;var e=this,i=this.checkbox,n=this.challenge;this.overflow.override&&((window.document.scrollingElement||document.getElementsByTagName("html")[0]).scrollTop=this.overflow.scroll,this.overflow.override=!1,this.overflow.scroll=0,document.body.style.overflowY=this.overflow.cssUsed?null:this.overflow.value);var o=t.response||"";switch(i.setResponse(o),n.close(t.event),i.$iframe.dom.focus(),Ct("Challenge has closed","hCaptcha","info",{event:t.event,response:t.response,message:t.message}),t.event){case et.CHALLENGE_ESCAPED:this._state.escaped=!0,i.reset(),e.onClose&&Dt(e.onClose),e._promise&&e._promise.reject(et.CHALLENGE_CLOSED);break;case et.CHALLENGE_EXPIRED:this._state.expiredChallenge=!0,i.reset(),i.status("hCaptcha window closed due to timeout.",!0),e.onChalExpire&&Dt(e.onChalExpire),e._promise&&e._promise.reject(et.CHALLENGE_EXPIRED);break;case it.CHALLENGE_ERROR:case it.BUNDLE_ERROR:case it.NETWORK_ERROR:var s=t.event;i.reset(),t.event===it.NETWORK_ERROR?(i.status(t.message),429===t.status?s=it.RATE_LIMITED:"invalid-data"===t.message&&(s=it.INVALID_DATA)):t.event===it.BUNDLE_ERROR?s=it.CHALLENGE_ERROR:t.event===it.CHALLENGE_ERROR&&"Answers are incomplete"===t.message&&(s=it.INCOMPLETE_ANSWER),wt("Failed to execute","error","hCaptcha",{error:s,event:t.event,message:t.message}),this.onError&&Dt(this.onError,s),e._promise&&e._promise.reject(s);break;case et.CHALLENGE_PASSED:this._state.passed=!0,i.tick(),this.onPass&&Dt(this.onPass,o),e._promise&&e._promise.resolve({response:o,key:ws(this.id)}),"number"==typeof t.expiration&&(e._resetTimer(),e._responseTimer=setTimeout((function(){try{i.$iframe&&(i.$iframe.dom.contentWindow?(i.reset(),i.setResponse(""),i.status("hCaptcha security token has expired. Please complete the challenge again.",!0)):xs(e.id))}catch(Rs){xt("global",Rs)}e.onExpire&&Dt(e.onExpire),e._responseTimer=null,e._state.expiredResponse=!0}),1e3*t.expiration))}e._promise=null},As.prototype.updateTranslation=function(t){this.config.hl=t,this.checkbox&&this.checkbox.sendTranslation(t),this.challenge&&this.challenge.sendTranslation(t)},As.prototype.isReady=function(){return this._ready},As.prototype.setReady=function(t){if(this._ready=t,this._ready){var e;Ct("Instance is ready","hCaptcha","info");for(var i=this._listeners.length;--i>-1;)e=this._listeners[i],this._listeners.splice(i,1),e()}},As.prototype.setPromise=function(t){this._promise=t},As.prototype.onReady=function(t){var e=Array.prototype.slice.call(arguments,1),i=function(){t.apply(null,e)};this._ready?i():this._listeners.push(i)},As.prototype.destroy=function(){(Ct("Captcha Destroy","hCaptcha","info"),this._resetTimer(),this.overflow.override)&&((window.document.scrollingElement||document.getElementsByTagName("html")[0]).scrollTop=this.overflow.scroll,this.overflow.override=!1,this.overflow.scroll=0,document.body.style.overflowY=this.overflow.cssUsed?null:this.overflow.value);this.challenge.destroy(),this.checkbox.destroy(),this.challenge=null,this.checkbox=null},As.prototype.setSiteConfig=function(t){var e=this;if("ok"in t){var i=t.ok.features||{};if(this.config.themeConfig&&i.custom_theme){var n="custom-"+this.id;ks.add(n,ks.extend(ks.active(),this.config.themeConfig)),ks.use(n),this.challenge.style()}}return"invisible"===this.config.size?Promise.resolve():this.checkbox.ready.then((function(){return e.checkbox.chat.send("site-setup",t),new Promise((function(t){e.checkbox.chat.listen("checkbox-loaded",(function(){t()}))}))}))};var Ls=0,Bs=["hl","custom","tplinks","sitekey","theme","size","tabindex","challenge-container","confirm-nav","orientation"];function Hs(t,e){if(t)try{t.updateTranslation(e)}catch(Rs){xt("translation",Rs)}}var Ts={render:function(t,e){if("string"==typeof t&&(t=document.getElementById(t)),t&&1===t.nodeType)if(function(t){if(!t||!("challenge-container"in t))return!0;var e=t["challenge-container"];return"string"==typeof e&&(e=document.getElementById(e)),!!e&&1===e.nodeType}(e)){if(!1!==si.isSupported()){for(var i,n,o=t.getElementsByTagName("iframe"),s=-1;++supgrade your browser or enable it for hCaptcha.com")}else console.log("[hCaptcha] render: invalid challenge container '"+e["challenge-container"]+"'.");else console.log("[hCaptcha] render: invalid container '"+t+"'.")},reset:function(t){var e;if(t){if(!(e=vs.getById(t)))throw new ps(t);e.reset()}else{if(!(e=vs.getByIndex(0)))throw new fs;e.reset()}},remove:xs,execute:bs,getResponse:function(t){var e,i;if((i=t?vs.getById(t):vs.getByIndex(0))&&(e=i.checkbox.response||""),void 0!==e)return e;throw t?new ps(t):new fs},getRespKey:ws,close:function(t){var e=!1;if(!(e=t?vs.getById(t):vs.getByIndex(0)))throw t?new ps(t):new fs;e.closeChallenge({event:et.CHALLENGE_ESCAPED})},setData:function(t,e){if("object"!=typeof t||e||(e=t,t=null),!e||"object"!=typeof e)throw Error("[hCaptcha] invalid data supplied");var i=!1;if(!(i=t?vs.getById(t):vs.getByIndex(0)))throw t?new ps(t):new fs;Ct("Set data","hCaptcha","info");var n=i.challenge.setData.bind(i.challenge);i.onReady(n,e)},nodes:vs};function Ms(t){ct.file="hcaptcha";var e=document.currentScript,i=!1,n=!1,o="on",s=tt.Browser.width()/tt.Browser.height(),r=!(!window.hcaptcha||!window.hcaptcha.render);function a(){var t=tt.Browser.width(),e=tt.Browser.height(),i=tt.System.mobile&&s!==t/e;s=t/e,h(),Ts.nodes.each((function(n){n.visible&&n.resize(t,e,i)}))}function l(t){c(),Ts.nodes.each((function(t){t.visible&&t.position()}))}function c(){Ue.circBuffPush("xy",[tt.Browser.scrollX(),tt.Browser.scrollY(),document.documentElement.clientWidth/tt.Browser.width(),Date.now()])}function h(){Ue.circBuffPush("wn",[tt.Browser.width(),tt.Browser.height(),tt.System.dpr(),Date.now()])}window.hcaptcha={render:Ts.render,remove:Ts.remove,execute:Ts.execute,reset:Ts.reset,close:Ts.close,setData:Ts.setData,getResponse:Ts.getResponse,getRespKey:Ts.getRespKey},Fo.run(3e3),Le((function(){r||(!function(){var s;s=e?[e]:document.getElementsByTagName("script");var r=-1,a=!1,l=null,c=null;for(;++r2&&0!==e.status&&429!==e.status&&403!==e.status&&400!==e.status&&wt("api:getcaptcha failed","error","challenge",{error:e}),t=0,s&&(i={event:it.NETWORK_ERROR,message:(o||[""]).join(", ")}),bo(i.event),ri.send(et.CHALLENGE_CLOSED,i)):(t+=1,h())}))}function c(t){if(po(t.c),t.skip)bo(et.CHALLENGE_ESCAPED),ri.send(et.CHALLENGE_CLOSED,{event:et.CHALLENGE_ESCAPED});else if(t.pass)bo(et.CHALLENGE_PASSED),ri.send(et.CHALLENGE_CLOSED,{event:et.CHALLENGE_PASSED,response:t.generated_pass_UUID,expiration:t.expiration}),t.generated_pass_UUID||wt("no pass id","error","frame:challenge");else if(!1===t.success){var e=t["error-codes"]||[];if(-1!==e.indexOf("expired-session")||-1!==e.indexOf("client-fail"))return void h();bo(it.NETWORK_ERROR),ri.send(et.CHALLENGE_CLOSED,{event:it.NETWORK_ERROR,message:(t["error-codes"]||[""]).join(", ")})}else n.displayTryAgain(!0),Po.logAction("challenge-failed"),h()}function h(){if(vo&&(vo={s:Date.now(),n:vo.n+1}),Po.isRqChl()&&!ut.a11yChallenge)return n.lockInterface(!0),void(e=setTimeout((function(){bo(it.CHALLENGE_ERROR),ri.send(et.CHALLENGE_CLOSED,{event:it.CHALLENGE_ERROR,message:"Challenge Retry Failed"})}),2e3));ri.contact("getcaptcha-manifest").then((function(t){l(t,!0)}))}function u(t,e){ct.browserWidth=t.width,ct.browserHeight=t.height,n.size(t.width,t.height).then((function(t){e.resolve(t),Ct("challenge resized","challenge","info",t)}))}function d(){Ct("challenge refresh","challenge","info"),Po.logAction("challenge-refresh"),h()}function p(){uo(Fo.collect()),n.submit().then((function(t){Po.logAction(t),"challenge-skip"!==t||h()}))["catch"]((function(t){vt(t),Po.logAction(it.CHALLENGE_ERROR),h()}))}function f(){var t=Po.getData();n.displayReport(t).then((function(t){if(t){var e=function(){d(),n.getModal().off("refresh",e)};n.getModal().display("report_image",{key:t}),n.getModal().on("refresh",e)}}))["catch"]((function(t){vt(t),d()}))}window.addEventListener("load",(function(){a&&(clearTimeout(a),a=null),Ct("iframe:load","challenge","info")})),document.addEventListener("DOMContentLoaded",(function(){Ct("iframe:DOMContentLoaded","challenge","info")})),document.addEventListener("readystatechange",(function(){Ct("iframe:readystatechange","challenge","info",{readyState:document.readyState})})),Fo.run(),n=new so(document.body,{host:ct.host,sitekey:ct.sitekey,orientation:r.orientation||"portrait"}),ut.themeConfig&&n.addTheme("custom",ut.themeConfig),ri.init(r.id,r.origin),i=new Gt(document.body),ri.answer("create-challenge",(function(t){vo={s:Date.now(),n:0};var e,o={};t&&(Po.setRqData(t.rqdata||Po.getRqData()),t.wdata&&(e=t.wdata,window.__wdata=e),uo(t.errors),t.width&&(ct.browserWidth=t.width,ct.browserHeight=t.height),t.manifest&&(o=t.manifest),"enter"===t.action?i.addClass("using-kb"):i.hasClass("using-kb")&&i.removeClass("using-kb"),n.init(t)),n.setFocus("info"),l(o,!1)})),ri.answer("close-challenge",(function(t){bo(t.event),null!==e&&(clearTimeout(e),e=null),t&&t.event===et.CHALLENGE_ESCAPED&&Po.logAction("challenge-abandon-retry"),Po.setRqData(null),n.close()})),ri.answer("resize-challenge",u),ri.answer("challenge-translate",(function(t){n.translateInterface(t),n.isVisible()&&("en"!==t.locale?(Po.logAction("challenge-language-change"),h()):n.translateBundle())})),ri.contact("get-url").then((function(t){ct.url=t})),ri.answer("challenge-data",(function(t){t.rqdata&&Po.setRqData(t.rqdata)})),n.events.on("refresh",d),n.events.on("submit",p),n.events.on("report",f),n.events.on("report-submission",(function(t){Po.reportIssue(t.reason,t.comment,t.key)["catch"]((function(t){}))})),n.events.on("resize",(function(){ri.send("challenge-resize")})),n.events.on("focus-check",(function(){i.addClass("using-kb"),n.triggerFocus("submit")})),i.addEventListener("down",(function(t){n.isInterfaceLocked()||n.displayTryAgain(!1)})),i.addEventListener("keydown",(function(t){27===t.keyNum&&(n.getModal().isOpen()?(n.getModal().close(),n.hideReport()):(bo(et.CHALLENGE_ESCAPED),ri.send(et.CHALLENGE_CLOSED,{event:et.CHALLENGE_ESCAPED}),n.close()))})),i.addEventListener("down",(function(){"menu"!==n.getActiveElement()&&i.hasClass("using-kb")&&i.removeClass("using-kb")}),!0),i.addEventListener("keydown",(function(t){9===t.keyNum&&(i.addClass("using-kb"),t.shiftKey||"submit"===n.getActiveElement()&&(n.triggerFocus("challenge",0),t.preventDefault&&t.preventDefault()))}),!0),i.addEventListener("keydown",(function(t){if("submit"===n.getActiveElement()){var e=t.keyNum;37===e||38===e?(i.addClass("using-kb"),n.triggerFocus("challenge",-1),t.preventDefault&&t.preventDefault()):39!==e&&40!==e||(i.addClass("using-kb"),n.triggerFocus("challenge",0),t.preventDefault&&t.preventDefault())}})),jo().then((function(t){var e=t.features;return"object"!=typeof e&&(e={}),n.setWhiteLabel(!!t.custom),po(t.c),ut.themeConfig&&e.custom_theme?n.useTheme("custom"):n.useTheme(ut.theme),e.a11y_challenge&&(ut.a11yChallenge=!0,n.enableA11yChallenge(!0)),{ok:t}}),(function(t){return{err:t instanceof Error?{name:t.name,message:t.message}:t}})).then((function(t){ri.send("site-setup",t)})),ri.send("challenge-loaded")}():"checkbox"===Vs?function(){var t=!1,e=window.location.hash.slice(1),i=Bt(e),n=si.createChat(window.parent,i.id,i.origin);!function(t){ct.id=t.id,ct.host=t.host,ct.sitekey=t.sitekey,ct.file="checkbox",ut.sentry=!1!==t.sentry,bt(!0),ut.size=t.size||ut.compact,ut.custom=t.custom||ut.custom,ut.se=t.se||null,t.endpoint!==undefined&&"undefined"!==t.endpoint&&(ut.endpoint=t.endpoint),t.assethost!==undefined&&"undefined"!==t.assethost&&(It.URL(t.assethost)?ut.assethost=t.assethost:console.error("Invalid assethost uri.")),t.imghost!==undefined&&"undefined"!==t.imghost&&(ut.imghost=t.imghost),t.hl!==undefined&&"undefined"!==t.hl&&(ut.language=t.hl,se.setLocale(t.hl)),t.tplinks!==undefined&&"undefined"!==t.tplinks&&(ut.tplinks=t.tplinks),t.pat!==undefined&&"undefined"!==t.pat&&(ut.pat=t.pat),t.pstissuer!==undefined&&"undefined"!==t.pstissuer&&(ut.pstIssuer=t.pstissuer),ut.theme=t.theme||ut.theme,ut.themeConfig=t.themeConfig,ut.themeConfig&&(t.custom=!0)}(i);var o=setTimeout((function(){wt("Slow iframe initialization","error","frame:checkbox",{readyState:document.readyState})}),6e4);window.addEventListener("load",(function(){o&&(clearTimeout(o),o=null),Ct("iframe:load","checkbox","info")})),document.addEventListener("DOMContentLoaded",(function(){Ct("iframe:DOMContentLoaded","checkbox","info")})),document.addEventListener("readystatechange",(function(){Ct("iframe:readystatechange","checkbox","info",{readyState:document.readyState})}));var s=$o.sitekey(ct.sitekey),r=$o.dummykey(ct.sitekey);Fo.run();var a=null,l=null,c=new Promise((function(t){l=t})),h=null;function u(e,i){var n={host:ct.host,sitekey:ct.sitekey,size:ut.size,theme:ut.theme,linksOff:"off"===ut.tplinks,displayLogo:"invisible"!==ut.size,logo:null,logoUrl:null,privacyUrl:null,termsUrl:null},o=e&&e.custom;if(o){n.logo=o.logo,o.links&&(n.logoUrl=o.links.logo,n.privacyUrl=o.links.privacy,n.termsUrl=o.links.terms);var a=o.copy;if(a){var l={checkbox_prompt:"I am human",checkbox_a11y:"hCaptcha checkbox. Select in order to trigger the challenge, or to bypass it if you have an accessibility cookie."};for(var h in l){var u=a[h];for(var d in u){var p={};p[l[h]]=u[d],se.addTable(d,p)}}c.then((function(t){t.translate()}))}}var f=new us(document.body,n),m=e&&e.features&&e.features.custom_theme;return ut.themeConfig&&m?f.theme("custom",ut.themeConfig):f.theme(ut.theme),f.setStatus(!1),s||r?r&&f.setWarning("This hCaptcha is for testing only. Please contact the site admin if you see this."):f.setWarning("The sitekey for this hCaptcha is incorrect. Please contact the site admin if you see this."),f.on("select",(function(e){f.setStatus(!1),setTimeout((function(){i.send("checkbox-selected",{manifest:Ue.getData(),charity:t,a11yChallenge:ut.a11yChallenge||!1,link:f.getLogoUrl(),action:e,errors:Fo.collect()})}),1)})),f}new Promise((function(t){h=t})).then((function(t){if("ok"in t)return t.ok;throw t.err})).then((function(e){a||(a=u(e,n),l(a));var i=e.features||{};ut.a11yChallenge=i.a11y_challenge||!1,t=e.charity||!1,e.status_message&&s&&!r&&a.setWarning(e.status_message)}),(function(t){a||(a=u(null,n),l(a)),t.message&&a.setStatus(t.message)})).then((function(){Ue.resetData(),Ue.record(!0,!0,!0,!1),n.send("checkbox-loaded",a.getLocation())})),function(t,e,i){i.listen("site-setup",e),i.listen("checkbox-tick",(function(){t.then((function(t){t.tick()}))})),i.listen("checkbox-translate",(function(e){try{if(!e||!e.locale||!e.table)return;se.setLocale(e.locale),se.addTable(e.locale,e.table),t.then((function(t){t.translate()})),document.documentElement.setAttribute("lang",se.getLocale())}catch(Rs){xt("translation",Rs)}})),i.listen("checkbox-status",(function(e){t.then((function(t){t.setStatus(e.text,e.a11yOnly)}))})),i.listen("checkbox-reset",(function(){t.then((function(t){t.reset(),Ue.resetData(),Ue.record()}))})),i.listen("checkbox-clear",(function(){t.then((function(t){t.setLoading(!1)}))})),i.listen("checkbox-location",(function(e){t.then((function(t){var i=t.getLocation();e.resolve(i)}))}))}(c,h,n),n.send("checkbox-ready"),i.custom||(a=u(null,n),l(a))}():Ms():Ms()}(); diff --git a/dejacode/static/js/htmx-1.9.8.min.js b/dejacode/static/js/htmx-1.9.8.min.js new file mode 100644 index 00000000..4091536a --- /dev/null +++ b/dejacode/static/js/htmx-1.9.8.min.js @@ -0,0 +1 @@ +(function(e,t){if(typeof define==="function"&&define.amd){define([],t)}else if(typeof module==="object"&&module.exports){module.exports=t()}else{e.htmx=e.htmx||t()}})(typeof self!=="undefined"?self:this,function(){return function(){"use strict";var Y={onLoad:t,process:Dt,on:Z,off:K,trigger:fe,ajax:Cr,find:E,findAll:f,closest:d,values:function(e,t){var r=or(e,t||"post");return r.values},remove:B,addClass:F,removeClass:n,toggleClass:V,takeClass:j,defineExtension:Ar,removeExtension:Nr,logAll:X,logNone:U,logger:null,config:{historyEnabled:true,historyCacheSize:10,refreshOnHistoryMiss:false,defaultSwapStyle:"innerHTML",defaultSwapDelay:0,defaultSettleDelay:20,includeIndicatorStyles:true,indicatorClass:"htmx-indicator",requestClass:"htmx-request",addedClass:"htmx-added",settlingClass:"htmx-settling",swappingClass:"htmx-swapping",allowEval:true,allowScriptTags:true,inlineScriptNonce:"",attributesToSettle:["class","style","width","height"],withCredentials:false,timeout:0,wsReconnectDelay:"full-jitter",wsBinaryType:"blob",disableSelector:"[hx-disable], [data-hx-disable]",useTemplateFragments:false,scrollBehavior:"smooth",defaultFocusScroll:false,getCacheBusterParam:false,globalViewTransitions:false,methodsThatUseUrlParams:["get"],selfRequestsOnly:false,scrollIntoViewOnBoost:true},parseInterval:v,_:e,createEventSource:function(e){return new EventSource(e,{withCredentials:true})},createWebSocket:function(e){var t=new WebSocket(e,[]);t.binaryType=Y.config.wsBinaryType;return t},version:"1.9.8"};var r={addTriggerHandler:St,bodyContains:oe,canAccessLocalStorage:M,findThisElement:ve,filterValues:cr,hasAttribute:o,getAttributeValue:ee,getClosestAttributeValue:re,getClosestMatch:c,getExpressionVars:wr,getHeaders:fr,getInputValues:or,getInternalData:ie,getSwapSpecification:dr,getTriggerSpecs:Ze,getTarget:ge,makeFragment:l,mergeObjects:se,makeSettleInfo:T,oobSwap:ye,querySelectorExt:le,selectAndSwap:Ue,settleImmediately:Jt,shouldCancel:tt,triggerEvent:fe,triggerErrorEvent:ue,withExtensions:C};var b=["get","post","put","delete","patch"];var w=b.map(function(e){return"[hx-"+e+"], [data-hx-"+e+"]"}).join(", ");function v(e){if(e==undefined){return undefined}if(e.slice(-2)=="ms"){return parseFloat(e.slice(0,-2))||undefined}if(e.slice(-1)=="s"){return parseFloat(e.slice(0,-1))*1e3||undefined}if(e.slice(-1)=="m"){return parseFloat(e.slice(0,-1))*1e3*60||undefined}return parseFloat(e)||undefined}function Q(e,t){return e.getAttribute&&e.getAttribute(t)}function o(e,t){return e.hasAttribute&&(e.hasAttribute(t)||e.hasAttribute("data-"+t))}function ee(e,t){return Q(e,t)||Q(e,"data-"+t)}function u(e){return e.parentElement}function te(){return document}function c(e,t){while(e&&!t(e)){e=u(e)}return e?e:null}function R(e,t,r){var n=ee(t,r);var i=ee(t,"hx-disinherit");if(e!==t&&i&&(i==="*"||i.split(" ").indexOf(r)>=0)){return"unset"}else{return n}}function re(t,r){var n=null;c(t,function(e){return n=R(t,e,r)});if(n!=="unset"){return n}}function h(e,t){var r=e.matches||e.matchesSelector||e.msMatchesSelector||e.mozMatchesSelector||e.webkitMatchesSelector||e.oMatchesSelector;return r&&r.call(e,t)}function q(e){var t=/<([a-z][^\/\0>\x20\t\r\n\f]*)/i;var r=t.exec(e);if(r){return r[1].toLowerCase()}else{return""}}function i(e,t){var r=new DOMParser;var n=r.parseFromString(e,"text/html");var i=n.body;while(t>0){t--;i=i.firstChild}if(i==null){i=te().createDocumentFragment()}return i}function H(e){return e.match(/",0);return r.querySelector("template").content}else{var n=q(e);switch(n){case"thead":case"tbody":case"tfoot":case"colgroup":case"caption":return i(""+e+"
        ",1);case"col":return i(""+e+"
        ",2);case"tr":return i(""+e+"
        ",2);case"td":case"th":return i(""+e+"
        ",3);case"script":case"style":return i("
        "+e+"
        ",1);default:return i(e,0)}}}function ne(e){if(e){e()}}function L(e,t){return Object.prototype.toString.call(e)==="[object "+t+"]"}function A(e){return L(e,"Function")}function N(e){return L(e,"Object")}function ie(e){var t="htmx-internal-data";var r=e[t];if(!r){r=e[t]={}}return r}function I(e){var t=[];if(e){for(var r=0;r=0}function oe(e){if(e.getRootNode&&e.getRootNode()instanceof window.ShadowRoot){return te().body.contains(e.getRootNode().host)}else{return te().body.contains(e)}}function P(e){return e.trim().split(/\s+/)}function se(e,t){for(var r in t){if(t.hasOwnProperty(r)){e[r]=t[r]}}return e}function S(e){try{return JSON.parse(e)}catch(e){y(e);return null}}function M(){var e="htmx:localStorageTest";try{localStorage.setItem(e,e);localStorage.removeItem(e);return true}catch(e){return false}}function D(t){try{var e=new URL(t);if(e){t=e.pathname+e.search}if(!t.match("^/$")){t=t.replace(/\/+$/,"")}return t}catch(e){return t}}function e(e){return xr(te().body,function(){return eval(e)})}function t(t){var e=Y.on("htmx:load",function(e){t(e.detail.elt)});return e}function X(){Y.logger=function(e,t,r){if(console){console.log(t,e,r)}}}function U(){Y.logger=null}function E(e,t){if(t){return e.querySelector(t)}else{return E(te(),e)}}function f(e,t){if(t){return e.querySelectorAll(t)}else{return f(te(),e)}}function B(e,t){e=s(e);if(t){setTimeout(function(){B(e);e=null},t)}else{e.parentElement.removeChild(e)}}function F(e,t,r){e=s(e);if(r){setTimeout(function(){F(e,t);e=null},r)}else{e.classList&&e.classList.add(t)}}function n(e,t,r){e=s(e);if(r){setTimeout(function(){n(e,t);e=null},r)}else{if(e.classList){e.classList.remove(t);if(e.classList.length===0){e.removeAttribute("class")}}}}function V(e,t){e=s(e);e.classList.toggle(t)}function j(e,t){e=s(e);ae(e.parentElement.children,function(e){n(e,t)});F(e,t)}function d(e,t){e=s(e);if(e.closest){return e.closest(t)}else{do{if(e==null||h(e,t)){return e}}while(e=e&&u(e));return null}}function g(e,t){return e.substring(0,t.length)===t}function _(e,t){return e.substring(e.length-t.length)===t}function z(e){var t=e.trim();if(g(t,"<")&&_(t,"/>")){return t.substring(1,t.length-2)}else{return t}}function W(e,t){if(t.indexOf("closest ")===0){return[d(e,z(t.substr(8)))]}else if(t.indexOf("find ")===0){return[E(e,z(t.substr(5)))]}else if(t==="next"){return[e.nextElementSibling]}else if(t.indexOf("next ")===0){return[$(e,z(t.substr(5)))]}else if(t==="previous"){return[e.previousElementSibling]}else if(t.indexOf("previous ")===0){return[G(e,z(t.substr(9)))]}else if(t==="document"){return[document]}else if(t==="window"){return[window]}else if(t==="body"){return[document.body]}else{return te().querySelectorAll(z(t))}}var $=function(e,t){var r=te().querySelectorAll(t);for(var n=0;n=0;n--){var i=r[n];if(i.compareDocumentPosition(e)===Node.DOCUMENT_POSITION_FOLLOWING){return i}}};function le(e,t){if(t){return W(e,t)[0]}else{return W(te().body,e)[0]}}function s(e){if(L(e,"String")){return E(e)}else{return e}}function J(e,t,r){if(A(t)){return{target:te().body,event:e,listener:t}}else{return{target:s(e),event:t,listener:r}}}function Z(t,r,n){Pr(function(){var e=J(t,r,n);e.target.addEventListener(e.event,e.listener)});var e=A(r);return e?r:n}function K(t,r,n){Pr(function(){var e=J(t,r,n);e.target.removeEventListener(e.event,e.listener)});return A(r)?r:n}var he=te().createElement("output");function de(e,t){var r=re(e,t);if(r){if(r==="this"){return[ve(e,t)]}else{var n=W(e,r);if(n.length===0){y('The selector "'+r+'" on '+t+" returned no matches!");return[he]}else{return n}}}}function ve(e,t){return c(e,function(e){return ee(e,t)!=null})}function ge(e){var t=re(e,"hx-target");if(t){if(t==="this"){return ve(e,"hx-target")}else{return le(e,t)}}else{var r=ie(e);if(r.boosted){return te().body}else{return e}}}function me(e){var t=Y.config.attributesToSettle;for(var r=0;r0){o=e.substr(0,e.indexOf(":"));t=e.substr(e.indexOf(":")+1,e.length)}else{o=e}var r=te().querySelectorAll(t);if(r){ae(r,function(e){var t;var r=i.cloneNode(true);t=te().createDocumentFragment();t.appendChild(r);if(!xe(o,e)){t=r}var n={shouldSwap:true,target:e,fragment:t};if(!fe(e,"htmx:oobBeforeSwap",n))return;e=n.target;if(n["shouldSwap"]){De(o,e,e,t,a)}ae(a.elts,function(e){fe(e,"htmx:oobAfterSwap",n)})});i.parentNode.removeChild(i)}else{i.parentNode.removeChild(i);ue(te().body,"htmx:oobErrorNoTarget",{content:i})}return e}function be(e,t,r){var n=re(e,"hx-select-oob");if(n){var i=n.split(",");for(let e=0;e0){var r=t.replace("'","\\'");var n=e.tagName.replace(":","\\:");var i=o.querySelector(n+"[id='"+r+"']");if(i&&i!==o){var a=e.cloneNode();pe(e,i);s.tasks.push(function(){pe(e,a)})}}})}function Ee(e){return function(){n(e,Y.config.addedClass);Dt(e);Ct(e);Ce(e);fe(e,"htmx:load")}}function Ce(e){var t="[autofocus]";var r=h(e,t)?e:e.querySelector(t);if(r!=null){r.focus()}}function a(e,t,r,n){Se(e,r,n);while(r.childNodes.length>0){var i=r.firstChild;F(i,Y.config.addedClass);e.insertBefore(i,t);if(i.nodeType!==Node.TEXT_NODE&&i.nodeType!==Node.COMMENT_NODE){n.tasks.push(Ee(i))}}}function Te(e,t){var r=0;while(r-1){var t=e.replace(/]*>|>)([\s\S]*?)<\/svg>/gim,"");var r=t.match(/]*>|>)([\s\S]*?)<\/title>/im);if(r){return r[2]}}}function Ue(e,t,r,n,i,a){i.title=Xe(n);var o=l(n);if(o){be(r,o,i);o=Me(r,o,a);we(o);return De(e,r,t,o,i)}}function Be(e,t,r){var n=e.getResponseHeader(t);if(n.indexOf("{")===0){var i=S(n);for(var a in i){if(i.hasOwnProperty(a)){var o=i[a];if(!N(o)){o={value:o}}fe(r,a,o)}}}else{var s=n.split(",");for(var l=0;l0){var o=t[0];if(o==="]"){n--;if(n===0){if(a===null){i=i+"true"}t.shift();i+=")})";try{var s=xr(e,function(){return Function(i)()},function(){return true});s.source=i;return s}catch(e){ue(te().body,"htmx:syntax:error",{error:e,source:i});return null}}}else if(o==="["){n++}if($e(o,a,r)){i+="(("+r+"."+o+") ? ("+r+"."+o+") : (window."+o+"))"}else{i=i+o}a=t.shift()}}}function x(e,t){var r="";while(e.length>0&&!e[0].match(t)){r+=e.shift()}return r}var Je="input, textarea, select";function Ze(e){var t=ee(e,"hx-trigger");var r=[];if(t){var n=We(t);do{x(n,ze);var i=n.length;var a=x(n,/[,\[\s]/);if(a!==""){if(a==="every"){var o={trigger:"every"};x(n,ze);o.pollInterval=v(x(n,/[,\[\s]/));x(n,ze);var s=Ge(e,n,"event");if(s){o.eventFilter=s}r.push(o)}else if(a.indexOf("sse:")===0){r.push({trigger:"sse",sseEvent:a.substr(4)})}else{var l={trigger:a};var s=Ge(e,n,"event");if(s){l.eventFilter=s}while(n.length>0&&n[0]!==","){x(n,ze);var u=n.shift();if(u==="changed"){l.changed=true}else if(u==="once"){l.once=true}else if(u==="consume"){l.consume=true}else if(u==="delay"&&n[0]===":"){n.shift();l.delay=v(x(n,p))}else if(u==="from"&&n[0]===":"){n.shift();var f=x(n,p);if(f==="closest"||f==="find"||f==="next"||f==="previous"){n.shift();var c=x(n,p);if(c.length>0){f+=" "+c}}l.from=f}else if(u==="target"&&n[0]===":"){n.shift();l.target=x(n,p)}else if(u==="throttle"&&n[0]===":"){n.shift();l.throttle=v(x(n,p))}else if(u==="queue"&&n[0]===":"){n.shift();l.queue=x(n,p)}else if((u==="root"||u==="threshold")&&n[0]===":"){n.shift();l[u]=x(n,p)}else{ue(e,"htmx:syntax:error",{token:n.shift()})}}r.push(l)}}if(n.length===i){ue(e,"htmx:syntax:error",{token:n.shift()})}x(n,ze)}while(n[0]===","&&n.shift())}if(r.length>0){return r}else if(h(e,"form")){return[{trigger:"submit"}]}else if(h(e,'input[type="button"], input[type="submit"]')){return[{trigger:"click"}]}else if(h(e,Je)){return[{trigger:"change"}]}else{return[{trigger:"click"}]}}function Ke(e){ie(e).cancelled=true}function Ye(e,t,r){var n=ie(e);n.timeout=setTimeout(function(){if(oe(e)&&n.cancelled!==true){if(!nt(r,e,Ut("hx:poll:trigger",{triggerSpec:r,target:e}))){t(e)}Ye(e,t,r)}},r.pollInterval)}function Qe(e){return location.hostname===e.hostname&&Q(e,"href")&&Q(e,"href").indexOf("#")!==0}function et(t,r,e){if(t.tagName==="A"&&Qe(t)&&(t.target===""||t.target==="_self")||t.tagName==="FORM"){r.boosted=true;var n,i;if(t.tagName==="A"){n="get";i=Q(t,"href")}else{var a=Q(t,"method");n=a?a.toLowerCase():"get";if(n==="get"){}i=Q(t,"action")}e.forEach(function(e){it(t,function(e,t){if(d(e,Y.config.disableSelector)){m(e);return}ce(n,i,e,t)},r,e,true)})}}function tt(e,t){if(e.type==="submit"||e.type==="click"){if(t.tagName==="FORM"){return true}if(h(t,'input[type="submit"], button')&&d(t,"form")!==null){return true}if(t.tagName==="A"&&t.href&&(t.getAttribute("href")==="#"||t.getAttribute("href").indexOf("#")!==0)){return true}}return false}function rt(e,t){return ie(e).boosted&&e.tagName==="A"&&t.type==="click"&&(t.ctrlKey||t.metaKey)}function nt(e,t,r){var n=e.eventFilter;if(n){try{return n.call(t,r)!==true}catch(e){ue(te().body,"htmx:eventFilter:error",{error:e,source:n.source});return true}}return false}function it(a,o,e,s,l){var u=ie(a);var t;if(s.from){t=W(a,s.from)}else{t=[a]}if(s.changed){t.forEach(function(e){var t=ie(e);t.lastValue=e.value})}ae(t,function(n){var i=function(e){if(!oe(a)){n.removeEventListener(s.trigger,i);return}if(rt(a,e)){return}if(l||tt(e,a)){e.preventDefault()}if(nt(s,a,e)){return}var t=ie(e);t.triggerSpec=s;if(t.handledFor==null){t.handledFor=[]}if(t.handledFor.indexOf(a)<0){t.handledFor.push(a);if(s.consume){e.stopPropagation()}if(s.target&&e.target){if(!h(e.target,s.target)){return}}if(s.once){if(u.triggeredOnce){return}else{u.triggeredOnce=true}}if(s.changed){var r=ie(n);if(r.lastValue===n.value){return}r.lastValue=n.value}if(u.delayed){clearTimeout(u.delayed)}if(u.throttle){return}if(s.throttle){if(!u.throttle){o(a,e);u.throttle=setTimeout(function(){u.throttle=null},s.throttle)}}else if(s.delay){u.delayed=setTimeout(function(){o(a,e)},s.delay)}else{fe(a,"htmx:trigger");o(a,e)}}};if(e.listenerInfos==null){e.listenerInfos=[]}e.listenerInfos.push({trigger:s.trigger,listener:i,on:n});n.addEventListener(s.trigger,i)})}var at=false;var ot=null;function st(){if(!ot){ot=function(){at=true};window.addEventListener("scroll",ot);setInterval(function(){if(at){at=false;ae(te().querySelectorAll("[hx-trigger='revealed'],[data-hx-trigger='revealed']"),function(e){lt(e)})}},200)}}function lt(t){if(!o(t,"data-hx-revealed")&&k(t)){t.setAttribute("data-hx-revealed","true");var e=ie(t);if(e.initHash){fe(t,"revealed")}else{t.addEventListener("htmx:afterProcessNode",function(e){fe(t,"revealed")},{once:true})}}}function ut(e,t,r){var n=P(r);for(var i=0;i=0){var t=dt(n);setTimeout(function(){ft(s,r,n+1)},t)}};t.onopen=function(e){n=0};ie(s).webSocket=t;t.addEventListener("message",function(e){if(ct(s)){return}var t=e.data;C(s,function(e){t=e.transformResponse(t,null,s)});var r=T(s);var n=l(t);var i=I(n.children);for(var a=0;a0){fe(u,"htmx:validation:halted",i);return}t.send(JSON.stringify(l));if(tt(e,u)){e.preventDefault()}})}else{ue(u,"htmx:noWebSocketSourceError")}}function dt(e){var t=Y.config.wsReconnectDelay;if(typeof t==="function"){return t(e)}if(t==="full-jitter"){var r=Math.min(e,6);var n=1e3*Math.pow(2,r);return n*Math.random()}y('htmx.config.wsReconnectDelay must either be a function or the string "full-jitter"')}function vt(e,t,r){var n=P(r);for(var i=0;i0){var o=n.shift();var s=o.match(/^\s*([a-zA-Z:\-\.]+:)(.*)/);if(a===0&&s){o.split(":");i=s[1].slice(0,-1);r[i]=s[2]}else{r[i]+=o}a+=Nt(o)}for(var l in r){It(e,l,r[l])}}}function Pt(t){Re(t);for(var e=0;eY.config.historyCacheSize){i.shift()}while(i.length>0){try{localStorage.setItem("htmx-history-cache",JSON.stringify(i));break}catch(e){ue(te().body,"htmx:historyCacheError",{cause:e,cache:i});i.shift()}}}function _t(e){if(!M()){return null}e=D(e);var t=S(localStorage.getItem("htmx-history-cache"))||[];for(var r=0;r=200&&this.status<400){fe(te().body,"htmx:historyCacheMissLoad",o);var e=l(this.response);e=e.querySelector("[hx-history-elt],[data-hx-history-elt]")||e;var t=Vt();var r=T(t);var n=Xe(this.response);if(n){var i=E("title");if(i){i.innerHTML=n}else{window.document.title=n}}Pe(t,e,r);Jt(r.tasks);Ft=a;fe(te().body,"htmx:historyRestore",{path:a,cacheMiss:true,serverResponse:this.response})}else{ue(te().body,"htmx:historyCacheMissLoadError",o)}};e.send()}function Kt(e){Wt();e=e||location.pathname+location.search;var t=_t(e);if(t){var r=l(t.content);var n=Vt();var i=T(n);Pe(n,r,i);Jt(i.tasks);document.title=t.title;setTimeout(function(){window.scrollTo(0,t.scroll)},0);Ft=e;fe(te().body,"htmx:historyRestore",{path:e,item:t})}else{if(Y.config.refreshOnHistoryMiss){window.location.reload(true)}else{Zt(e)}}}function Yt(e){var t=de(e,"hx-indicator");if(t==null){t=[e]}ae(t,function(e){var t=ie(e);t.requestCount=(t.requestCount||0)+1;e.classList["add"].call(e.classList,Y.config.requestClass)});return t}function Qt(e){var t=de(e,"hx-disabled-elt");if(t==null){t=[]}ae(t,function(e){var t=ie(e);t.requestCount=(t.requestCount||0)+1;e.setAttribute("disabled","")});return t}function er(e,t){ae(e,function(e){var t=ie(e);t.requestCount=(t.requestCount||0)-1;if(t.requestCount===0){e.classList["remove"].call(e.classList,Y.config.requestClass)}});ae(t,function(e){var t=ie(e);t.requestCount=(t.requestCount||0)-1;if(t.requestCount===0){e.removeAttribute("disabled")}})}function tr(e,t){for(var r=0;r=0}function dr(e,t){var r=t?t:re(e,"hx-swap");var n={swapStyle:ie(e).boosted?"innerHTML":Y.config.defaultSwapStyle,swapDelay:Y.config.defaultSwapDelay,settleDelay:Y.config.defaultSettleDelay};if(Y.config.scrollIntoViewOnBoost&&ie(e).boosted&&!hr(e)){n["show"]="top"}if(r){var i=P(r);if(i.length>0){for(var a=0;a0?l.join(":"):null;n["scroll"]=u;n["scrollTarget"]=f}else if(o.indexOf("show:")===0){var c=o.substr(5);var l=c.split(":");var h=l.pop();var f=l.length>0?l.join(":"):null;n["show"]=h;n["showTarget"]=f}else if(o.indexOf("focus-scroll:")===0){var d=o.substr("focus-scroll:".length);n["focusScroll"]=d=="true"}else if(a==0){n["swapStyle"]=o}else{y("Unknown modifier in hx-swap: "+o)}}}}return n}function vr(e){return re(e,"hx-encoding")==="multipart/form-data"||h(e,"form")&&Q(e,"enctype")==="multipart/form-data"}function gr(t,r,n){var i=null;C(r,function(e){if(i==null){i=e.encodeParameters(t,n,r)}});if(i!=null){return i}else{if(vr(r)){return ur(n)}else{return lr(n)}}}function T(e){return{tasks:[],elts:[e]}}function mr(e,t){var r=e[0];var n=e[e.length-1];if(t.scroll){var i=null;if(t.scrollTarget){i=le(r,t.scrollTarget)}if(t.scroll==="top"&&(r||i)){i=i||r;i.scrollTop=0}if(t.scroll==="bottom"&&(n||i)){i=i||n;i.scrollTop=i.scrollHeight}}if(t.show){var i=null;if(t.showTarget){var a=t.showTarget;if(t.showTarget==="window"){a="body"}i=le(r,a)}if(t.show==="top"&&(r||i)){i=i||r;i.scrollIntoView({block:"start",behavior:Y.config.scrollBehavior})}if(t.show==="bottom"&&(n||i)){i=i||n;i.scrollIntoView({block:"end",behavior:Y.config.scrollBehavior})}}}function pr(e,t,r,n){if(n==null){n={}}if(e==null){return n}var i=ee(e,t);if(i){var a=i.trim();var o=r;if(a==="unset"){return null}if(a.indexOf("javascript:")===0){a=a.substr(11);o=true}else if(a.indexOf("js:")===0){a=a.substr(3);o=true}if(a.indexOf("{")!==0){a="{"+a+"}"}var s;if(o){s=xr(e,function(){return Function("return ("+a+")")()},{})}else{s=S(a)}for(var l in s){if(s.hasOwnProperty(l)){if(n[l]==null){n[l]=s[l]}}}}return pr(u(e),t,r,n)}function xr(e,t,r){if(Y.config.allowEval){return t()}else{ue(e,"htmx:evalDisallowedError");return r}}function yr(e,t){return pr(e,"hx-vars",true,t)}function br(e,t){return pr(e,"hx-vals",false,t)}function wr(e){return se(yr(e),br(e))}function Sr(t,r,n){if(n!==null){try{t.setRequestHeader(r,n)}catch(e){t.setRequestHeader(r,encodeURIComponent(n));t.setRequestHeader(r+"-URI-AutoEncoded","true")}}}function Er(t){if(t.responseURL&&typeof URL!=="undefined"){try{var e=new URL(t.responseURL);return e.pathname+e.search}catch(e){ue(te().body,"htmx:badResponseUrl",{url:t.responseURL})}}}function O(e,t){return e.getAllResponseHeaders().match(t)}function Cr(e,t,r){e=e.toLowerCase();if(r){if(r instanceof Element||L(r,"String")){return ce(e,t,null,null,{targetOverride:s(r),returnPromise:true})}else{return ce(e,t,s(r.source),r.event,{handler:r.handler,headers:r.headers,values:r.values,targetOverride:s(r.target),swapOverride:r.swap,returnPromise:true})}}else{return ce(e,t,null,null,{returnPromise:true})}}function Tr(e){var t=[];while(e){t.push(e);e=e.parentElement}return t}function Or(e,t,r){var n;var i;if(typeof URL==="function"){i=new URL(t,document.location.href);var a=document.location.origin;n=a===i.origin}else{i=t;n=g(t,document.location.origin)}if(Y.config.selfRequestsOnly){if(!n){return false}}return fe(e,"htmx:validateUrl",se({url:i,sameHost:n},r))}function ce(t,r,n,i,a,e){var o=null;var s=null;a=a!=null?a:{};if(a.returnPromise&&typeof Promise!=="undefined"){var l=new Promise(function(e,t){o=e;s=t})}if(n==null){n=te().body}var M=a.handler||qr;if(!oe(n)){ne(o);return l}var u=a.targetOverride||ge(n);if(u==null||u==he){ue(n,"htmx:targetError",{target:ee(n,"hx-target")});ne(s);return l}var f=ie(n);var c=f.lastButtonClicked;if(c){var h=Q(c,"formaction");if(h!=null){r=h}var d=Q(c,"formmethod");if(d!=null){if(d.toLowerCase()!=="dialog"){t=d}}}var v=re(n,"hx-confirm");if(e===undefined){var D=function(e){return ce(t,r,n,i,a,!!e)};var X={target:u,elt:n,path:r,verb:t,triggeringEvent:i,etc:a,issueRequest:D,question:v};if(fe(n,"htmx:confirm",X)===false){ne(o);return l}}var g=n;var m=re(n,"hx-sync");var p=null;var x=false;if(m){var U=m.split(":");var B=U[0].trim();if(B==="this"){g=ve(n,"hx-sync")}else{g=le(n,B)}m=(U[1]||"drop").trim();f=ie(g);if(m==="drop"&&f.xhr&&f.abortable!==true){ne(o);return l}else if(m==="abort"){if(f.xhr){ne(o);return l}else{x=true}}else if(m==="replace"){fe(g,"htmx:abort")}else if(m.indexOf("queue")===0){var F=m.split(" ");p=(F[1]||"last").trim()}}if(f.xhr){if(f.abortable){fe(g,"htmx:abort")}else{if(p==null){if(i){var y=ie(i);if(y&&y.triggerSpec&&y.triggerSpec.queue){p=y.triggerSpec.queue}}if(p==null){p="last"}}if(f.queuedRequests==null){f.queuedRequests=[]}if(p==="first"&&f.queuedRequests.length===0){f.queuedRequests.push(function(){ce(t,r,n,i,a)})}else if(p==="all"){f.queuedRequests.push(function(){ce(t,r,n,i,a)})}else if(p==="last"){f.queuedRequests=[];f.queuedRequests.push(function(){ce(t,r,n,i,a)})}ne(o);return l}}var b=new XMLHttpRequest;f.xhr=b;f.abortable=x;var w=function(){f.xhr=null;f.abortable=false;if(f.queuedRequests!=null&&f.queuedRequests.length>0){var e=f.queuedRequests.shift();e()}};var V=re(n,"hx-prompt");if(V){var S=prompt(V);if(S===null||!fe(n,"htmx:prompt",{prompt:S,target:u})){ne(o);w();return l}}if(v&&!e){if(!confirm(v)){ne(o);w();return l}}var E=fr(n,u,S);if(a.headers){E=se(E,a.headers)}var j=or(n,t);var C=j.errors;var T=j.values;if(a.values){T=se(T,a.values)}var _=wr(n);var z=se(T,_);var O=cr(z,n);if(t!=="get"&&!vr(n)){E["Content-Type"]="application/x-www-form-urlencoded"}if(Y.config.getCacheBusterParam&&t==="get"){O["org.htmx.cache-buster"]=Q(u,"id")||"true"}if(r==null||r===""){r=te().location.href}var R=pr(n,"hx-request");var W=ie(n).boosted;var q=Y.config.methodsThatUseUrlParams.indexOf(t)>=0;var H={boosted:W,useUrlParams:q,parameters:O,unfilteredParameters:z,headers:E,target:u,verb:t,errors:C,withCredentials:a.credentials||R.credentials||Y.config.withCredentials,timeout:a.timeout||R.timeout||Y.config.timeout,path:r,triggeringEvent:i};if(!fe(n,"htmx:configRequest",H)){ne(o);w();return l}r=H.path;t=H.verb;E=H.headers;O=H.parameters;C=H.errors;q=H.useUrlParams;if(C&&C.length>0){fe(n,"htmx:validation:halted",H);ne(o);w();return l}var $=r.split("#");var G=$[0];var L=$[1];var A=r;if(q){A=G;var J=Object.keys(O).length!==0;if(J){if(A.indexOf("?")<0){A+="?"}else{A+="&"}A+=lr(O);if(L){A+="#"+L}}}if(!Or(n,A,H)){ue(n,"htmx:invalidPath",H);ne(s);return l}b.open(t.toUpperCase(),A,true);b.overrideMimeType("text/html");b.withCredentials=H.withCredentials;b.timeout=H.timeout;if(R.noHeaders){}else{for(var N in E){if(E.hasOwnProperty(N)){var Z=E[N];Sr(b,N,Z)}}}var I={xhr:b,target:u,requestConfig:H,etc:a,boosted:W,pathInfo:{requestPath:r,finalRequestPath:A,anchor:L}};b.onload=function(){try{var e=Tr(n);I.pathInfo.responsePath=Er(b);M(n,I);er(k,P);fe(n,"htmx:afterRequest",I);fe(n,"htmx:afterOnLoad",I);if(!oe(n)){var t=null;while(e.length>0&&t==null){var r=e.shift();if(oe(r)){t=r}}if(t){fe(t,"htmx:afterRequest",I);fe(t,"htmx:afterOnLoad",I)}}ne(o);w()}catch(e){ue(n,"htmx:onLoadError",se({error:e},I));throw e}};b.onerror=function(){er(k,P);ue(n,"htmx:afterRequest",I);ue(n,"htmx:sendError",I);ne(s);w()};b.onabort=function(){er(k,P);ue(n,"htmx:afterRequest",I);ue(n,"htmx:sendAbort",I);ne(s);w()};b.ontimeout=function(){er(k,P);ue(n,"htmx:afterRequest",I);ue(n,"htmx:timeout",I);ne(s);w()};if(!fe(n,"htmx:beforeRequest",I)){ne(o);w();return l}var k=Yt(n);var P=Qt(n);ae(["loadstart","loadend","progress","abort"],function(t){ae([b,b.upload],function(e){e.addEventListener(t,function(e){fe(n,"htmx:xhr:"+t,{lengthComputable:e.lengthComputable,loaded:e.loaded,total:e.total})})})});fe(n,"htmx:beforeSend",I);var K=q?null:gr(b,n,O);b.send(K);return l}function Rr(e,t){var r=t.xhr;var n=null;var i=null;if(O(r,/HX-Push:/i)){n=r.getResponseHeader("HX-Push");i="push"}else if(O(r,/HX-Push-Url:/i)){n=r.getResponseHeader("HX-Push-Url");i="push"}else if(O(r,/HX-Replace-Url:/i)){n=r.getResponseHeader("HX-Replace-Url");i="replace"}if(n){if(n==="false"){return{}}else{return{type:i,path:n}}}var a=t.pathInfo.finalRequestPath;var o=t.pathInfo.responsePath;var s=re(e,"hx-push-url");var l=re(e,"hx-replace-url");var u=ie(e).boosted;var f=null;var c=null;if(s){f="push";c=s}else if(l){f="replace";c=l}else if(u){f="push";c=o||a}if(c){if(c==="false"){return{}}if(c==="true"){c=o||a}if(t.pathInfo.anchor&&c.indexOf("#")===-1){c=c+"#"+t.pathInfo.anchor}return{type:f,path:c}}else{return{}}}function qr(l,u){var f=u.xhr;var c=u.target;var e=u.etc;var t=u.requestConfig;if(!fe(l,"htmx:beforeOnLoad",u))return;if(O(f,/HX-Trigger:/i)){Be(f,"HX-Trigger",l)}if(O(f,/HX-Location:/i)){Wt();var r=f.getResponseHeader("HX-Location");var h;if(r.indexOf("{")===0){h=S(r);r=h["path"];delete h["path"]}Cr("GET",r,h).then(function(){$t(r)});return}var n=O(f,/HX-Refresh:/i)&&"true"===f.getResponseHeader("HX-Refresh");if(O(f,/HX-Redirect:/i)){location.href=f.getResponseHeader("HX-Redirect");n&&location.reload();return}if(n){location.reload();return}if(O(f,/HX-Retarget:/i)){u.target=te().querySelector(f.getResponseHeader("HX-Retarget"))}var d=Rr(l,u);var i=f.status>=200&&f.status<400&&f.status!==204;var v=f.response;var a=f.status>=400;var g=Y.config.ignoreTitle;var o=se({shouldSwap:i,serverResponse:v,isError:a,ignoreTitle:g},u);if(!fe(c,"htmx:beforeSwap",o))return;c=o.target;v=o.serverResponse;a=o.isError;g=o.ignoreTitle;u.target=c;u.failed=a;u.successful=!a;if(o.shouldSwap){if(f.status===286){Ke(l)}C(l,function(e){v=e.transformResponse(v,f,l)});if(d.type){Wt()}var s=e.swapOverride;if(O(f,/HX-Reswap:/i)){s=f.getResponseHeader("HX-Reswap")}var h=dr(l,s);if(h.hasOwnProperty("ignoreTitle")){g=h.ignoreTitle}c.classList.add(Y.config.swappingClass);var m=null;var p=null;var x=function(){try{var e=document.activeElement;var t={};try{t={elt:e,start:e?e.selectionStart:null,end:e?e.selectionEnd:null}}catch(e){}var r;if(O(f,/HX-Reselect:/i)){r=f.getResponseHeader("HX-Reselect")}var n=T(c);Ue(h.swapStyle,c,l,v,n,r);if(t.elt&&!oe(t.elt)&&Q(t.elt,"id")){var i=document.getElementById(Q(t.elt,"id"));var a={preventScroll:h.focusScroll!==undefined?!h.focusScroll:!Y.config.defaultFocusScroll};if(i){if(t.start&&i.setSelectionRange){try{i.setSelectionRange(t.start,t.end)}catch(e){}}i.focus(a)}}c.classList.remove(Y.config.swappingClass);ae(n.elts,function(e){if(e.classList){e.classList.add(Y.config.settlingClass)}fe(e,"htmx:afterSwap",u)});if(O(f,/HX-Trigger-After-Swap:/i)){var o=l;if(!oe(l)){o=te().body}Be(f,"HX-Trigger-After-Swap",o)}var s=function(){ae(n.tasks,function(e){e.call()});ae(n.elts,function(e){if(e.classList){e.classList.remove(Y.config.settlingClass)}fe(e,"htmx:afterSettle",u)});if(d.type){fe(te().body,"htmx:beforeHistoryUpdate",se({history:d},u));if(d.type==="push"){$t(d.path);fe(te().body,"htmx:pushedIntoHistory",{path:d.path})}else{Gt(d.path);fe(te().body,"htmx:replacedInHistory",{path:d.path})}}if(u.pathInfo.anchor){var e=te().getElementById(u.pathInfo.anchor);if(e){e.scrollIntoView({block:"start",behavior:"auto"})}}if(n.title&&!g){var t=E("title");if(t){t.innerHTML=n.title}else{window.document.title=n.title}}mr(n.elts,h);if(O(f,/HX-Trigger-After-Settle:/i)){var r=l;if(!oe(l)){r=te().body}Be(f,"HX-Trigger-After-Settle",r)}ne(m)};if(h.settleDelay>0){setTimeout(s,h.settleDelay)}else{s()}}catch(e){ue(l,"htmx:swapError",u);ne(p);throw e}};var y=Y.config.globalViewTransitions;if(h.hasOwnProperty("transition")){y=h.transition}if(y&&fe(l,"htmx:beforeTransition",u)&&typeof Promise!=="undefined"&&document.startViewTransition){var b=new Promise(function(e,t){m=e;p=t});var w=x;x=function(){document.startViewTransition(function(){w();return b})}}if(h.swapDelay>0){setTimeout(x,h.swapDelay)}else{x()}}if(a){ue(l,"htmx:responseError",se({error:"Response Status Error Code "+f.status+" from "+u.pathInfo.requestPath},u))}}var Hr={};function Lr(){return{init:function(e){return null},onEvent:function(e,t){return true},transformResponse:function(e,t,r){return e},isInlineSwap:function(e){return false},handleSwap:function(e,t,r,n){return false},encodeParameters:function(e,t,r){return null}}}function Ar(e,t){if(t.init){t.init(r)}Hr[e]=se(Lr(),t)}function Nr(e){delete Hr[e]}function Ir(e,r,n){if(e==undefined){return r}if(r==undefined){r=[]}if(n==undefined){n=[]}var t=ee(e,"hx-ext");if(t){ae(t.split(","),function(e){e=e.replace(/ /g,"");if(e.slice(0,7)=="ignore:"){n.push(e.slice(7));return}if(n.indexOf(e)<0){var t=Hr[e];if(t&&r.indexOf(t)<0){r.push(t)}}})}return Ir(u(e),r,n)}var kr=false;te().addEventListener("DOMContentLoaded",function(){kr=true});function Pr(e){if(kr||te().readyState==="complete"){e()}else{te().addEventListener("DOMContentLoaded",e)}}function Mr(){if(Y.config.includeIndicatorStyles!==false){te().head.insertAdjacentHTML("beforeend","")}}function Dr(){var e=te().querySelector('meta[name="htmx-config"]');if(e){return S(e.content)}else{return null}}function Xr(){var e=Dr();if(e){Y.config=se(Y.config,e)}}Pr(function(){Xr();Mr();var e=te().body;Dt(e);var t=te().querySelectorAll("[hx-trigger='restored'],[data-hx-trigger='restored']");e.addEventListener("htmx:abort",function(e){var t=e.target;var r=ie(t);if(r&&r.xhr){r.xhr.abort()}});var r=window.onpopstate;window.onpopstate=function(e){if(e.state&&e.state.htmx){Kt();ae(t,function(e){fe(e,"htmx:restored",{document:te(),triggerEvent:fe})})}else{if(r){r(e)}}};setTimeout(function(){fe(e,"htmx:load",{});e=null},0)});return Y}()}); \ No newline at end of file diff --git a/dejacode/static/js/jquery-1.12.4.min.js b/dejacode/static/js/jquery-1.12.4.min.js new file mode 100755 index 00000000..e8364758 --- /dev/null +++ b/dejacode/static/js/jquery-1.12.4.min.js @@ -0,0 +1,5 @@ +/*! jQuery v1.12.4 | (c) jQuery Foundation | jquery.org/license */ +!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=a.document,e=c.slice,f=c.concat,g=c.push,h=c.indexOf,i={},j=i.toString,k=i.hasOwnProperty,l={},m="1.12.4",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return e.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:e.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a){return n.each(this,a)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(e.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor()},push:g,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(n.isPlainObject(c)||(b=n.isArray(c)))?(b?(b=!1,f=a&&n.isArray(a)?a:[]):f=a&&n.isPlainObject(a)?a:{},g[d]=n.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray||function(a){return"array"===n.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){var b=a&&a.toString();return!n.isArray(a)&&b-parseFloat(b)+1>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==n.type(a)||a.nodeType||n.isWindow(a))return!1;try{if(a.constructor&&!k.call(a,"constructor")&&!k.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(!l.ownFirst)for(b in a)return k.call(a,b);for(b in a);return void 0===b||k.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?i[j.call(a)]||"object":typeof a},globalEval:function(b){b&&n.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b){var c,d=0;if(s(a)){for(c=a.length;c>d;d++)if(b.call(a[d],d,a[d])===!1)break}else for(d in a)if(b.call(a[d],d,a[d])===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):g.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(h)return h.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,e,g=0,h=[];if(s(a))for(d=a.length;d>g;g++)e=b(a[g],g,c),null!=e&&h.push(e);else for(g in a)e=b(a[g],g,c),null!=e&&h.push(e);return f.apply([],h)},guid:1,proxy:function(a,b){var c,d,f;return"string"==typeof b&&(f=a[b],b=a,a=f),n.isFunction(a)?(c=e.call(arguments,2),d=function(){return a.apply(b||this,c.concat(e.call(arguments)))},d.guid=a.guid=a.guid||n.guid++,d):void 0},now:function(){return+new Date},support:l}),"function"==typeof Symbol&&(n.fn[Symbol.iterator]=c[Symbol.iterator]),n.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(a,b){i["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=!!a&&"length"in a&&a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ga(),z=ga(),A=ga(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+M+"))|)"+L+"*\\]",O=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+N+")*)|.*)\\)|)",P=new RegExp(L+"+","g"),Q=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),R=new RegExp("^"+L+"*,"+L+"*"),S=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),T=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),U=new RegExp(O),V=new RegExp("^"+M+"$"),W={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M+"|[*])"),ATTR:new RegExp("^"+N),PSEUDO:new RegExp("^"+O),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},X=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,Z=/^[^{]+\{\s*\[native \w/,$=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,_=/[+~]/,aa=/'|\\/g,ba=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),ca=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},da=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(ea){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fa(a,b,d,e){var f,h,j,k,l,o,r,s,w=b&&b.ownerDocument,x=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==x&&9!==x&&11!==x)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==x&&(o=$.exec(a)))if(f=o[1]){if(9===x){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(w&&(j=w.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(o[2])return H.apply(d,b.getElementsByTagName(a)),d;if((f=o[3])&&c.getElementsByClassName&&b.getElementsByClassName)return H.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==x)w=b,s=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(aa,"\\$&"):b.setAttribute("id",k=u),r=g(a),h=r.length,l=V.test(k)?"#"+k:"[id='"+k+"']";while(h--)r[h]=l+" "+qa(r[h]);s=r.join(","),w=_.test(a)&&oa(b.parentNode)||b}if(s)try{return H.apply(d,w.querySelectorAll(s)),d}catch(y){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(Q,"$1"),b,d,e)}function ga(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ha(a){return a[u]=!0,a}function ia(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ja(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function ka(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function la(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function na(a){return ha(function(b){return b=+b,ha(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function oa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=fa.support={},f=fa.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fa.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ia(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ia(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Z.test(n.getElementsByClassName),c.getById=ia(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return"undefined"!=typeof b.getElementsByClassName&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=Z.test(n.querySelectorAll))&&(ia(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ia(function(a){var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Z.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ia(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",O)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Z.test(o.compareDocumentPosition),t=b||Z.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return ka(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?ka(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},fa.matches=function(a,b){return fa(a,null,null,b)},fa.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(T,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fa(b,n,null,[a]).length>0},fa.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fa.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fa.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fa.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fa.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fa.selectors={cacheLength:50,createPseudo:ha,match:W,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ba,ca),a[3]=(a[3]||a[4]||a[5]||"").replace(ba,ca),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fa.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fa.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return W.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&U.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ba,ca).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fa.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(P," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fa.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ha(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ha(function(a){var b=[],c=[],d=h(a.replace(Q,"$1"));return d[u]?ha(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ha(function(a){return function(b){return fa(a,b).length>0}}),contains:ha(function(a){return a=a.replace(ba,ca),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ha(function(a){return V.test(a||"")||fa.error("unsupported lang: "+a),a=a.replace(ba,ca).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Y.test(a.nodeName)},input:function(a){return X.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:na(function(){return[0]}),last:na(function(a,b){return[b-1]}),eq:na(function(a,b,c){return[0>c?c+b:c]}),even:na(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:na(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:na(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:na(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function ra(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j,k=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(j=b[u]||(b[u]={}),i=j[b.uniqueID]||(j[b.uniqueID]={}),(h=i[d])&&h[0]===w&&h[1]===f)return k[2]=h[2];if(i[d]=k,k[2]=a(b,c,g))return!0}}}function sa(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ta(a,b,c){for(var d=0,e=b.length;e>d;d++)fa(a,b[d],c);return c}function ua(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(c&&!c(f,d,e)||(g.push(f),j&&b.push(h)));return g}function va(a,b,c,d,e,f){return d&&!d[u]&&(d=va(d)),e&&!e[u]&&(e=va(e,f)),ha(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ta(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ua(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ua(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ua(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function wa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ra(function(a){return a===b},h,!0),l=ra(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[ra(sa(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return va(i>1&&sa(m),i>1&&qa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(Q,"$1"),c,e>i&&wa(a.slice(i,e)),f>e&&wa(a=a.slice(e)),f>e&&qa(a))}m.push(c)}return sa(m)}function xa(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=F.call(i));u=ua(u)}H.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&fa.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ha(f):f}return h=fa.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xa(e,d)),f.selector=a}return f},i=fa.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ba,ca),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=W.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ba,ca),_.test(j[0].type)&&oa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qa(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,!b||_.test(a)&&oa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ia(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ia(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ja("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ia(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ja("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ia(function(a){return null==a.getAttribute("disabled")})||ja(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fa}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.uniqueSort=n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},v=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},w=n.expr.match.needsContext,x=/^<([\w-]+)\s*\/?>(?:<\/\1>|)$/,y=/^.[^:#\[\.,]*$/;function z(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(y.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return n.inArray(a,b)>-1!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;e>b;b++)if(n.contains(d[b],this))return!0}));for(b=0;e>b;b++)n.find(a,d[b],c);return c=this.pushStack(e>1?n.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(z(this,a||[],!1))},not:function(a){return this.pushStack(z(this,a||[],!0))},is:function(a){return!!z(this,"string"==typeof a&&w.test(a)?n(a):a||[],!1).length}});var A,B=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=n.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||A,"string"==typeof a){if(e="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:B.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),x.test(e[1])&&n.isPlainObject(b))for(e in b)n.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}if(f=d.getElementById(e[2]),f&&f.parentNode){if(f.id!==e[2])return A.find(a);this.length=1,this[0]=f}return this.context=d,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?"undefined"!=typeof c.ready?c.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};C.prototype=n.fn,A=n(d);var D=/^(?:parents|prev(?:Until|All))/,E={children:!0,contents:!0,next:!0,prev:!0};n.fn.extend({has:function(a){var b,c=n(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(n.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=w.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?n.inArray(this[0],n(a)):n.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.uniqueSort(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function F(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return u(a,"parentNode")},parentsUntil:function(a,b,c){return u(a,"parentNode",c)},next:function(a){return F(a,"nextSibling")},prev:function(a){return F(a,"previousSibling")},nextAll:function(a){return u(a,"nextSibling")},prevAll:function(a){return u(a,"previousSibling")},nextUntil:function(a,b,c){return u(a,"nextSibling",c)},prevUntil:function(a,b,c){return u(a,"previousSibling",c)},siblings:function(a){return v((a.parentNode||{}).firstChild,a)},children:function(a){return v(a.firstChild)},contents:function(a){return n.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(E[a]||(e=n.uniqueSort(e)),D.test(a)&&(e=e.reverse())),this.pushStack(e)}});var G=/\S+/g;function H(a){var b={};return n.each(a.match(G)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?H(a):n.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),h>=c&&h--}),this},has:function(a){return a?n.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=!0,c||j.disable(),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().progress(c.notify).done(c.resolve).fail(c.reject):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=e.call(arguments),d=c.length,f=1!==d||a&&n.isFunction(a.promise)?d:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(d){b[a]=this,c[a]=arguments.length>1?e.call(arguments):d,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(d>1)for(i=new Array(d),j=new Array(d),k=new Array(d);d>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().progress(h(b,j,i)).done(h(b,k,c)).fail(g.reject):--f;return f||g.resolveWith(k,c),g.promise()}});var I;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(I.resolveWith(d,[n]),n.fn.triggerHandler&&(n(d).triggerHandler("ready"),n(d).off("ready"))))}});function J(){d.addEventListener?(d.removeEventListener("DOMContentLoaded",K),a.removeEventListener("load",K)):(d.detachEvent("onreadystatechange",K),a.detachEvent("onload",K))}function K(){(d.addEventListener||"load"===a.event.type||"complete"===d.readyState)&&(J(),n.ready())}n.ready.promise=function(b){if(!I)if(I=n.Deferred(),"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll)a.setTimeout(n.ready);else if(d.addEventListener)d.addEventListener("DOMContentLoaded",K),a.addEventListener("load",K);else{d.attachEvent("onreadystatechange",K),a.attachEvent("onload",K);var c=!1;try{c=null==a.frameElement&&d.documentElement}catch(e){}c&&c.doScroll&&!function f(){if(!n.isReady){try{c.doScroll("left")}catch(b){return a.setTimeout(f,50)}J(),n.ready()}}()}return I.promise(b)},n.ready.promise();var L;for(L in n(l))break;l.ownFirst="0"===L,l.inlineBlockNeedsLayout=!1,n(function(){var a,b,c,e;c=d.getElementsByTagName("body")[0],c&&c.style&&(b=d.createElement("div"),e=d.createElement("div"),e.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(e).appendChild(b),"undefined"!=typeof b.style.zoom&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",l.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(e))}),function(){var a=d.createElement("div");l.deleteExpando=!0;try{delete a.test}catch(b){l.deleteExpando=!1}a=null}();var M=function(a){var b=n.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b},N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(O,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c}catch(e){}n.data(a,b,c)}else c=void 0; +}return c}function Q(a){var b;for(b in a)if(("data"!==b||!n.isEmptyObject(a[b]))&&"toJSON"!==b)return!1;return!0}function R(a,b,d,e){if(M(a)){var f,g,h=n.expando,i=a.nodeType,j=i?n.cache:a,k=i?a[h]:a[h]&&h;if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||n.guid++:h),j[k]||(j[k]=i?{}:{toJSON:n.noop}),"object"!=typeof b&&"function"!=typeof b||(e?j[k]=n.extend(j[k],b):j[k].data=n.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[n.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[n.camelCase(b)])):f=g,f}}function S(a,b,c){if(M(a)){var d,e,f=a.nodeType,g=f?n.cache:a,h=f?a[n.expando]:n.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){n.isArray(b)?b=b.concat(n.map(b,n.camelCase)):b in d?b=[b]:(b=n.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!Q(d):!n.isEmptyObject(d))return}(c||(delete g[h].data,Q(g[h])))&&(f?n.cleanData([a],!0):l.deleteExpando||g!=g.window?delete g[h]:g[h]=void 0)}}}n.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?n.cache[a[n.expando]]:a[n.expando],!!a&&!Q(a)},data:function(a,b,c){return R(a,b,c)},removeData:function(a,b){return S(a,b)},_data:function(a,b,c){return R(a,b,c,!0)},_removeData:function(a,b){return S(a,b,!0)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=n.data(f),1===f.nodeType&&!n._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d])));n._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){n.data(this,a)}):arguments.length>1?this.each(function(){n.data(this,a,b)}):f?P(f,a,n.data(f,a)):void 0},removeData:function(a){return this.each(function(){n.removeData(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=n._data(a,b),c&&(!d||n.isArray(c)?d=n._data(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return n._data(a,c)||n._data(a,c,{empty:n.Callbacks("once memory").add(function(){n._removeData(a,b+"queue"),n._removeData(a,c)})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthh;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},Z=/^(?:checkbox|radio)$/i,$=/<([\w:-]+)/,_=/^$|\/(?:java|ecma)script/i,aa=/^\s+/,ba="abbr|article|aside|audio|bdi|canvas|data|datalist|details|dialog|figcaption|figure|footer|header|hgroup|main|mark|meter|nav|output|picture|progress|section|summary|template|time|video";function ca(a){var b=ba.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}!function(){var a=d.createElement("div"),b=d.createDocumentFragment(),c=d.createElement("input");a.innerHTML="
        a",l.leadingWhitespace=3===a.firstChild.nodeType,l.tbody=!a.getElementsByTagName("tbody").length,l.htmlSerialize=!!a.getElementsByTagName("link").length,l.html5Clone="<:nav>"!==d.createElement("nav").cloneNode(!0).outerHTML,c.type="checkbox",c.checked=!0,b.appendChild(c),l.appendChecked=c.checked,a.innerHTML="",l.noCloneChecked=!!a.cloneNode(!0).lastChild.defaultValue,b.appendChild(a),c=d.createElement("input"),c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),a.appendChild(c),l.checkClone=a.cloneNode(!0).cloneNode(!0).lastChild.checked,l.noCloneEvent=!!a.addEventListener,a[n.expando]=1,l.attributes=!a.getAttribute(n.expando)}();var da={option:[1,""],legend:[1,"
        ","
        "],area:[1,"",""],param:[1,"",""],thead:[1,"","
        "],tr:[2,"","
        "],col:[2,"","
        "],td:[3,"","
        "],_default:l.htmlSerialize?[0,"",""]:[1,"X
        ","
        "]};da.optgroup=da.option,da.tbody=da.tfoot=da.colgroup=da.caption=da.thead,da.th=da.td;function ea(a,b){var c,d,e=0,f="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||n.nodeName(d,b)?f.push(d):n.merge(f,ea(d,b));return void 0===b||b&&n.nodeName(a,b)?n.merge([a],f):f}function fa(a,b){for(var c,d=0;null!=(c=a[d]);d++)n._data(c,"globalEval",!b||n._data(b[d],"globalEval"))}var ga=/<|&#?\w+;/,ha=/r;r++)if(g=a[r],g||0===g)if("object"===n.type(g))n.merge(q,g.nodeType?[g]:g);else if(ga.test(g)){i=i||p.appendChild(b.createElement("div")),j=($.exec(g)||["",""])[1].toLowerCase(),m=da[j]||da._default,i.innerHTML=m[1]+n.htmlPrefilter(g)+m[2],f=m[0];while(f--)i=i.lastChild;if(!l.leadingWhitespace&&aa.test(g)&&q.push(b.createTextNode(aa.exec(g)[0])),!l.tbody){g="table"!==j||ha.test(g)?""!==m[1]||ha.test(g)?0:i:i.firstChild,f=g&&g.childNodes.length;while(f--)n.nodeName(k=g.childNodes[f],"tbody")&&!k.childNodes.length&&g.removeChild(k)}n.merge(q,i.childNodes),i.textContent="";while(i.firstChild)i.removeChild(i.firstChild);i=p.lastChild}else q.push(b.createTextNode(g));i&&p.removeChild(i),l.appendChecked||n.grep(ea(q,"input"),ia),r=0;while(g=q[r++])if(d&&n.inArray(g,d)>-1)e&&e.push(g);else if(h=n.contains(g.ownerDocument,g),i=ea(p.appendChild(g),"script"),h&&fa(i),c){f=0;while(g=i[f++])_.test(g.type||"")&&c.push(g)}return i=null,p}!function(){var b,c,e=d.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(l[b]=c in a)||(e.setAttribute(c,"t"),l[b]=e.attributes[c].expando===!1);e=null}();var ka=/^(?:input|select|textarea)$/i,la=/^key/,ma=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,na=/^(?:focusinfocus|focusoutblur)$/,oa=/^([^.]*)(?:\.(.+)|)/;function pa(){return!0}function qa(){return!1}function ra(){try{return d.activeElement}catch(a){}}function sa(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)sa(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=qa;else if(!e)return a;return 1===f&&(g=e,e=function(a){return n().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=n.guid++)),a.each(function(){n.event.add(this,b,e,d,c)})}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=n._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=n.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return"undefined"==typeof n||a&&n.event.triggered===a.type?void 0:n.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(G)||[""],h=b.length;while(h--)f=oa.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=n.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=n.event.special[o]||{},l=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},i),(m=g[o])||(m=g[o]=[],m.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,l):m.push(l),n.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=n.hasData(a)&&n._data(a);if(r&&(k=r.events)){b=(b||"").match(G)||[""],j=b.length;while(j--)if(h=oa.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=m.length;while(f--)g=m[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(m.splice(f,1),g.selector&&m.delegateCount--,l.remove&&l.remove.call(a,g));i&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(k)&&(delete r.handle,n._removeData(a,"events"))}},trigger:function(b,c,e,f){var g,h,i,j,l,m,o,p=[e||d],q=k.call(b,"type")?b.type:b,r=k.call(b,"namespace")?b.namespace.split("."):[];if(i=m=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!na.test(q+n.event.triggered)&&(q.indexOf(".")>-1&&(r=q.split("."),q=r.shift(),r.sort()),h=q.indexOf(":")<0&&"on"+q,b=b[n.expando]?b:new n.Event(q,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=r.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+r.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:n.makeArray(c,[b]),l=n.event.special[q]||{},f||!l.trigger||l.trigger.apply(e,c)!==!1)){if(!f&&!l.noBubble&&!n.isWindow(e)){for(j=l.delegateType||q,na.test(j+q)||(i=i.parentNode);i;i=i.parentNode)p.push(i),m=i;m===(e.ownerDocument||d)&&p.push(m.defaultView||m.parentWindow||a)}o=0;while((i=p[o++])&&!b.isPropagationStopped())b.type=o>1?j:l.bindType||q,g=(n._data(i,"events")||{})[b.type]&&n._data(i,"handle"),g&&g.apply(i,c),g=h&&i[h],g&&g.apply&&M(i)&&(b.result=g.apply(i,c),b.result===!1&&b.preventDefault());if(b.type=q,!f&&!b.isDefaultPrevented()&&(!l._default||l._default.apply(p.pop(),c)===!1)&&M(e)&&h&&e[q]&&!n.isWindow(e)){m=e[h],m&&(e[h]=null),n.event.triggered=q;try{e[q]()}catch(s){}n.event.triggered=void 0,m&&(e[h]=m)}return b.result}},dispatch:function(a){a=n.event.fix(a);var b,c,d,f,g,h=[],i=e.call(arguments),j=(n._data(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())a.rnamespace&&!a.rnamespace.test(g.namespace)||(a.handleObj=g,a.data=g.data,d=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==d&&(a.result=d)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&("click"!==a.type||isNaN(a.button)||a.button<1))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>-1:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h]","i"),va=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi,wa=/\s*$/g,Aa=ca(d),Ba=Aa.appendChild(d.createElement("div"));function Ca(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function Da(a){return a.type=(null!==n.find.attr(a,"type"))+"/"+a.type,a}function Ea(a){var b=ya.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Fa(a,b){if(1===b.nodeType&&n.hasData(a)){var c,d,e,f=n._data(a),g=n._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)n.event.add(b,c,h[c][d])}g.data&&(g.data=n.extend({},g.data))}}function Ga(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!l.noCloneEvent&&b[n.expando]){e=n._data(b);for(d in e.events)n.removeEvent(b,d,e.handle);b.removeAttribute(n.expando)}"script"===c&&b.text!==a.text?(Da(b).text=a.text,Ea(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),l.html5Clone&&a.innerHTML&&!n.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&Z.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:"input"!==c&&"textarea"!==c||(b.defaultValue=a.defaultValue)}}function Ha(a,b,c,d){b=f.apply([],b);var e,g,h,i,j,k,m=0,o=a.length,p=o-1,q=b[0],r=n.isFunction(q);if(r||o>1&&"string"==typeof q&&!l.checkClone&&xa.test(q))return a.each(function(e){var f=a.eq(e);r&&(b[0]=q.call(this,e,f.html())),Ha(f,b,c,d)});if(o&&(k=ja(b,a[0].ownerDocument,!1,a,d),e=k.firstChild,1===k.childNodes.length&&(k=e),e||d)){for(i=n.map(ea(k,"script"),Da),h=i.length;o>m;m++)g=k,m!==p&&(g=n.clone(g,!0,!0),h&&n.merge(i,ea(g,"script"))),c.call(a[m],g,m);if(h)for(j=i[i.length-1].ownerDocument,n.map(i,Ea),m=0;h>m;m++)g=i[m],_.test(g.type||"")&&!n._data(g,"globalEval")&&n.contains(j,g)&&(g.src?n._evalUrl&&n._evalUrl(g.src):n.globalEval((g.text||g.textContent||g.innerHTML||"").replace(za,"")));k=e=null}return a}function Ia(a,b,c){for(var d,e=b?n.filter(b,a):a,f=0;null!=(d=e[f]);f++)c||1!==d.nodeType||n.cleanData(ea(d)),d.parentNode&&(c&&n.contains(d.ownerDocument,d)&&fa(ea(d,"script")),d.parentNode.removeChild(d));return a}n.extend({htmlPrefilter:function(a){return a.replace(va,"<$1>")},clone:function(a,b,c){var d,e,f,g,h,i=n.contains(a.ownerDocument,a);if(l.html5Clone||n.isXMLDoc(a)||!ua.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(Ba.innerHTML=a.outerHTML,Ba.removeChild(f=Ba.firstChild)),!(l.noCloneEvent&&l.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(d=ea(f),h=ea(a),g=0;null!=(e=h[g]);++g)d[g]&&Ga(e,d[g]);if(b)if(c)for(h=h||ea(a),d=d||ea(f),g=0;null!=(e=h[g]);g++)Fa(e,d[g]);else Fa(a,f);return d=ea(f,"script"),d.length>0&&fa(d,!i&&ea(a,"script")),d=h=e=null,f},cleanData:function(a,b){for(var d,e,f,g,h=0,i=n.expando,j=n.cache,k=l.attributes,m=n.event.special;null!=(d=a[h]);h++)if((b||M(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)m[e]?n.event.remove(d,e):n.removeEvent(d,e,g.handle);j[f]&&(delete j[f],k||"undefined"==typeof d.removeAttribute?d[i]=void 0:d.removeAttribute(i),c.push(f))}}}),n.fn.extend({domManip:Ha,detach:function(a){return Ia(this,a,!0)},remove:function(a){return Ia(this,a)},text:function(a){return Y(this,function(a){return void 0===a?n.text(this):this.empty().append((this[0]&&this[0].ownerDocument||d).createTextNode(a))},null,a,arguments.length)},append:function(){return Ha(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ca(this,a);b.appendChild(a)}})},prepend:function(){return Ha(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ca(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return Ha(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return Ha(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&n.cleanData(ea(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&n.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return Y(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(ta,""):void 0;if("string"==typeof a&&!wa.test(a)&&(l.htmlSerialize||!ua.test(a))&&(l.leadingWhitespace||!aa.test(a))&&!da[($.exec(a)||["",""])[1].toLowerCase()]){a=n.htmlPrefilter(a);try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(ea(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=[];return Ha(this,arguments,function(b){var c=this.parentNode;n.inArray(this,a)<0&&(n.cleanData(ea(this)),c&&c.replaceChild(b,this))},a)}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=0,e=[],f=n(a),h=f.length-1;h>=d;d++)c=d===h?this:this.clone(!0),n(f[d])[b](c),g.apply(e,c.get());return this.pushStack(e)}});var Ja,Ka={HTML:"block",BODY:"block"};function La(a,b){var c=n(b.createElement(a)).appendTo(b.body),d=n.css(c[0],"display");return c.detach(),d}function Ma(a){var b=d,c=Ka[a];return c||(c=La(a,b),"none"!==c&&c||(Ja=(Ja||n("