diff --git a/muk_web_theme/LICENSE b/muk_web_theme/LICENSE new file mode 100644 index 0000000..faf7bf4 --- /dev/null +++ b/muk_web_theme/LICENSE @@ -0,0 +1,619 @@ + 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 \ No newline at end of file diff --git a/muk_web_theme/__init__.py b/muk_web_theme/__init__.py new file mode 100644 index 0000000..3a0a7c9 --- /dev/null +++ b/muk_web_theme/__init__.py @@ -0,0 +1,20 @@ +################################################################################### +# +# Copyright (C) 2018 MuK IT GmbH +# +# 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 . +# +################################################################################### + +from . import models \ No newline at end of file diff --git a/muk_web_theme/__manifest__.py b/muk_web_theme/__manifest__.py new file mode 100644 index 0000000..f1ca259 --- /dev/null +++ b/muk_web_theme/__manifest__.py @@ -0,0 +1,59 @@ +################################################################################### +# +# Copyright (C) 2018 MuK IT GmbH +# +# 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 . +# +################################################################################### + +{ + "name": "MuK Backend Theme", + "summary": "Odoo 12.0 community backend theme", + "version": "12.0.1.0.0", + "category": "Themes/Backend", + "license": "AGPL-3", + "author": "MuK IT", + "website": "http://www.mukit.at", + "live_test_url": "https://demo.mukit.at/web/login", + "contributors": [ + "Mathias Markl ", + ], + "depends": [ + "muk_web_utils", + ], + "excludes": [ + "web_enterprise", + ], + "data": [ + "template/assets.xml", + "template/web.xml", + "views/res_users.xml", + "views/res_config_settings_view.xml", + "data/res_company.xml", + ], + "qweb": [ + "static/src/xml/*.xml", + ], + "images": [ + 'static/description/banner.png' + ], + "external_dependencies": { + "python": [], + "bin": [], + }, + "application": False, + "installable": True, + "auto_install": False, + "sequence": "999", +} \ No newline at end of file diff --git a/muk_web_theme/data/res_company.xml b/muk_web_theme/data/res_company.xml new file mode 100644 index 0000000..623df9f --- /dev/null +++ b/muk_web_theme/data/res_company.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + diff --git a/muk_web_theme/doc/changelog.rst b/muk_web_theme/doc/changelog.rst new file mode 100644 index 0000000..d62cbe4 --- /dev/null +++ b/muk_web_theme/doc/changelog.rst @@ -0,0 +1,4 @@ +`1.0.0` +------- + +- Init version diff --git a/muk_web_theme/doc/index.rst b/muk_web_theme/doc/index.rst new file mode 100644 index 0000000..149fff1 --- /dev/null +++ b/muk_web_theme/doc/index.rst @@ -0,0 +1,123 @@ +============= +MuK Web Theme +============= + +This module offers a mobile compatible design for Odoo Community. Furthermore +it allows the user to define some design preferences. So he can choose the +size of the sidebar and the position of the chatter. In addition, the background +image of the app menu can be set for each company. + +Installation +============ + +To install this module, you need to: + +Download the module and add it to your Odoo addons folder. Afterward, log on to +your Odoo server and go to the Apps menu. Trigger the debug mode and update the +list by clicking on the "Update Apps List" link. Now install the module by +clicking on the install button. + +Another way to install this module is via the package management for Python +(`PyPI `_). + +To install our modules using the package manager make sure +`odoo-autodiscover `_ is installed +correctly. Then open a console and install the module by entering the following +command: + +``pip install --extra-index-url https://nexus.mukit.at/repository/odoo/simple `` + +The module name consists of the Odoo version and the module name, where +underscores are replaced by a dash. + +**Module:** + +``odoo-addon-`` + +**Example:** + +``sudo -H pip3 install --extra-index-url https://nexus.mukit.at/repository/odoo/simple odoo11-addon-muk-utils`` + +Once the installation has been successfully completed, the app is already in the +correct folder. Log on to your Odoo server and go to the Apps menu. Trigger the +debug mode and update the list by clicking on the "Update Apps List" link. Now +install the module by clicking on the install button. + +The biggest advantage of this variant is that you can now also update the app +using the "pip" command. To do this, enter the following command in your console: + +``pip install --upgrade --extra-index-url https://nexus.mukit.at/repository/odoo/simple `` + +When the process is finished, restart your server and update the application in +Odoo. The steps are the same as for the installation only the button has changed +from "Install" to "Upgrade". + +You can also view available Apps directly in our `repository `_ +and find a more detailed installation guide on our `website `_. + +For modules licensed under OPL-1, you will receive access data when you purchase +the module. If the modules were not purchased directly from +`MuK IT `_ please contact our support (support@mukit.at) +with a confirmation of purchase to receive the corresponding access data. + +Upgrade +============ + +To upgrade this module, you need to: + +Download the module and add it to your Odoo addons folder. Restart the server +and log on to your Odoo server. Select the Apps menu and upgrade the module by +clicking on the upgrade button. + +If you installed the module using the "pip" command, you can also update the +module in the same way. Just type the following command into the console: + +``pip install --upgrade --extra-index-url https://nexus.mukit.at/repository/odoo/simple `` + +When the process is finished, restart your server and update the application in +Odoo, just like you would normally. + +Configuration +============= + +No additional configuration is needed to use this module. + +Usage +============= + +TODO + +Credits +======= + +Contributors +------------ + +* Mathias Markl + +Images +------------ + +Some pictures are based on or inspired by: + +* `Freepik `_ + +Projects +------------ + +Parts of the module are based on or inspired by: + +* `Web Responsive `_ +* `Openworx Backend Theme `_ + +Author & Maintainer +------------------- + +This module is maintained by the `MuK IT GmbH `_. + +MuK IT is an Austrian company specialized in customizing and extending Odoo. +We develop custom solutions for your individual needs to help you focus on +your strength and expertise to grow your business. + +If you want to get in touch please contact us via mail +(sale@mukit.at) or visit our website (https://mukit.at). diff --git a/muk_web_theme/models/__init__.py b/muk_web_theme/models/__init__.py new file mode 100644 index 0000000..29c0a2c --- /dev/null +++ b/muk_web_theme/models/__init__.py @@ -0,0 +1,22 @@ +################################################################################### +# +# Copyright (C) 2018 MuK IT GmbH +# +# 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 . +# +################################################################################### + +from . import res_users +from . import res_company +from . import res_config_settings diff --git a/muk_web_theme/models/res_company.py b/muk_web_theme/models/res_company.py new file mode 100644 index 0000000..347bc7c --- /dev/null +++ b/muk_web_theme/models/res_company.py @@ -0,0 +1,28 @@ +################################################################################### +# +# Copyright (C) 2018 MuK IT GmbH +# +# 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 . +# +################################################################################### + +from odoo import models, fields + +class ResCompany(models.Model): + + _inherit = 'res.company' + + background_image = fields.Binary( + string="Apps Menu Background Image", + attachment=True) \ No newline at end of file diff --git a/muk_web_theme/models/res_config_settings.py b/muk_web_theme/models/res_config_settings.py new file mode 100644 index 0000000..ada2544 --- /dev/null +++ b/muk_web_theme/models/res_config_settings.py @@ -0,0 +1,28 @@ +################################################################################### +# +# Copyright (C) 2017 MuK IT GmbH +# +# 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 . +# +################################################################################### + +from odoo import api, fields, models + +class ResConfigSettings(models.TransientModel): + + _inherit = 'res.config.settings' + + theme_background_image = fields.Binary( + related="company_id.background_image", + readonly=False) \ No newline at end of file diff --git a/muk_web_theme/models/res_users.py b/muk_web_theme/models/res_users.py new file mode 100644 index 0000000..65c6b0f --- /dev/null +++ b/muk_web_theme/models/res_users.py @@ -0,0 +1,53 @@ +################################################################################### +# +# Copyright (C) 2018 MuK IT GmbH +# +# 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 . +# +################################################################################### + +from odoo import models, fields + +class ResUsers(models.Model): + + _inherit = 'res.users' + + sidebar_type = fields.Selection( + selection=[ + ('invisible', 'Invisible'), + ('small', 'Small'), + ('large', 'Large') + ], + string="Sidebar Type", + default='large', + required=True) + + chatter_position = fields.Selection( + selection=[ + ('normal', 'Normal'), + ('sided', 'Sided'), + ], + string="Chatter Position", + default='sided', + required=True) + + def __init__(self, pool, cr): + super(ResUsers, self).__init__(pool, cr) + type(self).SELF_READABLE_FIELDS = list(self.SELF_READABLE_FIELDS) + type(self).SELF_WRITEABLE_FIELDS = list(self.SELF_WRITEABLE_FIELDS) + type(self).SELF_READABLE_FIELDS.extend(['sidebar_type']) + type(self).SELF_READABLE_FIELDS.extend(['sidebar_type']) + type(self).SELF_WRITEABLE_FIELDS.extend(['chatter_position']) + type(self).SELF_WRITEABLE_FIELDS.extend(['chatter_position']) + diff --git a/muk_web_theme/static/description/banner.png b/muk_web_theme/static/description/banner.png new file mode 100644 index 0000000..2648acc Binary files /dev/null and b/muk_web_theme/static/description/banner.png differ diff --git a/muk_web_theme/static/description/icon.png b/muk_web_theme/static/description/icon.png new file mode 100644 index 0000000..35f7a43 Binary files /dev/null and b/muk_web_theme/static/description/icon.png differ diff --git a/muk_web_theme/static/description/icon.svg b/muk_web_theme/static/description/icon.svg new file mode 100644 index 0000000..93d5a08 --- /dev/null +++ b/muk_web_theme/static/description/icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/muk_web_theme/static/description/index.html b/muk_web_theme/static/description/index.html new file mode 100644 index 0000000..d269aec --- /dev/null +++ b/muk_web_theme/static/description/index.html @@ -0,0 +1,124 @@ +
+
+

MuK Backend Theme

+

Odoo 12.0 community backend theme

+

MuK IT GmbH - + www.mukit.at

+
+ +
+
+
+ +
+
+
+

Overview

+

This module offers a mobile compatible design + for Odoo Community. Furthermore it allows the user to define some + design preferences. So he can choose the size of the sidebar and the + position of the chatter. In addition, the background image of the app + menu can be set for each company.

+
+
+
+ +
+
+
+

Desktop Interface

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

Mobile Interface

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

Demo

+
+
+
User:
+
+
+
apps
+
+
+
Password:
+
+
+
demo
+
+
+ +
+ +
+

Help and Support

+
Feel free to + contact us, if you need any help with your Odoo integration or + addiontal features.
+ + +
\ No newline at end of file diff --git a/muk_web_theme/static/description/logo.png b/muk_web_theme/static/description/logo.png new file mode 100644 index 0000000..9427ce3 Binary files /dev/null and b/muk_web_theme/static/description/logo.png differ diff --git a/muk_web_theme/static/description/screenshot.png b/muk_web_theme/static/description/screenshot.png new file mode 100644 index 0000000..f0b7b14 Binary files /dev/null and b/muk_web_theme/static/description/screenshot.png differ diff --git a/muk_web_theme/static/description/screenshot_apps.png b/muk_web_theme/static/description/screenshot_apps.png new file mode 100644 index 0000000..186fea1 Binary files /dev/null and b/muk_web_theme/static/description/screenshot_apps.png differ diff --git a/muk_web_theme/static/description/screenshot_chatter.png b/muk_web_theme/static/description/screenshot_chatter.png new file mode 100644 index 0000000..c6e4a37 Binary files /dev/null and b/muk_web_theme/static/description/screenshot_chatter.png differ diff --git a/muk_web_theme/static/description/screenshot_mobile_apps.png b/muk_web_theme/static/description/screenshot_mobile_apps.png new file mode 100644 index 0000000..18684c8 Binary files /dev/null and b/muk_web_theme/static/description/screenshot_mobile_apps.png differ diff --git a/muk_web_theme/static/description/screenshot_mobile_kanban.png b/muk_web_theme/static/description/screenshot_mobile_kanban.png new file mode 100644 index 0000000..3a782a0 Binary files /dev/null and b/muk_web_theme/static/description/screenshot_mobile_kanban.png differ diff --git a/muk_web_theme/static/description/screenshot_mobile_menu.png b/muk_web_theme/static/description/screenshot_mobile_menu.png new file mode 100644 index 0000000..cd61707 Binary files /dev/null and b/muk_web_theme/static/description/screenshot_mobile_menu.png differ diff --git a/muk_web_theme/static/description/screenshot_mobile_search.png b/muk_web_theme/static/description/screenshot_mobile_search.png new file mode 100644 index 0000000..1d69260 Binary files /dev/null and b/muk_web_theme/static/description/screenshot_mobile_search.png differ diff --git a/muk_web_theme/static/libs/jquery-bootstrap-scrolling-tabs/jquery-bootstrap-scrolling-tabs.css b/muk_web_theme/static/libs/jquery-bootstrap-scrolling-tabs/jquery-bootstrap-scrolling-tabs.css new file mode 100644 index 0000000..0fce147 --- /dev/null +++ b/muk_web_theme/static/libs/jquery-bootstrap-scrolling-tabs/jquery-bootstrap-scrolling-tabs.css @@ -0,0 +1,61 @@ +/** + * jquery-bootstrap-scrolling-tabs + * @version v2.4.0 + * @link https://github.com/mikejacobson/jquery-bootstrap-scrolling-tabs + * @author Mike Jacobson + * @license MIT License, http://www.opensource.org/licenses/MIT + */ +.scrtabs-tab-container * { + box-sizing: border-box; } + +.scrtabs-tab-container { + height: 42px; } + .scrtabs-tab-container .tab-content { + clear: left; } + +.scrtabs-tab-container.scrtabs-bootstrap4 .scrtabs-tabs-movable-container > .navbar-nav { + -ms-flex-direction: row; + flex-direction: row; } + +.scrtabs-tabs-fixed-container { + float: left; + height: 42px; + overflow: hidden; + width: 100%; } + +.scrtabs-tabs-movable-container { + position: relative; } + .scrtabs-tabs-movable-container .tab-content { + display: none; } + +.scrtabs-tab-container.scrtabs-rtl .scrtabs-tabs-movable-container > ul.nav-tabs { + padding-right: 0; } + +.scrtabs-tab-scroll-arrow { + border: 1px solid #dddddd; + color: #428bca; + display: none; + float: left; + font-size: 12px; + padding: 0.55rem 0.5rem; + width: 20px; } + .scrtabs-tab-scroll-arrow:hover { + background-color: #eeeeee; } + +.scrtabs-tab-scroll-arrow, +.scrtabs-tab-scroll-arrow .scrtabs-click-target { + cursor: pointer; } + +.scrtabs-tab-scroll-arrow.scrtabs-with-click-target { + cursor: default; } + +.scrtabs-tab-scroll-arrow.scrtabs-disable, +.scrtabs-tab-scroll-arrow.scrtabs-disable .scrtabs-click-target { + color: #ddd; + cursor: default; } + +.scrtabs-tab-scroll-arrow.scrtabs-disable:hover { + background-color: initial; } + +.scrtabs-tabs-fixed-container ul.nav-tabs > li { + white-space: nowrap; } \ No newline at end of file diff --git a/muk_web_theme/static/libs/jquery-bootstrap-scrolling-tabs/jquery-bootstrap-scrolling-tabs.js b/muk_web_theme/static/libs/jquery-bootstrap-scrolling-tabs/jquery-bootstrap-scrolling-tabs.js new file mode 100644 index 0000000..4b33cb1 --- /dev/null +++ b/muk_web_theme/static/libs/jquery-bootstrap-scrolling-tabs/jquery-bootstrap-scrolling-tabs.js @@ -0,0 +1,1963 @@ +/** + * jquery-bootstrap-scrolling-tabs + * @version v2.4.0 + * @link https://github.com/mikejacobson/jquery-bootstrap-scrolling-tabs + * @author Mike Jacobson + * @license MIT License, http://www.opensource.org/licenses/MIT + */ +/** + * jQuery plugin version of Angular directive angular-bootstrap-scrolling-tabs: + * https://github.com/mikejacobson/angular-bootstrap-scrolling-tabs + * + * Usage: + * + * Use case #1: HTML-defined tabs + * ------------------------------ + * Demo: http://plnkr.co/edit/thyD0grCxIjyU4PoTt4x?p=preview + * + * Sample HTML: + * + * + * + * + * + *
+ *
Tab 1 content...
+ *
Tab 2 content...
+ *
Tab 3 content...
+ *
Tab 4 content...
+ *
+ * + * + * JavaScript: + * + * $('.nav-tabs').scrollingTabs(); + * + * + * Use Case #2: Data-driven tabs + * ----------------------------- + * Demo: http://plnkr.co/edit/MWBjLnTvJeetjU3NEimg?p=preview + * + * Sample HTML: + * + * + *
+ * + * + * JavaScript: + * + * $('#tabs-inside-here').scrollingTabs({ + * tabs: tabs, // required + * propPaneId: 'paneId', // optional + * propTitle: 'title', // optional + * propActive: 'active', // optional + * propDisabled: 'disabled', // optional + * propContent: 'content', // optional + * ignoreTabPanes: false, // optional + * scrollToTabEdge: false, // optional + * disableScrollArrowsOnFullyScrolled: false, // optional + * reverseScroll: false // optional + * }); + * + * Settings/Options: + * + * tabs: tabs data array + * prop*: name of your tab object's property name that + * corresponds to that required tab property if + * your property name is different than the + * standard name (paneId, title, etc.) + * tabsLiContent: + * optional string array used to define custom HTML + * for each tab's
  • element. Each entry is an HTML + * string defining the tab
  • element for the + * corresponding tab in the tabs array. + * The default for a tab is: + * '
  • ' + * So, for example, if you had 3 tabs and you needed + * a custom 'tooltip' attribute on each one, your + * tabsLiContent array might look like this: + * [ + * '', + * '', + * '' + * ] + * This plunk demonstrates its usage (in conjunction + * with tabsPostProcessors): + * http://plnkr.co/edit/ugJLMk7lmDCuZQziQ0k0 + * tabsPostProcessors: + * optional array of functions, each one associated + * with an entry in the tabs array. When a tab element + * has been created, its associated post-processor + * function will be called with two arguments: the + * newly created $li and $a jQuery elements for that tab. + * This allows you to, for example, attach a custom + * event listener to each anchor tag. + * This plunk demonstrates its usage (in conjunction + * with tabsLiContent): + * http://plnkr.co/edit/ugJLMk7lmDCuZQziQ0k0 + * ignoreTabPanes: relevant for data-driven tabs only--set to true if + * you want the plugin to only touch the tabs + * and to not generate the tab pane elements + * that go in .tab-content. By default, the plugin + * will generate the tab panes based on the content + * property in your tab data, if a content property + * is present. + * scrollToTabEdge: set to true if you want to force full-width tabs + * to display at the left scroll arrow. i.e., if the + * scrolling stops with only half a tab showing, + * it will snap the tab to its edge so the full tab + * shows. + * disableScrollArrowsOnFullyScrolled: + * set to true if you want the left scroll arrow to + * disable when the tabs are scrolled fully left, + * and the right scroll arrow to disable when the tabs + * are scrolled fully right. + * reverseScroll: + * set to true if you want the left scroll arrow to + * slide the tabs left instead of right, and the right + * scroll arrow to slide the tabs right. + * enableSwiping: + * set to true if you want to enable horizontal swiping + * for touch screens. + * widthMultiplier: + * set to a value less than 1 if you want the tabs + * container to be less than the full width of its + * parent element. For example, set it to 0.5 if you + * want the tabs container to be half the width of + * its parent. + * tabClickHandler: + * a callback function to execute any time a tab is clicked. + * The function is simply passed as the event handler + * to jQuery's .on(), so the function will receive + * the jQuery event as an argument, and the 'this' + * inside the function will be the clicked tab's anchor + * element. + * cssClassLeftArrow, cssClassRightArrow: + * custom values for the class attributes for the + * left and right scroll arrows. The defaults are + * 'glyphicon glyphicon-chevron-left' and + * 'glyphicon glyphicon-chevron-right'. + * Using different icons might require you to add + * custom styling to the arrows to position the icons + * correctly; the arrows can be targeted with these + * selectors: + * .scrtabs-tab-scroll-arrow + * .scrtabs-tab-scroll-arrow-left + * .scrtabs-tab-scroll-arrow-right + * leftArrowContent, rightArrowContent: + * custom HTML string for the left and right scroll + * arrows. This will override any custom cssClassLeftArrow + * and cssClassRightArrow settings. + * For example, if you wanted to use svg icons, you + * could set them like so: + * + * leftArrowContent: [ + * '
    ', + * ' ', + * ' ', + * ' ', + * '
    ' + * ].join(''), + * rightArrowContent: [ + * '
    ', + * ' ', + * ' ', + * ' ', + * '
    ' + * ].join('') + * + * You would then need to add some CSS to make them + * work correctly if you don't give them the + * default scrtabs-tab-scroll-arrow classes. + * This plunk shows it working with svg icons: + * http://plnkr.co/edit/2MdZCAnLyeU40shxaol3?p=preview + * + * When using this option, you can also mark a child + * element within the arrow content as the click target + * if you don't want the entire content to be + * clickable. You do that my adding the CSS class + * 'scrtabs-click-target' to the element that should + * be clickable, like so: + * + * leftArrowContent: [ + * '
    ', + * ' ', + * '
    ' + * ].join(''), + * rightArrowContent: [ + * '
    ', + * ' ', + * '
    ' + * ].join('') + * + * enableRtlSupport: + * set to true if you want your site to support + * right-to-left languages. If true, the plugin will + * check the page's tag for attribute dir="rtl" + * and will adjust its behavior accordingly. + * bootstrapVersion: + * set to 4 if you're using Boostrap 4. Default is 3. + * Bootstrap 4 handles some things differently than 3 + * (e.g., the 'active' class gets applied to the tab's + * 'li > a' element rather than the 'li' itself). + * + * + * On tabs data change: + * + * $('#tabs-inside-here').scrollingTabs('refresh'); + * + * On tabs data change, if you want the active tab to be set based on + * the updated tabs data (i.e., you want to override the current + * active tab setting selected by the user), for example, if you + * added a new tab and you want it to be the active tab: + * + * $('#tabs-inside-here').scrollingTabs('refresh', { + * forceActiveTab: true + * }); + * + * Any options that can be passed into the plugin can be set on the + * plugin's 'defaults' object instead so you don't have to pass them in: + * + * $.fn.scrollingTabs.defaults.tabs = tabs; + * $.fn.scrollingTabs.defaults.forceActiveTab = true; + * $.fn.scrollingTabs.defaults.scrollToTabEdge = true; + * $.fn.scrollingTabs.defaults.disableScrollArrowsOnFullyScrolled = true; + * $.fn.scrollingTabs.defaults.reverseScroll = true; + * $.fn.scrollingTabs.defaults.widthMultiplier = 0.5; + * $.fn.scrollingTabs.defaults.tabClickHandler = function () { }; + * + * + * Methods + * ----------------------------- + * - refresh + * On window resize, the tabs should refresh themselves, but to force a refresh: + * + * $('.nav-tabs').scrollingTabs('refresh'); + * + * - scrollToActiveTab + * On window resize, the active tab will automatically be scrolled to + * if it ends up offscreen, but you can also programmatically force a + * scroll to the active tab any time (if, for example, you're + * programmatically setting the active tab) by calling the + * 'scrollToActiveTab' method: + * + * $('.nav-tabs').scrollingTabs('scrollToActiveTab'); + * + * + * Events + * ----------------------------- + * The plugin triggers event 'ready.scrtabs' when the tabs have + * been wrapped in the scroller and are ready for viewing: + * + * $('.nav-tabs') + * .scrollingTabs() + * .on('ready.scrtabs', function() { + * // tabs ready, do my other stuff... + * }); + * + * $('#tabs-inside-here') + * .scrollingTabs({ tabs: tabs }) + * .on('ready.scrtabs', function() { + * // tabs ready, do my other stuff... + * }); + * + * + * Destroying + * ----------------------------- + * To destroy: + * + * $('.nav-tabs').scrollingTabs('destroy'); + * + * $('#tabs-inside-here').scrollingTabs('destroy'); + * + * If you were wrapping markup, the markup will be restored; if your tabs + * were data-driven, the tabs will be destroyed along with the plugin. + * + */ + +;(function ($, window) { + 'use strict'; + /* jshint unused:false */ + + /* exported CONSTANTS */ + var CONSTANTS = { + CONTINUOUS_SCROLLING_TIMEOUT_INTERVAL: 50, // timeout interval for repeatedly moving the tabs container + // by one increment while the mouse is held down--decrease to + // make mousedown continous scrolling faster + SCROLL_OFFSET_FRACTION: 6, // each click moves the container this fraction of the fixed container--decrease + // to make the tabs scroll farther per click + + DATA_KEY_DDMENU_MODIFIED: 'scrtabsddmenumodified', + DATA_KEY_IS_MOUSEDOWN: 'scrtabsismousedown', + + CSS_CLASSES: { + BOOTSTRAP4: 'scrtabs-bootstrap4', + RTL: 'scrtabs-rtl', + SCROLL_ARROW_CLICK_TARGET: 'scrtabs-click-target', + SCROLL_ARROW_DISABLE: 'scrtabs-disable', + SCROLL_ARROW_WITH_CLICK_TARGET: 'scrtabs-with-click-target' + }, + + SLIDE_DIRECTION: { + LEFT: 1, + RIGHT: 2 + }, + + EVENTS: { + CLICK: 'click.scrtabs', + DROPDOWN_MENU_HIDE: 'hide.bs.dropdown.scrtabs', + DROPDOWN_MENU_SHOW: 'show.bs.dropdown.scrtabs', + FORCE_REFRESH: 'forcerefresh.scrtabs', + MOUSEDOWN: 'mousedown.scrtabs', + MOUSEUP: 'mouseup.scrtabs', + TABS_READY: 'ready.scrtabs', + TOUCH_END: 'touchend.scrtabs', + TOUCH_MOVE: 'touchmove.scrtabs', + TOUCH_START: 'touchstart.scrtabs', + WINDOW_RESIZE: 'resize.scrtabs' + } + }; + + // smartresize from Paul Irish (debounced window resize) + (function (sr) { + var debounce = function (func, threshold, execAsap) { + var timeout; + + return function debounced() { + var obj = this, args = arguments; + function delayed() { + if (!execAsap) { + func.apply(obj, args); + } + timeout = null; + } + + if (timeout) { + clearTimeout(timeout); + } else if (execAsap) { + func.apply(obj, args); + } + + timeout = setTimeout(delayed, threshold || 100); + }; + }; + $.fn[sr] = function (fn, customEventName) { + var eventName = customEventName || CONSTANTS.EVENTS.WINDOW_RESIZE; + return fn ? this.bind(eventName, debounce(fn)) : this.trigger(sr); + }; + + })('smartresizeScrtabs'); + + /* *********************************************************************************** + * ElementsHandler - Class that each instance of ScrollingTabsControl will instantiate + * **********************************************************************************/ + function ElementsHandler(scrollingTabsControl) { + var ehd = this; + + ehd.stc = scrollingTabsControl; + } + + // ElementsHandler prototype methods + (function (p) { + p.initElements = function (options) { + var ehd = this; + + ehd.setElementReferences(options); + ehd.setEventListeners(options); + }; + + p.listenForTouchEvents = function () { + var ehd = this, + stc = ehd.stc, + smv = stc.scrollMovement, + ev = CONSTANTS.EVENTS; + + var touching = false; + var touchStartX; + var startingContainerLeftPos; + var newLeftPos; + + stc.$movableContainer + .on(ev.TOUCH_START, function (e) { + touching = true; + startingContainerLeftPos = stc.movableContainerLeftPos; + touchStartX = e.originalEvent.changedTouches[0].pageX; + }) + .on(ev.TOUCH_END, function () { + touching = false; + }) + .on(ev.TOUCH_MOVE, function (e) { + if (!touching) { + return; + } + + var touchPageX = e.originalEvent.changedTouches[0].pageX; + var diff = touchPageX - touchStartX; + if (stc.rtl) { + diff = -diff; + } + var minPos; + + newLeftPos = startingContainerLeftPos + diff; + if (newLeftPos > 0) { + newLeftPos = 0; + } else { + minPos = smv.getMinPos(); + if (newLeftPos < minPos) { + newLeftPos = minPos; + } + } + stc.movableContainerLeftPos = newLeftPos; + + var leftOrRight = stc.rtl ? 'right' : 'left'; + stc.$movableContainer.css(leftOrRight, smv.getMovableContainerCssLeftVal()); + smv.refreshScrollArrowsDisabledState(); + }); + }; + + p.refreshAllElementSizes = function () { + var ehd = this, + stc = ehd.stc, + smv = stc.scrollMovement, + scrollArrowsWereVisible = stc.scrollArrowsVisible, + actionsTaken = { + didScrollToActiveTab: false + }, + isPerformingSlideAnim = false, + minPos; + + ehd.setElementWidths(); + ehd.setScrollArrowVisibility(); + + // this could have been a window resize or the removal of a + // dynamic tab, so make sure the movable container is positioned + // correctly because, if it is far to the left and we increased the + // window width, it's possible that the tabs will be too far left, + // beyond the min pos. + if (stc.scrollArrowsVisible) { + // make sure container not too far left + minPos = smv.getMinPos(); + + isPerformingSlideAnim = smv.scrollToActiveTab({ + isOnWindowResize: true + }); + + if (!isPerformingSlideAnim) { + smv.refreshScrollArrowsDisabledState(); + + if (stc.rtl) { + if (stc.movableContainerRightPos < minPos) { + smv.incrementMovableContainerLeft(minPos); + } + } else { + if (stc.movableContainerLeftPos < minPos) { + smv.incrementMovableContainerRight(minPos); + } + } + } + + actionsTaken.didScrollToActiveTab = true; + + } else if (scrollArrowsWereVisible) { + // scroll arrows went away after resize, so position movable container at 0 + stc.movableContainerLeftPos = 0; + smv.slideMovableContainerToLeftPos(); + } + + return actionsTaken; + }; + + p.setElementReferences = function (settings) { + var ehd = this, + stc = ehd.stc, + $tabsContainer = stc.$tabsContainer, + $leftArrow, + $rightArrow, + $leftArrowClickTarget, + $rightArrowClickTarget; + + stc.isNavPills = false; + + if (stc.rtl) { + $tabsContainer.addClass(CONSTANTS.CSS_CLASSES.RTL); + } + + if (stc.usingBootstrap4) { + $tabsContainer.addClass(CONSTANTS.CSS_CLASSES.BOOTSTRAP4); + } + + stc.$fixedContainer = $tabsContainer.find('.scrtabs-tabs-fixed-container'); + $leftArrow = stc.$fixedContainer.prev(); + $rightArrow = stc.$fixedContainer.next(); + + // if we have custom arrow content, we might have a click target defined + if (settings.leftArrowContent) { + $leftArrowClickTarget = $leftArrow.find('.' + CONSTANTS.CSS_CLASSES.SCROLL_ARROW_CLICK_TARGET); + } + + if (settings.rightArrowContent) { + $rightArrowClickTarget = $rightArrow.find('.' + CONSTANTS.CSS_CLASSES.SCROLL_ARROW_CLICK_TARGET); + } + + if ($leftArrowClickTarget && $leftArrowClickTarget.length) { + $leftArrow.addClass(CONSTANTS.CSS_CLASSES.SCROLL_ARROW_WITH_CLICK_TARGET); + } else { + $leftArrowClickTarget = $leftArrow; + } + + if ($rightArrowClickTarget && $rightArrowClickTarget.length) { + $rightArrow.addClass(CONSTANTS.CSS_CLASSES.SCROLL_ARROW_WITH_CLICK_TARGET); + } else { + $rightArrowClickTarget = $rightArrow; + } + + stc.$movableContainer = $tabsContainer.find('.scrtabs-tabs-movable-container'); + stc.$tabsUl = $tabsContainer.find('.nav-tabs'); + + // check for pills + if (!stc.$tabsUl.length) { + stc.$tabsUl = $tabsContainer.find('.nav-pills'); + + if (stc.$tabsUl.length) { + stc.isNavPills = true; + } + } + + stc.$tabsLiCollection = stc.$tabsUl.find('> li'); + + stc.$slideLeftArrow = stc.reverseScroll ? $leftArrow : $rightArrow; + stc.$slideLeftArrowClickTarget = stc.reverseScroll ? $leftArrowClickTarget : $rightArrowClickTarget; + stc.$slideRightArrow = stc.reverseScroll ? $rightArrow : $leftArrow; + stc.$slideRightArrowClickTarget = stc.reverseScroll ? $rightArrowClickTarget : $leftArrowClickTarget; + stc.$scrollArrows = stc.$slideLeftArrow.add(stc.$slideRightArrow); + + stc.$win = $(window); + }; + + p.setElementWidths = function () { + var ehd = this, + stc = ehd.stc; + + stc.winWidth = stc.$win.width(); + stc.scrollArrowsCombinedWidth = stc.$slideLeftArrow.outerWidth() + stc.$slideRightArrow.outerWidth(); + + ehd.setFixedContainerWidth(); + ehd.setMovableContainerWidth(); + }; + + p.setEventListeners = function (settings) { + var ehd = this, + stc = ehd.stc, + evh = stc.eventHandlers, + ev = CONSTANTS.EVENTS, + resizeEventName = ev.WINDOW_RESIZE + stc.instanceId; + + if (settings.enableSwiping) { + ehd.listenForTouchEvents(); + } + + stc.$slideLeftArrowClickTarget + .off('.scrtabs') + .on(ev.MOUSEDOWN, function (e) { evh.handleMousedownOnSlideMovContainerLeftArrow.call(evh, e); }) + .on(ev.MOUSEUP, function (e) { evh.handleMouseupOnSlideMovContainerLeftArrow.call(evh, e); }) + .on(ev.CLICK, function (e) { evh.handleClickOnSlideMovContainerLeftArrow.call(evh, e); }); + + stc.$slideRightArrowClickTarget + .off('.scrtabs') + .on(ev.MOUSEDOWN, function (e) { evh.handleMousedownOnSlideMovContainerRightArrow.call(evh, e); }) + .on(ev.MOUSEUP, function (e) { evh.handleMouseupOnSlideMovContainerRightArrow.call(evh, e); }) + .on(ev.CLICK, function (e) { evh.handleClickOnSlideMovContainerRightArrow.call(evh, e); }); + + if (stc.tabClickHandler) { + stc.$tabsLiCollection + .find('a[data-toggle="tab"]') + .off(ev.CLICK) + .on(ev.CLICK, stc.tabClickHandler); + } + + stc.$win + .off(resizeEventName) + .smartresizeScrtabs(function (e) { evh.handleWindowResize.call(evh, e); }, resizeEventName); + + $('body').on(CONSTANTS.EVENTS.FORCE_REFRESH, stc.elementsHandler.refreshAllElementSizes.bind(stc.elementsHandler)); + }; + + p.setFixedContainerWidth = function () { + var ehd = this, + stc = ehd.stc, + tabsContainerRect = stc.$tabsContainer.get(0).getBoundingClientRect(); + /** + * @author poletaew + * It solves problem with rounding by jQuery.outerWidth + * If we have real width 100.5 px, jQuery.outerWidth returns us 101 px and we get layout's fail + */ + stc.fixedContainerWidth = tabsContainerRect.width || (tabsContainerRect.right - tabsContainerRect.left); + stc.fixedContainerWidth = stc.fixedContainerWidth * stc.widthMultiplier; + + stc.$fixedContainer.width(stc.fixedContainerWidth); + }; + + p.setFixedContainerWidthForHiddenScrollArrows = function () { + var ehd = this, + stc = ehd.stc; + + stc.$fixedContainer.width(stc.fixedContainerWidth); + }; + + p.setFixedContainerWidthForVisibleScrollArrows = function () { + var ehd = this, + stc = ehd.stc; + + stc.$fixedContainer.width(stc.fixedContainerWidth - stc.scrollArrowsCombinedWidth); + }; + + p.setMovableContainerWidth = function () { + var ehd = this, + stc = ehd.stc, + $tabLi = stc.$tabsUl.find('> li'); + + stc.movableContainerWidth = 0; + + if ($tabLi.length) { + + $tabLi.each(function () { + var $li = $(this), + totalMargin = 0; + + if (stc.isNavPills) { // pills have a margin-left, tabs have no margin + totalMargin = parseInt($li.css('margin-left'), 10) + parseInt($li.css('margin-right'), 10); + } + + stc.movableContainerWidth += ($li.outerWidth() + totalMargin); + }); + + stc.movableContainerWidth += 1; + + // if the tabs don't span the width of the page, force the + // movable container width to full page width so the bottom + // border spans the page width instead of just spanning the + // width of the tabs + if (stc.movableContainerWidth < stc.fixedContainerWidth) { + stc.movableContainerWidth = stc.fixedContainerWidth; + } + } + + stc.$movableContainer.width(stc.movableContainerWidth); + }; + + p.setScrollArrowVisibility = function () { + var ehd = this, + stc = ehd.stc, + shouldBeVisible = stc.movableContainerWidth > stc.fixedContainerWidth; + + if (shouldBeVisible && !stc.scrollArrowsVisible) { + stc.$scrollArrows.show(); + stc.scrollArrowsVisible = true; + } else if (!shouldBeVisible && stc.scrollArrowsVisible) { + stc.$scrollArrows.hide(); + stc.scrollArrowsVisible = false; + } + + if (stc.scrollArrowsVisible) { + ehd.setFixedContainerWidthForVisibleScrollArrows(); + } else { + ehd.setFixedContainerWidthForHiddenScrollArrows(); + } + }; + + }(ElementsHandler.prototype)); + + /* *********************************************************************************** + * EventHandlers - Class that each instance of ScrollingTabsControl will instantiate + * **********************************************************************************/ + function EventHandlers(scrollingTabsControl) { + var evh = this; + + evh.stc = scrollingTabsControl; + } + + // prototype methods + (function (p){ + p.handleClickOnSlideMovContainerLeftArrow = function () { + var evh = this, + stc = evh.stc; + + stc.scrollMovement.incrementMovableContainerLeft(); + }; + + p.handleClickOnSlideMovContainerRightArrow = function () { + var evh = this, + stc = evh.stc; + + stc.scrollMovement.incrementMovableContainerRight(); + }; + + p.handleMousedownOnSlideMovContainerLeftArrow = function () { + var evh = this, + stc = evh.stc; + + stc.$slideLeftArrowClickTarget.data(CONSTANTS.DATA_KEY_IS_MOUSEDOWN, true); + stc.scrollMovement.continueSlideMovableContainerLeft(); + }; + + p.handleMousedownOnSlideMovContainerRightArrow = function () { + var evh = this, + stc = evh.stc; + + stc.$slideRightArrowClickTarget.data(CONSTANTS.DATA_KEY_IS_MOUSEDOWN, true); + stc.scrollMovement.continueSlideMovableContainerRight(); + }; + + p.handleMouseupOnSlideMovContainerLeftArrow = function () { + var evh = this, + stc = evh.stc; + + stc.$slideLeftArrowClickTarget.data(CONSTANTS.DATA_KEY_IS_MOUSEDOWN, false); + }; + + p.handleMouseupOnSlideMovContainerRightArrow = function () { + var evh = this, + stc = evh.stc; + + stc.$slideRightArrowClickTarget.data(CONSTANTS.DATA_KEY_IS_MOUSEDOWN, false); + }; + + p.handleWindowResize = function () { + var evh = this, + stc = evh.stc, + newWinWidth = stc.$win.width(); + + if (newWinWidth === stc.winWidth) { + return false; + } + + stc.winWidth = newWinWidth; + stc.elementsHandler.refreshAllElementSizes(); + }; + + }(EventHandlers.prototype)); + + /* *********************************************************************************** + * ScrollMovement - Class that each instance of ScrollingTabsControl will instantiate + * **********************************************************************************/ + function ScrollMovement(scrollingTabsControl) { + var smv = this; + + smv.stc = scrollingTabsControl; + } + + // prototype methods + (function (p) { + + p.continueSlideMovableContainerLeft = function () { + var smv = this, + stc = smv.stc; + + setTimeout(function() { + if (stc.movableContainerLeftPos <= smv.getMinPos() || + !stc.$slideLeftArrowClickTarget.data(CONSTANTS.DATA_KEY_IS_MOUSEDOWN)) { + return; + } + + if (!smv.incrementMovableContainerLeft()) { // haven't reached max left + smv.continueSlideMovableContainerLeft(); + } + }, CONSTANTS.CONTINUOUS_SCROLLING_TIMEOUT_INTERVAL); + }; + + p.continueSlideMovableContainerRight = function () { + var smv = this, + stc = smv.stc; + + setTimeout(function() { + if (stc.movableContainerLeftPos >= 0 || + !stc.$slideRightArrowClickTarget.data(CONSTANTS.DATA_KEY_IS_MOUSEDOWN)) { + return; + } + + if (!smv.incrementMovableContainerRight()) { // haven't reached max right + smv.continueSlideMovableContainerRight(); + } + }, CONSTANTS.CONTINUOUS_SCROLLING_TIMEOUT_INTERVAL); + }; + + p.decrementMovableContainerLeftPos = function (minPos) { + var smv = this, + stc = smv.stc; + + stc.movableContainerLeftPos -= (stc.fixedContainerWidth / CONSTANTS.SCROLL_OFFSET_FRACTION); + if (stc.movableContainerLeftPos < minPos) { + stc.movableContainerLeftPos = minPos; + } else if (stc.scrollToTabEdge) { + smv.setMovableContainerLeftPosToTabEdge(CONSTANTS.SLIDE_DIRECTION.LEFT); + + if (stc.movableContainerLeftPos < minPos) { + stc.movableContainerLeftPos = minPos; + } + } + }; + + p.disableSlideLeftArrow = function () { + var smv = this, + stc = smv.stc; + + if (!stc.disableScrollArrowsOnFullyScrolled || !stc.scrollArrowsVisible) { + return; + } + + stc.$slideLeftArrow.addClass(CONSTANTS.CSS_CLASSES.SCROLL_ARROW_DISABLE); + }; + + p.disableSlideRightArrow = function () { + var smv = this, + stc = smv.stc; + + if (!stc.disableScrollArrowsOnFullyScrolled || !stc.scrollArrowsVisible) { + return; + } + + stc.$slideRightArrow.addClass(CONSTANTS.CSS_CLASSES.SCROLL_ARROW_DISABLE); + }; + + p.enableSlideLeftArrow = function () { + var smv = this, + stc = smv.stc; + + if (!stc.disableScrollArrowsOnFullyScrolled || !stc.scrollArrowsVisible) { + return; + } + + stc.$slideLeftArrow.removeClass(CONSTANTS.CSS_CLASSES.SCROLL_ARROW_DISABLE); + }; + + p.enableSlideRightArrow = function () { + var smv = this, + stc = smv.stc; + + if (!stc.disableScrollArrowsOnFullyScrolled || !stc.scrollArrowsVisible) { + return; + } + + stc.$slideRightArrow.removeClass(CONSTANTS.CSS_CLASSES.SCROLL_ARROW_DISABLE); + }; + + p.getMinPos = function () { + var smv = this, + stc = smv.stc; + + return stc.scrollArrowsVisible ? (stc.fixedContainerWidth - stc.movableContainerWidth - stc.scrollArrowsCombinedWidth) : 0; + }; + + p.getMovableContainerCssLeftVal = function () { + var smv = this, + stc = smv.stc; + + return (stc.movableContainerLeftPos === 0) ? '0' : stc.movableContainerLeftPos + 'px'; + }; + + p.incrementMovableContainerLeft = function () { + var smv = this, + stc = smv.stc, + minPos = smv.getMinPos(); + + smv.decrementMovableContainerLeftPos(minPos); + smv.slideMovableContainerToLeftPos(); + smv.enableSlideRightArrow(); + + // return true if we're fully left, false otherwise + return (stc.movableContainerLeftPos === minPos); + }; + + p.incrementMovableContainerRight = function (minPos) { + var smv = this, + stc = smv.stc; + + // if minPos passed in, the movable container was beyond the minPos + if (minPos) { + stc.movableContainerLeftPos = minPos; + } else { + stc.movableContainerLeftPos += (stc.fixedContainerWidth / CONSTANTS.SCROLL_OFFSET_FRACTION); + + if (stc.movableContainerLeftPos > 0) { + stc.movableContainerLeftPos = 0; + } else if (stc.scrollToTabEdge) { + smv.setMovableContainerLeftPosToTabEdge(CONSTANTS.SLIDE_DIRECTION.RIGHT); + } + } + + smv.slideMovableContainerToLeftPos(); + smv.enableSlideLeftArrow(); + + // return true if we're fully right, false otherwise + // left pos of 0 is the movable container's max position (farthest right) + return (stc.movableContainerLeftPos === 0); + }; + + p.refreshScrollArrowsDisabledState = function() { + var smv = this, + stc = smv.stc; + + if (!stc.disableScrollArrowsOnFullyScrolled || !stc.scrollArrowsVisible) { + return; + } + + if (stc.movableContainerLeftPos >= 0) { // movable container fully right + smv.disableSlideRightArrow(); + smv.enableSlideLeftArrow(); + return; + } + + if (stc.movableContainerLeftPos <= smv.getMinPos()) { // fully left + smv.disableSlideLeftArrow(); + smv.enableSlideRightArrow(); + return; + } + + smv.enableSlideLeftArrow(); + smv.enableSlideRightArrow(); + }; + + p.scrollToActiveTab = function () { + var smv = this, + stc = smv.stc, + $activeTab, + $activeTabAnchor, + activeTabLeftPos, + activeTabRightPos, + rightArrowLeftPos, + activeTabWidth, + leftPosOffset, + offsetToMiddle, + leftScrollArrowWidth, + rightScrollArrowWidth; + + if (!stc.scrollArrowsVisible) { + return; + } + + if (stc.usingBootstrap4) { + $activeTabAnchor = stc.$tabsUl.find('li > .nav-link.active'); + if ($activeTabAnchor.length) { + $activeTab = $activeTabAnchor.parent(); + } + } else { + $activeTab = stc.$tabsUl.find('li.active'); + } + + if (!$activeTab || !$activeTab.length) { + return; + } + + rightScrollArrowWidth = stc.$slideRightArrow.outerWidth(); + activeTabWidth = $activeTab.outerWidth(); + + /** + * @author poletaew + * We need relative offset (depends on $fixedContainer), don't absolute + */ + activeTabLeftPos = $activeTab.offset().left - stc.$fixedContainer.offset().left; + activeTabRightPos = activeTabLeftPos + activeTabWidth; + + rightArrowLeftPos = stc.fixedContainerWidth - rightScrollArrowWidth; + + if (stc.rtl) { + leftScrollArrowWidth = stc.$slideLeftArrow.outerWidth(); + + if (activeTabLeftPos < 0) { // active tab off left side + stc.movableContainerLeftPos += activeTabLeftPos; + smv.slideMovableContainerToLeftPos(); + return true; + } else { // active tab off right side + if (activeTabRightPos > rightArrowLeftPos) { + stc.movableContainerLeftPos += (activeTabRightPos - rightArrowLeftPos) + (2 * rightScrollArrowWidth); + smv.slideMovableContainerToLeftPos(); + return true; + } + } + } else { + if (activeTabRightPos > rightArrowLeftPos) { // active tab off right side + leftPosOffset = activeTabRightPos - rightArrowLeftPos + rightScrollArrowWidth; + offsetToMiddle = stc.fixedContainerWidth / 2; + leftPosOffset += offsetToMiddle - (activeTabWidth / 2); + stc.movableContainerLeftPos -= leftPosOffset; + smv.slideMovableContainerToLeftPos(); + return true; + } else { + leftScrollArrowWidth = stc.$slideLeftArrow.outerWidth(); + if (activeTabLeftPos < 0) { // active tab off left side + offsetToMiddle = stc.fixedContainerWidth / 2; + stc.movableContainerLeftPos += (-activeTabLeftPos) + offsetToMiddle - (activeTabWidth / 2); + smv.slideMovableContainerToLeftPos(); + return true; + } + } + } + + return false; + }; + + p.setMovableContainerLeftPosToTabEdge = function (slideDirection) { + var smv = this, + stc = smv.stc, + offscreenWidth = -stc.movableContainerLeftPos, + totalTabWidth = 0; + + // make sure LeftPos is set so that a tab edge will be against the + // left scroll arrow so we won't have a partial, cut-off tab + stc.$tabsLiCollection.each(function () { + var tabWidth = $(this).width(); + + totalTabWidth += tabWidth; + + if (totalTabWidth > offscreenWidth) { + stc.movableContainerLeftPos = (slideDirection === CONSTANTS.SLIDE_DIRECTION.RIGHT) ? -(totalTabWidth - tabWidth) : -totalTabWidth; + return false; // exit .each() loop + } + + }); + }; + + p.slideMovableContainerToLeftPos = function () { + var smv = this, + stc = smv.stc, + minPos = smv.getMinPos(), + leftOrRightVal; + + if (stc.movableContainerLeftPos > 0) { + stc.movableContainerLeftPos = 0; + } else if (stc.movableContainerLeftPos < minPos) { + stc.movableContainerLeftPos = minPos; + } + + stc.movableContainerLeftPos = stc.movableContainerLeftPos / 1; + leftOrRightVal = smv.getMovableContainerCssLeftVal(); + + smv.performingSlideAnim = true; + + var targetPos = stc.rtl ? { right: leftOrRightVal } : { left: leftOrRightVal }; + + stc.$movableContainer.stop().animate(targetPos, 'slow', function __slideAnimComplete() { + var newMinPos = smv.getMinPos(); + + smv.performingSlideAnim = false; + + // if we slid past the min pos--which can happen if you resize the window + // quickly--move back into position + if (stc.movableContainerLeftPos < newMinPos) { + smv.decrementMovableContainerLeftPos(newMinPos); + + targetPos = stc.rtl ? { right: smv.getMovableContainerCssLeftVal() } : { left: smv.getMovableContainerCssLeftVal() }; + + stc.$movableContainer.stop().animate(targetPos, 'fast', function() { + smv.refreshScrollArrowsDisabledState(); + }); + } else { + smv.refreshScrollArrowsDisabledState(); + } + }); + }; + + }(ScrollMovement.prototype)); + + /* ********************************************************************** + * ScrollingTabsControl - Class that each directive will instantiate + * **********************************************************************/ + function ScrollingTabsControl($tabsContainer) { + var stc = this; + + stc.$tabsContainer = $tabsContainer; + stc.instanceId = $.fn.scrollingTabs.nextInstanceId++; + + stc.movableContainerLeftPos = 0; + stc.scrollArrowsVisible = false; + stc.scrollToTabEdge = false; + stc.disableScrollArrowsOnFullyScrolled = false; + stc.reverseScroll = false; + stc.widthMultiplier = 1; + + stc.scrollMovement = new ScrollMovement(stc); + stc.eventHandlers = new EventHandlers(stc); + stc.elementsHandler = new ElementsHandler(stc); + } + + // prototype methods + (function (p) { + p.initTabs = function (options, $scroller, readyCallback, attachTabContentToDomCallback) { + var stc = this, + elementsHandler = stc.elementsHandler, + num; + + if (options.enableRtlSupport && $('html').attr('dir') === 'rtl') { + stc.rtl = true; + } + + if (options.scrollToTabEdge) { + stc.scrollToTabEdge = true; + } + + if (options.disableScrollArrowsOnFullyScrolled) { + stc.disableScrollArrowsOnFullyScrolled = true; + } + + if (options.reverseScroll) { + stc.reverseScroll = true; + } + + if (options.widthMultiplier !== 1) { + num = Number(options.widthMultiplier); // handle string value + + if (!isNaN(num)) { + stc.widthMultiplier = num; + } + } + + if (options.bootstrapVersion.toString().charAt(0) === '4') { + stc.usingBootstrap4 = true; + } + + setTimeout(initTabsAfterTimeout, 100); + + function initTabsAfterTimeout() { + var actionsTaken; + + // if we're just wrapping non-data-driven tabs, the user might + // have the .nav-tabs hidden to prevent the clunky flash of + // multi-line tabs on page refresh, so we need to make sure + // they're visible before trying to wrap them + $scroller.find('.nav-tabs').show(); + + elementsHandler.initElements(options); + actionsTaken = elementsHandler.refreshAllElementSizes(); + + $scroller.css('visibility', 'visible'); + + if (attachTabContentToDomCallback) { + attachTabContentToDomCallback(); + } + + if (readyCallback) { + readyCallback(); + } + } + }; + + p.scrollToActiveTab = function(options) { + var stc = this, + smv = stc.scrollMovement; + + smv.scrollToActiveTab(options); + }; + }(ScrollingTabsControl.prototype)); + + + /* exported buildNavTabsAndTabContentForTargetElementInstance */ + var tabElements = (function () { + + return { + getElTabPaneForLi: getElTabPaneForLi, + getNewElNavTabs: getNewElNavTabs, + getNewElScrollerElementWrappingNavTabsInstance: getNewElScrollerElementWrappingNavTabsInstance, + getNewElTabAnchor: getNewElTabAnchor, + getNewElTabContent: getNewElTabContent, + getNewElTabLi: getNewElTabLi, + getNewElTabPane: getNewElTabPane + }; + + /////////////////// + + // ---- retrieve existing elements from the DOM ---------- + function getElTabPaneForLi($li) { + return $($li.find('a').attr('href')); + } + + + // ---- create new elements ---------- + function getNewElNavTabs() { + return $(''); + } + + function getNewElScrollerElementWrappingNavTabsInstance($navTabsInstance, settings) { + var $tabsContainer = $('
    '), + leftArrowContent = settings.leftArrowContent || '
    ', + $leftArrow = $(leftArrowContent), + rightArrowContent = settings.rightArrowContent || '
    ', + $rightArrow = $(rightArrowContent), + $fixedContainer = $('
    '), + $movableContainer = $('
    '); + + if (settings.disableScrollArrowsOnFullyScrolled) { + $leftArrow.add($rightArrow).addClass(CONSTANTS.CSS_CLASSES.SCROLL_ARROW_DISABLE); + } + + return $tabsContainer + .append($leftArrow, + $fixedContainer.append($movableContainer.append($navTabsInstance)), + $rightArrow); + } + + function getNewElTabAnchor(tab, propNames) { + return $('') + .attr('href', '#' + tab[propNames.paneId]) + .html(tab[propNames.title]); + } + + function getNewElTabContent() { + return $('
    '); + } + + function getNewElTabLi(tab, propNames, options) { + var liContent = options.tabLiContent || '
  • ', + $li = $(liContent), + $a = getNewElTabAnchor(tab, propNames).appendTo($li); + + if (tab[propNames.disabled]) { + $li.addClass('disabled'); + $a.attr('data-toggle', ''); + } else if (options.forceActiveTab && tab[propNames.active]) { + $li.addClass('active'); + } + + if (options.tabPostProcessor) { + options.tabPostProcessor($li, $a); + } + + return $li; + } + + function getNewElTabPane(tab, propNames, options) { + var $pane = $('
    ') + .attr('id', tab[propNames.paneId]) + .html(tab[propNames.content]); + + if (options.forceActiveTab && tab[propNames.active]) { + $pane.addClass('active'); + } + + return $pane; + } + + + }()); // tabElements + + var tabUtils = (function () { + + return { + didTabOrderChange: didTabOrderChange, + getIndexOfClosestEnabledTab: getIndexOfClosestEnabledTab, + getTabIndexByPaneId: getTabIndexByPaneId, + storeDataOnLiEl: storeDataOnLiEl + }; + + /////////////////// + + function didTabOrderChange($currTabLis, updatedTabs, propNames) { + var isTabOrderChanged = false; + + $currTabLis.each(function (currDomIdx) { + var newIdx = getTabIndexByPaneId(updatedTabs, propNames.paneId, $(this).data('tab')[propNames.paneId]); + + if ((newIdx > -1) && (newIdx !== currDomIdx)) { // tab moved + isTabOrderChanged = true; + return false; // exit .each() loop + } + }); + + return isTabOrderChanged; + } + + function getIndexOfClosestEnabledTab($currTabLis, startIndex) { + var lastIndex = $currTabLis.length - 1, + closestIdx = -1, + incrementFromStartIndex = 0, + testIdx = 0; + + // expand out from the current tab looking for an enabled tab; + // we prefer the tab after us over the tab before + while ((closestIdx === -1) && (testIdx >= 0)) { + + if ( (((testIdx = startIndex + (++incrementFromStartIndex)) <= lastIndex) && + !$currTabLis.eq(testIdx).hasClass('disabled')) || + (((testIdx = startIndex - incrementFromStartIndex) >= 0) && + !$currTabLis.eq(testIdx).hasClass('disabled')) ) { + + closestIdx = testIdx; + + } + } + + return closestIdx; + } + + function getTabIndexByPaneId(tabs, paneIdPropName, paneId) { + var idx = -1; + + tabs.some(function (tab, i) { + if (tab[paneIdPropName] === paneId) { + idx = i; + return true; // exit loop + } + }); + + return idx; + } + + function storeDataOnLiEl($li, tabs, index) { + $li.data({ + tab: $.extend({}, tabs[index]), // store a clone so we can check for changes + index: index + }); + } + + }()); // tabUtils + + function buildNavTabsAndTabContentForTargetElementInstance($targetElInstance, settings, readyCallback) { + var tabs = settings.tabs, + propNames = { + paneId: settings.propPaneId, + title: settings.propTitle, + active: settings.propActive, + disabled: settings.propDisabled, + content: settings.propContent + }, + ignoreTabPanes = settings.ignoreTabPanes, + hasTabContent = tabs.length && tabs[0][propNames.content] !== undefined, + $navTabs = tabElements.getNewElNavTabs(), + $tabContent = tabElements.getNewElTabContent(), + $scroller, + attachTabContentToDomCallback = ignoreTabPanes ? null : function() { + $scroller.after($tabContent); + }; + + if (!tabs.length) { + return; + } + + tabs.forEach(function(tab, index) { + var options = { + forceActiveTab: true, + tabLiContent: settings.tabsLiContent && settings.tabsLiContent[index], + tabPostProcessor: settings.tabsPostProcessors && settings.tabsPostProcessors[index] + }; + + tabElements + .getNewElTabLi(tab, propNames, options) + .appendTo($navTabs); + + // build the tab panes if we weren't told to ignore them and there's + // tab content data available + if (!ignoreTabPanes && hasTabContent) { + tabElements + .getNewElTabPane(tab, propNames, options) + .appendTo($tabContent); + } + }); + + $scroller = wrapNavTabsInstanceInScroller($navTabs, + settings, + readyCallback, + attachTabContentToDomCallback); + + $scroller.appendTo($targetElInstance); + + $targetElInstance.data({ + scrtabs: { + tabs: tabs, + propNames: propNames, + ignoreTabPanes: ignoreTabPanes, + hasTabContent: hasTabContent, + tabsLiContent: settings.tabsLiContent, + tabsPostProcessors: settings.tabsPostProcessors, + scroller: $scroller + } + }); + + // once the nav-tabs are wrapped in the scroller, attach each tab's + // data to it for reference later; we need to wait till they're + // wrapped in the scroller because we wrap a *clone* of the nav-tabs + // we built above, not the original nav-tabs + $scroller.find('.nav-tabs > li').each(function (index) { + tabUtils.storeDataOnLiEl($(this), tabs, index); + }); + + return $targetElInstance; + } + + + function wrapNavTabsInstanceInScroller($navTabsInstance, settings, readyCallback, attachTabContentToDomCallback) { + var $scroller = tabElements.getNewElScrollerElementWrappingNavTabsInstance($navTabsInstance.clone(true), settings), // use clone because we replaceWith later + scrollingTabsControl = new ScrollingTabsControl($scroller), + navTabsInstanceData = $navTabsInstance.data('scrtabs'); + + if (!navTabsInstanceData) { + $navTabsInstance.data('scrtabs', { + scroller: $scroller + }); + } else { + navTabsInstanceData.scroller = $scroller; + } + + $navTabsInstance.replaceWith($scroller.css('visibility', 'hidden')); + + if (settings.tabClickHandler && (typeof settings.tabClickHandler === 'function')) { + $scroller.hasTabClickHandler = true; + scrollingTabsControl.tabClickHandler = settings.tabClickHandler; + } + + $scroller.initTabs = function () { + scrollingTabsControl.initTabs(settings, + $scroller, + readyCallback, + attachTabContentToDomCallback); + }; + + $scroller.scrollToActiveTab = function() { + scrollingTabsControl.scrollToActiveTab(settings); + }; + + $scroller.initTabs(); + + listenForDropdownMenuTabs($scroller, scrollingTabsControl); + + return $scroller; + } + + /* exported listenForDropdownMenuTabs, + refreshTargetElementInstance, + scrollToActiveTab */ + function checkForTabAdded(refreshData) { + var updatedTabsArray = refreshData.updatedTabsArray, + updatedTabsLiContent = refreshData.updatedTabsLiContent || [], + updatedTabsPostProcessors = refreshData.updatedTabsPostProcessors || [], + propNames = refreshData.propNames, + ignoreTabPanes = refreshData.ignoreTabPanes, + options = refreshData.options, + $currTabLis = refreshData.$currTabLis, + $navTabs = refreshData.$navTabs, + $currTabContentPanesContainer = ignoreTabPanes ? null : refreshData.$currTabContentPanesContainer, + $currTabContentPanes = ignoreTabPanes ? null : refreshData.$currTabContentPanes, + isInitTabsRequired = false; + + // make sure each tab in the updated tabs array has a corresponding DOM element + updatedTabsArray.forEach(function (tab, idx) { + var $li = $currTabLis.find('a[href="#' + tab[propNames.paneId] + '"]'), + isTabIdxPastCurrTabs = (idx >= $currTabLis.length), + $pane; + + if (!$li.length) { // new tab + isInitTabsRequired = true; + + // add the tab, add its pane (if necessary), and refresh the scroller + options.tabLiContent = updatedTabsLiContent[idx]; + options.tabPostProcessor = updatedTabsPostProcessors[idx]; + $li = tabElements.getNewElTabLi(tab, propNames, options); + tabUtils.storeDataOnLiEl($li, updatedTabsArray, idx); + + if (isTabIdxPastCurrTabs) { // append to end of current tabs + $li.appendTo($navTabs); + } else { // insert in middle of current tabs + $li.insertBefore($currTabLis.eq(idx)); + } + + if (!ignoreTabPanes && tab[propNames.content] !== undefined) { + $pane = tabElements.getNewElTabPane(tab, propNames, options); + if (isTabIdxPastCurrTabs) { // append to end of current tabs + $pane.appendTo($currTabContentPanesContainer); + } else { // insert in middle of current tabs + $pane.insertBefore($currTabContentPanes.eq(idx)); + } + } + + } + + }); + + return isInitTabsRequired; + } + + function checkForTabPropertiesUpdated(refreshData) { + var tabLiData = refreshData.tabLi, + ignoreTabPanes = refreshData.ignoreTabPanes, + $li = tabLiData.$li, + $contentPane = tabLiData.$contentPane, + origTabData = tabLiData.origTabData, + newTabData = tabLiData.newTabData, + propNames = refreshData.propNames, + isInitTabsRequired = false; + + // update tab title if necessary + if (origTabData[propNames.title] !== newTabData[propNames.title]) { + $li.find('a[role="tab"]') + .html(origTabData[propNames.title] = newTabData[propNames.title]); + + isInitTabsRequired = true; + } + + // update tab disabled state if necessary + if (origTabData[propNames.disabled] !== newTabData[propNames.disabled]) { + if (newTabData[propNames.disabled]) { // enabled -> disabled + $li.addClass('disabled'); + $li.find('a[role="tab"]').attr('data-toggle', ''); + } else { // disabled -> enabled + $li.removeClass('disabled'); + $li.find('a[role="tab"]').attr('data-toggle', 'tab'); + } + + origTabData[propNames.disabled] = newTabData[propNames.disabled]; + isInitTabsRequired = true; + } + + // update tab active state if necessary + if (refreshData.options.forceActiveTab) { + // set the active tab based on the tabs array regardless of the current + // DOM state, which could have been changed by the user clicking a tab + // without those changes being reflected back to the tab data + $li[newTabData[propNames.active] ? 'addClass' : 'removeClass']('active'); + + $contentPane[newTabData[propNames.active] ? 'addClass' : 'removeClass']('active'); + + origTabData[propNames.active] = newTabData[propNames.active]; + + isInitTabsRequired = true; + } + + // update tab content pane if necessary + if (!ignoreTabPanes && origTabData[propNames.content] !== newTabData[propNames.content]) { + $contentPane.html(origTabData[propNames.content] = newTabData[propNames.content]); + isInitTabsRequired = true; + } + + return isInitTabsRequired; + } + + function checkForTabRemoved(refreshData) { + var tabLiData = refreshData.tabLi, + ignoreTabPanes = refreshData.ignoreTabPanes, + $li = tabLiData.$li, + idxToMakeActive; + + if (tabLiData.newIdx !== -1) { // tab was not removed--it has a valid index + return false; + } + + // if this was the active tab, make the closest enabled tab active + if ($li.hasClass('active')) { + + idxToMakeActive = tabUtils.getIndexOfClosestEnabledTab(refreshData.$currTabLis, tabLiData.currDomIdx); + if (idxToMakeActive > -1) { + refreshData.$currTabLis + .eq(idxToMakeActive) + .addClass('active'); + + if (!ignoreTabPanes) { + refreshData.$currTabContentPanes + .eq(idxToMakeActive) + .addClass('active'); + } + } + } + + $li.remove(); + + if (!ignoreTabPanes) { + tabLiData.$contentPane.remove(); + } + + return true; + } + + function checkForTabsOrderChanged(refreshData) { + var $currTabLis = refreshData.$currTabLis, + updatedTabsArray = refreshData.updatedTabsArray, + propNames = refreshData.propNames, + ignoreTabPanes = refreshData.ignoreTabPanes, + newTabsCollection = [], + newTabPanesCollection = ignoreTabPanes ? null : []; + + if (!tabUtils.didTabOrderChange($currTabLis, updatedTabsArray, propNames)) { + return false; + } + + // the tab order changed... + updatedTabsArray.forEach(function (t) { + var paneId = t[propNames.paneId]; + + newTabsCollection.push( + $currTabLis + .find('a[role="tab"][href="#' + paneId + '"]') + .parent('li') + ); + + if (!ignoreTabPanes) { + newTabPanesCollection.push($('#' + paneId)); + } + }); + + refreshData.$navTabs.append(newTabsCollection); + + if (!ignoreTabPanes) { + refreshData.$currTabContentPanesContainer.append(newTabPanesCollection); + } + + return true; + } + + function checkForTabsRemovedOrUpdated(refreshData) { + var $currTabLis = refreshData.$currTabLis, + updatedTabsArray = refreshData.updatedTabsArray, + propNames = refreshData.propNames, + isInitTabsRequired = false; + + + $currTabLis.each(function (currDomIdx) { + var $li = $(this), + origTabData = $li.data('tab'), + newIdx = tabUtils.getTabIndexByPaneId(updatedTabsArray, propNames.paneId, origTabData[propNames.paneId]), + newTabData = (newIdx > -1) ? updatedTabsArray[newIdx] : null; + + refreshData.tabLi = { + $li: $li, + currDomIdx: currDomIdx, + newIdx: newIdx, + $contentPane: tabElements.getElTabPaneForLi($li), + origTabData: origTabData, + newTabData: newTabData + }; + + if (checkForTabRemoved(refreshData)) { + isInitTabsRequired = true; + return; // continue to next $li in .each() since we removed this tab + } + + if (checkForTabPropertiesUpdated(refreshData)) { + isInitTabsRequired = true; + } + }); + + return isInitTabsRequired; + } + + function listenForDropdownMenuTabs($scroller, stc) { + var $ddMenu; + + // for dropdown menus to show, we need to move them out of the + // scroller and append them to the body + $scroller + .on(CONSTANTS.EVENTS.DROPDOWN_MENU_SHOW, handleDropdownShow) + .on(CONSTANTS.EVENTS.DROPDOWN_MENU_HIDE, handleDropdownHide); + + function handleDropdownHide(e) { + // move the dropdown menu back into its tab + $(e.target).append($ddMenu.off(CONSTANTS.EVENTS.CLICK)); + } + + function handleDropdownShow(e) { + var $ddParentTabLi = $(e.target), + ddLiOffset = $ddParentTabLi.offset(), + $currActiveTab = $scroller.find('li[role="presentation"].active'), + ddMenuRightX, + tabsContainerMaxX, + ddMenuTargetLeft; + + $ddMenu = $ddParentTabLi + .find('.dropdown-menu') + .attr('data-' + CONSTANTS.DATA_KEY_DDMENU_MODIFIED, true); + + // if the dropdown's parent tab li isn't already active, + // we need to deactivate any active menu item in the dropdown + if ($currActiveTab[0] !== $ddParentTabLi[0]) { + $ddMenu.find('li.active').removeClass('active'); + } + + // we need to do our own click handling because the built-in + // bootstrap handlers won't work since we moved the dropdown + // menu outside the tabs container + $ddMenu.on(CONSTANTS.EVENTS.CLICK, 'a[role="tab"]', handleClickOnDropdownMenuItem); + + $('body').append($ddMenu); + + // make sure the menu doesn't go off the right side of the page + ddMenuRightX = $ddMenu.width() + ddLiOffset.left; + tabsContainerMaxX = $scroller.width() - (stc.$slideRightArrow.outerWidth() + 1); + ddMenuTargetLeft = ddLiOffset.left; + + if (ddMenuRightX > tabsContainerMaxX) { + ddMenuTargetLeft -= (ddMenuRightX - tabsContainerMaxX); + } + + $ddMenu.css({ + 'display': 'block', + 'top': ddLiOffset.top + $ddParentTabLi.outerHeight() - 2, + 'left': ddMenuTargetLeft + }); + + function handleClickOnDropdownMenuItem() { + /* jshint validthis: true */ + var $selectedMenuItemAnc = $(this), + $selectedMenuItemLi = $selectedMenuItemAnc.parent('li'), + $selectedMenuItemDropdownMenu = $selectedMenuItemLi.parent('.dropdown-menu'), + targetPaneId = $selectedMenuItemAnc.attr('href'); + + if ($selectedMenuItemLi.hasClass('active')) { + return; + } + + // once we select a menu item from the dropdown, deactivate + // the current tab (unless it's our parent tab), deactivate + // any active dropdown menu item, make our parent tab active + // (if it's not already), and activate the selected menu item + $scroller + .find('li.active') + .not($ddParentTabLi) + .add($selectedMenuItemDropdownMenu.find('li.active')) + .removeClass('active'); + + $ddParentTabLi + .add($selectedMenuItemLi) + .addClass('active'); + + // manually deactivate current active pane and activate our pane + $('.tab-content .tab-pane.active').removeClass('active'); + $(targetPaneId).addClass('active'); + } + + } + } + + function refreshDataDrivenTabs($container, options) { + var instanceData = $container.data().scrtabs, + scroller = instanceData.scroller, + $navTabs = $container.find('.scrtabs-tab-container .nav-tabs'), + $currTabContentPanesContainer = $container.find('.tab-content'), + isInitTabsRequired = false, + refreshData = { + options: options, + updatedTabsArray: instanceData.tabs, + updatedTabsLiContent: instanceData.tabsLiContent, + updatedTabsPostProcessors: instanceData.tabsPostProcessors, + propNames: instanceData.propNames, + ignoreTabPanes: instanceData.ignoreTabPanes, + $navTabs: $navTabs, + $currTabLis: $navTabs.find('> li'), + $currTabContentPanesContainer: $currTabContentPanesContainer, + $currTabContentPanes: $currTabContentPanesContainer.find('.tab-pane') + }; + + // to preserve the tab positions if we're just adding or removing + // a tab, don't completely rebuild the tab structure, but check + // for differences between the new tabs array and the old + if (checkForTabAdded(refreshData)) { + isInitTabsRequired = true; + } + + if (checkForTabsOrderChanged(refreshData)) { + isInitTabsRequired = true; + } + + if (checkForTabsRemovedOrUpdated(refreshData)) { + isInitTabsRequired = true; + } + + if (isInitTabsRequired) { + scroller.initTabs(); + } + + return isInitTabsRequired; + } + + function refreshTargetElementInstance($container, options) { + if (!$container.data('scrtabs')) { // target element doesn't have plugin on it + return; + } + + // force a refresh if the tabs are static html or they're data-driven + // but the data didn't change so we didn't call initTabs() + if ($container.data('scrtabs').isWrapperOnly || !refreshDataDrivenTabs($container, options)) { + $('body').trigger(CONSTANTS.EVENTS.FORCE_REFRESH); + } + } + + function scrollToActiveTab() { + /* jshint validthis: true */ + var $targetElInstance = $(this), + scrtabsData = $targetElInstance.data('scrtabs'); + + if (!scrtabsData) { + return; + } + + scrtabsData.scroller.scrollToActiveTab(); + } + + var methods = { + destroy: function() { + var $targetEls = this; + + return $targetEls.each(destroyPlugin); + }, + + init: function(options) { + var $targetEls = this, + targetElsLastIndex = $targetEls.length - 1, + settings = $.extend({}, $.fn.scrollingTabs.defaults, options || {}); + + // ---- tabs NOT data-driven ------------------------- + if (!settings.tabs) { + + // just wrap the selected .nav-tabs element(s) in the scroller + return $targetEls.each(function(index) { + var dataObj = { + isWrapperOnly: true + }, + $targetEl = $(this).data({ scrtabs: dataObj }), + readyCallback = (index < targetElsLastIndex) ? null : function() { + $targetEls.trigger(CONSTANTS.EVENTS.TABS_READY); + }; + + wrapNavTabsInstanceInScroller($targetEl, settings, readyCallback); + }); + + } + + // ---- tabs data-driven ------------------------- + return $targetEls.each(function (index) { + var $targetEl = $(this), + readyCallback = (index < targetElsLastIndex) ? null : function() { + $targetEls.trigger(CONSTANTS.EVENTS.TABS_READY); + }; + + buildNavTabsAndTabContentForTargetElementInstance($targetEl, settings, readyCallback); + }); + }, + + refresh: function(options) { + var $targetEls = this, + settings = $.extend({}, $.fn.scrollingTabs.defaults, options || {}); + + return $targetEls.each(function () { + refreshTargetElementInstance($(this), settings); + }); + }, + + scrollToActiveTab: function() { + return this.each(scrollToActiveTab); + } + }; + + function destroyPlugin() { + /* jshint validthis: true */ + var $targetElInstance = $(this), + scrtabsData = $targetElInstance.data('scrtabs'), + $tabsContainer; + + if (!scrtabsData) { + return; + } + + if (scrtabsData.enableSwipingElement === 'self') { + $targetElInstance.removeClass(CONSTANTS.CSS_CLASSES.ALLOW_SCROLLBAR); + } else if (scrtabsData.enableSwipingElement === 'parent') { + $targetElInstance.closest('.scrtabs-tab-container').parent().removeClass(CONSTANTS.CSS_CLASSES.ALLOW_SCROLLBAR); + } + + scrtabsData.scroller + .off(CONSTANTS.EVENTS.DROPDOWN_MENU_SHOW) + .off(CONSTANTS.EVENTS.DROPDOWN_MENU_HIDE); + + // if there were any dropdown menus opened, remove the css we added to + // them so they would display correctly + scrtabsData.scroller + .find('[data-' + CONSTANTS.DATA_KEY_DDMENU_MODIFIED + ']') + .css({ + display: '', + left: '', + top: '' + }) + .off(CONSTANTS.EVENTS.CLICK) + .removeAttr('data-' + CONSTANTS.DATA_KEY_DDMENU_MODIFIED); + + if (scrtabsData.scroller.hasTabClickHandler) { + $targetElInstance + .find('a[data-toggle="tab"]') + .off('.scrtabs'); + } + + if (scrtabsData.isWrapperOnly) { // we just wrapped nav-tabs markup, so restore it + // $targetElInstance is the ul.nav-tabs + $tabsContainer = $targetElInstance.parents('.scrtabs-tab-container'); + + if ($tabsContainer.length) { + $tabsContainer.replaceWith($targetElInstance); + } + + } else { // we generated the tabs from data so destroy everything we created + if (scrtabsData.scroller && scrtabsData.scroller.initTabs) { + scrtabsData.scroller.initTabs = null; + } + + // $targetElInstance is the container for the ul.nav-tabs we generated + $targetElInstance + .find('.scrtabs-tab-container') + .add('.tab-content') + .remove(); + } + + $targetElInstance.removeData('scrtabs'); + + while(--$.fn.scrollingTabs.nextInstanceId >= 0) { + $(window).off(CONSTANTS.EVENTS.WINDOW_RESIZE + $.fn.scrollingTabs.nextInstanceId); + } + + $('body').off(CONSTANTS.EVENTS.FORCE_REFRESH); + } + + + $.fn.scrollingTabs = function(methodOrOptions) { + + if (methods[methodOrOptions]) { + return methods[methodOrOptions].apply(this, Array.prototype.slice.call(arguments, 1)); + } else if (!methodOrOptions || (typeof methodOrOptions === 'object')) { + return methods.init.apply(this, arguments); + } else { + $.error('Method ' + methodOrOptions + ' does not exist on $.scrollingTabs.'); + } + }; + + $.fn.scrollingTabs.nextInstanceId = 0; + + $.fn.scrollingTabs.defaults = { + tabs: null, + propPaneId: 'paneId', + propTitle: 'title', + propActive: 'active', + propDisabled: 'disabled', + propContent: 'content', + ignoreTabPanes: false, + scrollToTabEdge: false, + disableScrollArrowsOnFullyScrolled: false, + forceActiveTab: false, + reverseScroll: false, + widthMultiplier: 1, + tabClickHandler: null, + cssClassLeftArrow: 'fa fa-chevron-left', + cssClassRightArrow: 'fa fa-chevron-right', + leftArrowContent: '', + rightArrowContent: '', + tabsLiContent: null, + tabsPostProcessors: null, + enableSwiping: false, + enableRtlSupport: false, + bootstrapVersion: 4 + }; + + + +}(jQuery, window)); \ No newline at end of file diff --git a/muk_web_theme/static/libs/simplebar/simplebar.css b/muk_web_theme/static/libs/simplebar/simplebar.css new file mode 100644 index 0000000..35eddba --- /dev/null +++ b/muk_web_theme/static/libs/simplebar/simplebar.css @@ -0,0 +1,183 @@ +[data-simplebar] { + position: relative; + flex-direction: column; + flex-wrap: wrap; + justify-content: flex-start; + align-content: flex-start; + align-items: flex-start; + width: inherit; + height: inherit; + max-width: inherit; + max-height: inherit; +} + +.simplebar-wrapper { + overflow: hidden; + width: inherit; + height: inherit; + max-width: inherit; + max-height: inherit; +} + +.simplebar-mask { + direction: inherit; + position: absolute; + overflow: hidden; + padding: 0; + margin: 0; + left: 0; + top: 0; + bottom: 0; + right: 0; + width: auto !important; + height: auto !important; + z-index: 0; +} + +.simplebar-offset { + direction: inherit !important; + box-sizing: inherit !important; + resize: none !important; + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + padding: 0; + margin: 0; + -webkit-overflow-scrolling: touch; +} + +.simplebar-content { + direction: inherit; + box-sizing: border-box !important; + position: relative; + display: block; + height: 100%; /* Required for horizontal native scrollbar to not appear if parent is taller than natural height */ + width: auto; + visibility: visible; + overflow: scroll; /* Scroll on this element otherwise element can't have a padding applied properly */ + max-width: 100%; /* Not required for horizontal scroll to trigger */ + max-height: 100%; /* Needed for vertical scroll to trigger */ +} + +.simplebar-placeholder { + max-height: 100%; + max-width: 100%; + width: 100%; + pointer-events: none; +} + +.simplebar-height-auto-observer-wrapper { + box-sizing: inherit !important; + height: 100%; + width: inherit; + max-width: 1px; + position: relative; + float: left; + max-height: 1px; + overflow: hidden; + z-index: -1; + padding: 0; + margin: 0; + pointer-events: none; + flex-grow: inherit; + flex-shrink: 0; + flex-basis: 0; +} + +.simplebar-height-auto-observer { + box-sizing: inherit; + display: block; + opacity: 0; + position: absolute; + top: 0; + left: 0; + height: 1000%; + width: 1000%; + min-height: 1px; + min-width: 1px; + overflow: hidden; + pointer-events: none; + z-index: -1; +} + +.simplebar-track { + z-index: 1; + position: absolute; + right: 0; + bottom: 0; + pointer-events: none; +} + +.simplebar-scrollbar { + position: absolute; + right: 2px; + width: 7px; + min-height: 10px; +} + +.simplebar-scrollbar:before { + position: absolute; + content: ""; + background: black; + border-radius: 7px; + left: 0; + right: 0; + opacity: 0; + transition: opacity 0.2s linear; +} + +.simplebar-track .simplebar-scrollbar.simplebar-visible:before { + /* When hovered, remove all transitions from drag handle */ + opacity: 0.5; + transition: opacity 0s linear; +} + +.simplebar-track.simplebar-vertical { + top: 0; + width: 11px; +} + +.simplebar-track.simplebar-vertical .simplebar-scrollbar:before { + top: 2px; + bottom: 2px; +} + +.simplebar-track.simplebar-horizontal { + left: 0; + height: 11px; +} + +.simplebar-track.simplebar-horizontal .simplebar-scrollbar:before { + height: 100%; + left: 2px; + right: 2px; +} + +.simplebar-track.simplebar-horizontal .simplebar-scrollbar { + right: auto; + left: 0; + top: 2px; + height: 7px; + min-height: 0; + min-width: 10px; + width: auto; +} + +/* Rtl support */ +[data-simplebar-direction="rtl"] .simplebar-track.simplebar-vertical { + right: auto; + left: 0; +} + +.hs-dummy-scrollbar-size { + direction: rtl; + position: fixed; + opacity: 0; + visibility: hidden; + height: 500px; + width: 500px; + overflow-y: hidden; + overflow-x: scroll; +} \ No newline at end of file diff --git a/muk_web_theme/static/libs/simplebar/simplebar.js b/muk_web_theme/static/libs/simplebar/simplebar.js new file mode 100644 index 0000000..561baea --- /dev/null +++ b/muk_web_theme/static/libs/simplebar/simplebar.js @@ -0,0 +1,4182 @@ +/** + * SimpleBar.js - v3.1.0 + * Scrollbars, simpler. + * https://grsmto.github.io/simplebar/ + * + * Made by Adrien Denat from a fork by Jonathan Nicol + * Under MIT License + */ + +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global.SimpleBar = factory()); +}(this, (function () { 'use strict'; + + var _isObject = function (it) { + return typeof it === 'object' ? it !== null : typeof it === 'function'; + }; + + var _anObject = function (it) { + if (!_isObject(it)) throw TypeError(it + ' is not an object!'); + return it; + }; + + var _fails = function (exec) { + try { + return !!exec(); + } catch (e) { + return true; + } + }; + + // Thank's IE8 for his funny defineProperty + var _descriptors = !_fails(function () { + return Object.defineProperty({}, 'a', { get: function () { return 7; } }).a != 7; + }); + + var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; + + function createCommonjsModule(fn, module) { + return module = { exports: {} }, fn(module, module.exports), module.exports; + } + + var _global = createCommonjsModule(function (module) { + // https://github.com/zloirock/core-js/issues/86#issuecomment-115759028 + var global = module.exports = typeof window != 'undefined' && window.Math == Math + ? window : typeof self != 'undefined' && self.Math == Math ? self + // eslint-disable-next-line no-new-func + : Function('return this')(); + if (typeof __g == 'number') __g = global; // eslint-disable-line no-undef + }); + + var document$1 = _global.document; + // typeof document.createElement is 'object' in old IE + var is = _isObject(document$1) && _isObject(document$1.createElement); + var _domCreate = function (it) { + return is ? document$1.createElement(it) : {}; + }; + + var _ie8DomDefine = !_descriptors && !_fails(function () { + return Object.defineProperty(_domCreate('div'), 'a', { get: function () { return 7; } }).a != 7; + }); + + // 7.1.1 ToPrimitive(input [, PreferredType]) + + // instead of the ES6 spec version, we didn't implement @@toPrimitive case + // and the second argument - flag - preferred type is a string + var _toPrimitive = function (it, S) { + if (!_isObject(it)) return it; + var fn, val; + if (S && typeof (fn = it.toString) == 'function' && !_isObject(val = fn.call(it))) return val; + if (typeof (fn = it.valueOf) == 'function' && !_isObject(val = fn.call(it))) return val; + if (!S && typeof (fn = it.toString) == 'function' && !_isObject(val = fn.call(it))) return val; + throw TypeError("Can't convert object to primitive value"); + }; + + var dP = Object.defineProperty; + + var f = _descriptors ? Object.defineProperty : function defineProperty(O, P, Attributes) { + _anObject(O); + P = _toPrimitive(P, true); + _anObject(Attributes); + if (_ie8DomDefine) try { + return dP(O, P, Attributes); + } catch (e) { /* empty */ } + if ('get' in Attributes || 'set' in Attributes) throw TypeError('Accessors not supported!'); + if ('value' in Attributes) O[P] = Attributes.value; + return O; + }; + + var _objectDp = { + f: f + }; + + var _propertyDesc = function (bitmap, value) { + return { + enumerable: !(bitmap & 1), + configurable: !(bitmap & 2), + writable: !(bitmap & 4), + value: value + }; + }; + + var _hide = _descriptors ? function (object, key, value) { + return _objectDp.f(object, key, _propertyDesc(1, value)); + } : function (object, key, value) { + object[key] = value; + return object; + }; + + var hasOwnProperty = {}.hasOwnProperty; + var _has = function (it, key) { + return hasOwnProperty.call(it, key); + }; + + var id = 0; + var px = Math.random(); + var _uid = function (key) { + return 'Symbol('.concat(key === undefined ? '' : key, ')_', (++id + px).toString(36)); + }; + + var _core = createCommonjsModule(function (module) { + var core = module.exports = { version: '2.5.7' }; + if (typeof __e == 'number') __e = core; // eslint-disable-line no-undef + }); + var _core_1 = _core.version; + + var _redefine = createCommonjsModule(function (module) { + var SRC = _uid('src'); + var TO_STRING = 'toString'; + var $toString = Function[TO_STRING]; + var TPL = ('' + $toString).split(TO_STRING); + + _core.inspectSource = function (it) { + return $toString.call(it); + }; + + (module.exports = function (O, key, val, safe) { + var isFunction = typeof val == 'function'; + if (isFunction) _has(val, 'name') || _hide(val, 'name', key); + if (O[key] === val) return; + if (isFunction) _has(val, SRC) || _hide(val, SRC, O[key] ? '' + O[key] : TPL.join(String(key))); + if (O === _global) { + O[key] = val; + } else if (!safe) { + delete O[key]; + _hide(O, key, val); + } else if (O[key]) { + O[key] = val; + } else { + _hide(O, key, val); + } + // add fake Function#toString for correct work wrapped methods / constructors with methods like LoDash isNative + })(Function.prototype, TO_STRING, function toString() { + return typeof this == 'function' && this[SRC] || $toString.call(this); + }); + }); + + // 7.2.1 RequireObjectCoercible(argument) + var _defined = function (it) { + if (it == undefined) throw TypeError("Can't call method on " + it); + return it; + }; + + var _library = false; + + var _shared = createCommonjsModule(function (module) { + var SHARED = '__core-js_shared__'; + var store = _global[SHARED] || (_global[SHARED] = {}); + + (module.exports = function (key, value) { + return store[key] || (store[key] = value !== undefined ? value : {}); + })('versions', []).push({ + version: _core.version, + mode: _library ? 'pure' : 'global', + copyright: '© 2018 Denis Pushkarev (zloirock.ru)' + }); + }); + + var _wks = createCommonjsModule(function (module) { + var store = _shared('wks'); + + var Symbol = _global.Symbol; + var USE_SYMBOL = typeof Symbol == 'function'; + + var $exports = module.exports = function (name) { + return store[name] || (store[name] = + USE_SYMBOL && Symbol[name] || (USE_SYMBOL ? Symbol : _uid)('Symbol.' + name)); + }; + + $exports.store = store; + }); + + var _fixReWks = function (KEY, length, exec) { + var SYMBOL = _wks(KEY); + var fns = exec(_defined, SYMBOL, ''[KEY]); + var strfn = fns[0]; + var rxfn = fns[1]; + if (_fails(function () { + var O = {}; + O[SYMBOL] = function () { return 7; }; + return ''[KEY](O) != 7; + })) { + _redefine(String.prototype, KEY, strfn); + _hide(RegExp.prototype, SYMBOL, length == 2 + // 21.2.5.8 RegExp.prototype[@@replace](string, replaceValue) + // 21.2.5.11 RegExp.prototype[@@split](string, limit) + ? function (string, arg) { return rxfn.call(string, this, arg); } + // 21.2.5.6 RegExp.prototype[@@match](string) + // 21.2.5.9 RegExp.prototype[@@search](string) + : function (string) { return rxfn.call(string, this); } + ); + } + }; + + // @@replace logic + _fixReWks('replace', 2, function (defined, REPLACE, $replace) { + // 21.1.3.14 String.prototype.replace(searchValue, replaceValue) + return [function replace(searchValue, replaceValue) { + var O = defined(this); + var fn = searchValue == undefined ? undefined : searchValue[REPLACE]; + return fn !== undefined + ? fn.call(searchValue, O, replaceValue) + : $replace.call(String(O), searchValue, replaceValue); + }, $replace]; + }); + + var dP$1 = _objectDp.f; + var FProto = Function.prototype; + var nameRE = /^\s*function ([^ (]*)/; + var NAME = 'name'; + + // 19.2.4.2 name + NAME in FProto || _descriptors && dP$1(FProto, NAME, { + configurable: true, + get: function () { + try { + return ('' + this).match(nameRE)[1]; + } catch (e) { + return ''; + } + } + }); + + // @@match logic + _fixReWks('match', 1, function (defined, MATCH, $match) { + // 21.1.3.11 String.prototype.match(regexp) + return [function match(regexp) { + var O = defined(this); + var fn = regexp == undefined ? undefined : regexp[MATCH]; + return fn !== undefined ? fn.call(regexp, O) : new RegExp(regexp)[MATCH](String(O)); + }, $match]; + }); + + // 22.1.3.31 Array.prototype[@@unscopables] + var UNSCOPABLES = _wks('unscopables'); + var ArrayProto = Array.prototype; + if (ArrayProto[UNSCOPABLES] == undefined) _hide(ArrayProto, UNSCOPABLES, {}); + var _addToUnscopables = function (key) { + ArrayProto[UNSCOPABLES][key] = true; + }; + + var _iterStep = function (done, value) { + return { value: value, done: !!done }; + }; + + var _iterators = {}; + + var toString = {}.toString; + + var _cof = function (it) { + return toString.call(it).slice(8, -1); + }; + + // fallback for non-array-like ES3 and non-enumerable old V8 strings + + // eslint-disable-next-line no-prototype-builtins + var _iobject = Object('z').propertyIsEnumerable(0) ? Object : function (it) { + return _cof(it) == 'String' ? it.split('') : Object(it); + }; + + // to indexed object, toObject with fallback for non-array-like ES3 strings + + + var _toIobject = function (it) { + return _iobject(_defined(it)); + }; + + var _aFunction = function (it) { + if (typeof it != 'function') throw TypeError(it + ' is not a function!'); + return it; + }; + + // optional / simple context binding + + var _ctx = function (fn, that, length) { + _aFunction(fn); + if (that === undefined) return fn; + switch (length) { + case 1: return function (a) { + return fn.call(that, a); + }; + case 2: return function (a, b) { + return fn.call(that, a, b); + }; + case 3: return function (a, b, c) { + return fn.call(that, a, b, c); + }; + } + return function (/* ...args */) { + return fn.apply(that, arguments); + }; + }; + + var PROTOTYPE = 'prototype'; + + var $export = function (type, name, source) { + var IS_FORCED = type & $export.F; + var IS_GLOBAL = type & $export.G; + var IS_STATIC = type & $export.S; + var IS_PROTO = type & $export.P; + var IS_BIND = type & $export.B; + var target = IS_GLOBAL ? _global : IS_STATIC ? _global[name] || (_global[name] = {}) : (_global[name] || {})[PROTOTYPE]; + var exports = IS_GLOBAL ? _core : _core[name] || (_core[name] = {}); + var expProto = exports[PROTOTYPE] || (exports[PROTOTYPE] = {}); + var key, own, out, exp; + if (IS_GLOBAL) source = name; + for (key in source) { + // contains in native + own = !IS_FORCED && target && target[key] !== undefined; + // export native or passed + out = (own ? target : source)[key]; + // bind timers to global for call from export context + exp = IS_BIND && own ? _ctx(out, _global) : IS_PROTO && typeof out == 'function' ? _ctx(Function.call, out) : out; + // extend global + if (target) _redefine(target, key, out, type & $export.U); + // export + if (exports[key] != out) _hide(exports, key, exp); + if (IS_PROTO && expProto[key] != out) expProto[key] = out; + } + }; + _global.core = _core; + // type bitmap + $export.F = 1; // forced + $export.G = 2; // global + $export.S = 4; // static + $export.P = 8; // proto + $export.B = 16; // bind + $export.W = 32; // wrap + $export.U = 64; // safe + $export.R = 128; // real proto method for `library` + var _export = $export; + + // 7.1.4 ToInteger + var ceil = Math.ceil; + var floor = Math.floor; + var _toInteger = function (it) { + return isNaN(it = +it) ? 0 : (it > 0 ? floor : ceil)(it); + }; + + // 7.1.15 ToLength + + var min = Math.min; + var _toLength = function (it) { + return it > 0 ? min(_toInteger(it), 0x1fffffffffffff) : 0; // pow(2, 53) - 1 == 9007199254740991 + }; + + var max = Math.max; + var min$1 = Math.min; + var _toAbsoluteIndex = function (index, length) { + index = _toInteger(index); + return index < 0 ? max(index + length, 0) : min$1(index, length); + }; + + // false -> Array#indexOf + // true -> Array#includes + + + + var _arrayIncludes = function (IS_INCLUDES) { + return function ($this, el, fromIndex) { + var O = _toIobject($this); + var length = _toLength(O.length); + var index = _toAbsoluteIndex(fromIndex, length); + var value; + // Array#includes uses SameValueZero equality algorithm + // eslint-disable-next-line no-self-compare + if (IS_INCLUDES && el != el) while (length > index) { + value = O[index++]; + // eslint-disable-next-line no-self-compare + if (value != value) return true; + // Array#indexOf ignores holes, Array#includes - not + } else for (;length > index; index++) if (IS_INCLUDES || index in O) { + if (O[index] === el) return IS_INCLUDES || index || 0; + } return !IS_INCLUDES && -1; + }; + }; + + var shared = _shared('keys'); + + var _sharedKey = function (key) { + return shared[key] || (shared[key] = _uid(key)); + }; + + var arrayIndexOf = _arrayIncludes(false); + var IE_PROTO = _sharedKey('IE_PROTO'); + + var _objectKeysInternal = function (object, names) { + var O = _toIobject(object); + var i = 0; + var result = []; + var key; + for (key in O) if (key != IE_PROTO) _has(O, key) && result.push(key); + // Don't enum bug & hidden keys + while (names.length > i) if (_has(O, key = names[i++])) { + ~arrayIndexOf(result, key) || result.push(key); + } + return result; + }; + + // IE 8- don't enum bug keys + var _enumBugKeys = ( + 'constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf' + ).split(','); + + // 19.1.2.14 / 15.2.3.14 Object.keys(O) + + + + var _objectKeys = Object.keys || function keys(O) { + return _objectKeysInternal(O, _enumBugKeys); + }; + + var _objectDps = _descriptors ? Object.defineProperties : function defineProperties(O, Properties) { + _anObject(O); + var keys = _objectKeys(Properties); + var length = keys.length; + var i = 0; + var P; + while (length > i) _objectDp.f(O, P = keys[i++], Properties[P]); + return O; + }; + + var document$2 = _global.document; + var _html = document$2 && document$2.documentElement; + + // 19.1.2.2 / 15.2.3.5 Object.create(O [, Properties]) + + + + var IE_PROTO$1 = _sharedKey('IE_PROTO'); + var Empty = function () { /* empty */ }; + var PROTOTYPE$1 = 'prototype'; + + // Create object with fake `null` prototype: use iframe Object with cleared prototype + var createDict = function () { + // Thrash, waste and sodomy: IE GC bug + var iframe = _domCreate('iframe'); + var i = _enumBugKeys.length; + var lt = '<'; + var gt = '>'; + var iframeDocument; + iframe.style.display = 'none'; + _html.appendChild(iframe); + iframe.src = 'javascript:'; // eslint-disable-line no-script-url + // createDict = iframe.contentWindow.Object; + // html.removeChild(iframe); + iframeDocument = iframe.contentWindow.document; + iframeDocument.open(); + iframeDocument.write(lt + 'script' + gt + 'document.F=Object' + lt + '/script' + gt); + iframeDocument.close(); + createDict = iframeDocument.F; + while (i--) delete createDict[PROTOTYPE$1][_enumBugKeys[i]]; + return createDict(); + }; + + var _objectCreate = Object.create || function create(O, Properties) { + var result; + if (O !== null) { + Empty[PROTOTYPE$1] = _anObject(O); + result = new Empty(); + Empty[PROTOTYPE$1] = null; + // add "__proto__" for Object.getPrototypeOf polyfill + result[IE_PROTO$1] = O; + } else result = createDict(); + return Properties === undefined ? result : _objectDps(result, Properties); + }; + + var def = _objectDp.f; + + var TAG = _wks('toStringTag'); + + var _setToStringTag = function (it, tag, stat) { + if (it && !_has(it = stat ? it : it.prototype, TAG)) def(it, TAG, { configurable: true, value: tag }); + }; + + var IteratorPrototype = {}; + + // 25.1.2.1.1 %IteratorPrototype%[@@iterator]() + _hide(IteratorPrototype, _wks('iterator'), function () { return this; }); + + var _iterCreate = function (Constructor, NAME, next) { + Constructor.prototype = _objectCreate(IteratorPrototype, { next: _propertyDesc(1, next) }); + _setToStringTag(Constructor, NAME + ' Iterator'); + }; + + // 7.1.13 ToObject(argument) + + var _toObject = function (it) { + return Object(_defined(it)); + }; + + // 19.1.2.9 / 15.2.3.2 Object.getPrototypeOf(O) + + + var IE_PROTO$2 = _sharedKey('IE_PROTO'); + var ObjectProto = Object.prototype; + + var _objectGpo = Object.getPrototypeOf || function (O) { + O = _toObject(O); + if (_has(O, IE_PROTO$2)) return O[IE_PROTO$2]; + if (typeof O.constructor == 'function' && O instanceof O.constructor) { + return O.constructor.prototype; + } return O instanceof Object ? ObjectProto : null; + }; + + var ITERATOR = _wks('iterator'); + var BUGGY = !([].keys && 'next' in [].keys()); // Safari has buggy iterators w/o `next` + var FF_ITERATOR = '@@iterator'; + var KEYS = 'keys'; + var VALUES = 'values'; + + var returnThis = function () { return this; }; + + var _iterDefine = function (Base, NAME, Constructor, next, DEFAULT, IS_SET, FORCED) { + _iterCreate(Constructor, NAME, next); + var getMethod = function (kind) { + if (!BUGGY && kind in proto) return proto[kind]; + switch (kind) { + case KEYS: return function keys() { return new Constructor(this, kind); }; + case VALUES: return function values() { return new Constructor(this, kind); }; + } return function entries() { return new Constructor(this, kind); }; + }; + var TAG = NAME + ' Iterator'; + var DEF_VALUES = DEFAULT == VALUES; + var VALUES_BUG = false; + var proto = Base.prototype; + var $native = proto[ITERATOR] || proto[FF_ITERATOR] || DEFAULT && proto[DEFAULT]; + var $default = $native || getMethod(DEFAULT); + var $entries = DEFAULT ? !DEF_VALUES ? $default : getMethod('entries') : undefined; + var $anyNative = NAME == 'Array' ? proto.entries || $native : $native; + var methods, key, IteratorPrototype; + // Fix native + if ($anyNative) { + IteratorPrototype = _objectGpo($anyNative.call(new Base())); + if (IteratorPrototype !== Object.prototype && IteratorPrototype.next) { + // Set @@toStringTag to native iterators + _setToStringTag(IteratorPrototype, TAG, true); + // fix for some old engines + if (!_library && typeof IteratorPrototype[ITERATOR] != 'function') _hide(IteratorPrototype, ITERATOR, returnThis); + } + } + // fix Array#{values, @@iterator}.name in V8 / FF + if (DEF_VALUES && $native && $native.name !== VALUES) { + VALUES_BUG = true; + $default = function values() { return $native.call(this); }; + } + // Define iterator + if ((!_library || FORCED) && (BUGGY || VALUES_BUG || !proto[ITERATOR])) { + _hide(proto, ITERATOR, $default); + } + // Plug for library + _iterators[NAME] = $default; + _iterators[TAG] = returnThis; + if (DEFAULT) { + methods = { + values: DEF_VALUES ? $default : getMethod(VALUES), + keys: IS_SET ? $default : getMethod(KEYS), + entries: $entries + }; + if (FORCED) for (key in methods) { + if (!(key in proto)) _redefine(proto, key, methods[key]); + } else _export(_export.P + _export.F * (BUGGY || VALUES_BUG), NAME, methods); + } + return methods; + }; + + // 22.1.3.4 Array.prototype.entries() + // 22.1.3.13 Array.prototype.keys() + // 22.1.3.29 Array.prototype.values() + // 22.1.3.30 Array.prototype[@@iterator]() + var es6_array_iterator = _iterDefine(Array, 'Array', function (iterated, kind) { + this._t = _toIobject(iterated); // target + this._i = 0; // next index + this._k = kind; // kind + // 22.1.5.2.1 %ArrayIteratorPrototype%.next() + }, function () { + var O = this._t; + var kind = this._k; + var index = this._i++; + if (!O || index >= O.length) { + this._t = undefined; + return _iterStep(1); + } + if (kind == 'keys') return _iterStep(0, index); + if (kind == 'values') return _iterStep(0, O[index]); + return _iterStep(0, [index, O[index]]); + }, 'values'); + + // argumentsList[@@iterator] is %ArrayProto_values% (9.4.4.6, 9.4.4.7) + _iterators.Arguments = _iterators.Array; + + _addToUnscopables('keys'); + _addToUnscopables('values'); + _addToUnscopables('entries'); + + var ITERATOR$1 = _wks('iterator'); + var TO_STRING_TAG = _wks('toStringTag'); + var ArrayValues = _iterators.Array; + + var DOMIterables = { + CSSRuleList: true, // TODO: Not spec compliant, should be false. + CSSStyleDeclaration: false, + CSSValueList: false, + ClientRectList: false, + DOMRectList: false, + DOMStringList: false, + DOMTokenList: true, + DataTransferItemList: false, + FileList: false, + HTMLAllCollection: false, + HTMLCollection: false, + HTMLFormElement: false, + HTMLSelectElement: false, + MediaList: true, // TODO: Not spec compliant, should be false. + MimeTypeArray: false, + NamedNodeMap: false, + NodeList: true, + PaintRequestList: false, + Plugin: false, + PluginArray: false, + SVGLengthList: false, + SVGNumberList: false, + SVGPathSegList: false, + SVGPointList: false, + SVGStringList: false, + SVGTransformList: false, + SourceBufferList: false, + StyleSheetList: true, // TODO: Not spec compliant, should be false. + TextTrackCueList: false, + TextTrackList: false, + TouchList: false + }; + + for (var collections = _objectKeys(DOMIterables), i = 0; i < collections.length; i++) { + var NAME$1 = collections[i]; + var explicit = DOMIterables[NAME$1]; + var Collection = _global[NAME$1]; + var proto = Collection && Collection.prototype; + var key; + if (proto) { + if (!proto[ITERATOR$1]) _hide(proto, ITERATOR$1, ArrayValues); + if (!proto[TO_STRING_TAG]) _hide(proto, TO_STRING_TAG, NAME$1); + _iterators[NAME$1] = ArrayValues; + if (explicit) for (key in es6_array_iterator) if (!proto[key]) _redefine(proto, key, es6_array_iterator[key], true); + } + } + + // true -> String#at + // false -> String#codePointAt + var _stringAt = function (TO_STRING) { + return function (that, pos) { + var s = String(_defined(that)); + var i = _toInteger(pos); + var l = s.length; + var a, b; + if (i < 0 || i >= l) return TO_STRING ? '' : undefined; + a = s.charCodeAt(i); + return a < 0xd800 || a > 0xdbff || i + 1 === l || (b = s.charCodeAt(i + 1)) < 0xdc00 || b > 0xdfff + ? TO_STRING ? s.charAt(i) : a + : TO_STRING ? s.slice(i, i + 2) : (a - 0xd800 << 10) + (b - 0xdc00) + 0x10000; + }; + }; + + var $at = _stringAt(true); + + // 21.1.3.27 String.prototype[@@iterator]() + _iterDefine(String, 'String', function (iterated) { + this._t = String(iterated); // target + this._i = 0; // next index + // 21.1.5.2.1 %StringIteratorPrototype%.next() + }, function () { + var O = this._t; + var index = this._i; + var point; + if (index >= O.length) return { value: undefined, done: true }; + point = $at(O, index); + this._i += point.length; + return { value: point, done: false }; + }); + + // call something on iterator step with safe closing on error + + var _iterCall = function (iterator, fn, value, entries) { + try { + return entries ? fn(_anObject(value)[0], value[1]) : fn(value); + // 7.4.6 IteratorClose(iterator, completion) + } catch (e) { + var ret = iterator['return']; + if (ret !== undefined) _anObject(ret.call(iterator)); + throw e; + } + }; + + // check on default Array iterator + + var ITERATOR$2 = _wks('iterator'); + var ArrayProto$1 = Array.prototype; + + var _isArrayIter = function (it) { + return it !== undefined && (_iterators.Array === it || ArrayProto$1[ITERATOR$2] === it); + }; + + var _createProperty = function (object, index, value) { + if (index in object) _objectDp.f(object, index, _propertyDesc(0, value)); + else object[index] = value; + }; + + // getting tag from 19.1.3.6 Object.prototype.toString() + + var TAG$1 = _wks('toStringTag'); + // ES3 wrong here + var ARG = _cof(function () { return arguments; }()) == 'Arguments'; + + // fallback for IE11 Script Access Denied error + var tryGet = function (it, key) { + try { + return it[key]; + } catch (e) { /* empty */ } + }; + + var _classof = function (it) { + var O, T, B; + return it === undefined ? 'Undefined' : it === null ? 'Null' + // @@toStringTag case + : typeof (T = tryGet(O = Object(it), TAG$1)) == 'string' ? T + // builtinTag case + : ARG ? _cof(O) + // ES3 arguments fallback + : (B = _cof(O)) == 'Object' && typeof O.callee == 'function' ? 'Arguments' : B; + }; + + var ITERATOR$3 = _wks('iterator'); + + var core_getIteratorMethod = _core.getIteratorMethod = function (it) { + if (it != undefined) return it[ITERATOR$3] + || it['@@iterator'] + || _iterators[_classof(it)]; + }; + + var ITERATOR$4 = _wks('iterator'); + var SAFE_CLOSING = false; + + try { + var riter = [7][ITERATOR$4](); + riter['return'] = function () { SAFE_CLOSING = true; }; + } catch (e) { /* empty */ } + + var _iterDetect = function (exec, skipClosing) { + if (!skipClosing && !SAFE_CLOSING) return false; + var safe = false; + try { + var arr = [7]; + var iter = arr[ITERATOR$4](); + iter.next = function () { return { done: safe = true }; }; + arr[ITERATOR$4] = function () { return iter; }; + exec(arr); + } catch (e) { /* empty */ } + return safe; + }; + + _export(_export.S + _export.F * !_iterDetect(function (iter) { }), 'Array', { + // 22.1.2.1 Array.from(arrayLike, mapfn = undefined, thisArg = undefined) + from: function from(arrayLike /* , mapfn = undefined, thisArg = undefined */) { + var O = _toObject(arrayLike); + var C = typeof this == 'function' ? this : Array; + var aLen = arguments.length; + var mapfn = aLen > 1 ? arguments[1] : undefined; + var mapping = mapfn !== undefined; + var index = 0; + var iterFn = core_getIteratorMethod(O); + var length, result, step, iterator; + if (mapping) mapfn = _ctx(mapfn, aLen > 2 ? arguments[2] : undefined, 2); + // if object isn't iterable or it's array with default iterator - use simple case + if (iterFn != undefined && !(C == Array && _isArrayIter(iterFn))) { + for (iterator = iterFn.call(O), result = new C(); !(step = iterator.next()).done; index++) { + _createProperty(result, index, mapping ? _iterCall(iterator, mapfn, [step.value, index], true) : step.value); + } + } else { + length = _toLength(O.length); + for (result = new C(length); length > index; index++) { + _createProperty(result, index, mapping ? mapfn(O[index], index) : O[index]); + } + } + result.length = index; + return result; + } + }); + + function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } + } + + function _defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + function _createClass(Constructor, protoProps, staticProps) { + if (protoProps) _defineProperties(Constructor.prototype, protoProps); + if (staticProps) _defineProperties(Constructor, staticProps); + return Constructor; + } + + function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + + return obj; + } + + function _objectSpread(target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i] != null ? arguments[i] : {}; + var ownKeys = Object.keys(source); + + if (typeof Object.getOwnPropertySymbols === 'function') { + ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { + return Object.getOwnPropertyDescriptor(source, sym).enumerable; + })); + } + + ownKeys.forEach(function (key) { + _defineProperty(target, key, source[key]); + }); + } + + return target; + } + + var scrollbarWidth = createCommonjsModule(function (module, exports) { + /*! scrollbarWidth.js v0.1.3 | felixexter | MIT | https://github.com/felixexter/scrollbarWidth */ + (function (root, factory) { + { + module.exports = factory(); + } + }(commonjsGlobal, function () { + + function scrollbarWidth() { + if (typeof document === 'undefined') { + return 0 + } + + var + body = document.body, + box = document.createElement('div'), + boxStyle = box.style, + width; + + boxStyle.position = 'absolute'; + boxStyle.top = boxStyle.left = '-9999px'; + boxStyle.width = boxStyle.height = '100px'; + boxStyle.overflow = 'scroll'; + + body.appendChild(box); + + width = box.offsetWidth - box.clientWidth; + + body.removeChild(box); + + return width; + } + + return scrollbarWidth; + })); + }); + + /** + * lodash (Custom Build) + * Build: `lodash modularize exports="npm" -o ./` + * Copyright jQuery Foundation and other contributors + * Released under MIT license + * Based on Underscore.js 1.8.3 + * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors + */ + + /** Used as the `TypeError` message for "Functions" methods. */ + var FUNC_ERROR_TEXT = 'Expected a function'; + + /** Used as references for various `Number` constants. */ + var NAN = 0 / 0; + + /** `Object#toString` result references. */ + var symbolTag = '[object Symbol]'; + + /** Used to match leading and trailing whitespace. */ + var reTrim = /^\s+|\s+$/g; + + /** Used to detect bad signed hexadecimal string values. */ + var reIsBadHex = /^[-+]0x[0-9a-f]+$/i; + + /** Used to detect binary string values. */ + var reIsBinary = /^0b[01]+$/i; + + /** Used to detect octal string values. */ + var reIsOctal = /^0o[0-7]+$/i; + + /** Built-in method references without a dependency on `root`. */ + var freeParseInt = parseInt; + + /** Detect free variable `global` from Node.js. */ + var freeGlobal = typeof commonjsGlobal == 'object' && commonjsGlobal && commonjsGlobal.Object === Object && commonjsGlobal; + + /** Detect free variable `self`. */ + var freeSelf = typeof self == 'object' && self && self.Object === Object && self; + + /** Used as a reference to the global object. */ + var root = freeGlobal || freeSelf || Function('return this')(); + + /** Used for built-in method references. */ + var objectProto = Object.prototype; + + /** + * Used to resolve the + * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) + * of values. + */ + var objectToString = objectProto.toString; + + /* Built-in method references for those with the same name as other `lodash` methods. */ + var nativeMax = Math.max, + nativeMin = Math.min; + + /** + * Gets the timestamp of the number of milliseconds that have elapsed since + * the Unix epoch (1 January 1970 00:00:00 UTC). + * + * @static + * @memberOf _ + * @since 2.4.0 + * @category Date + * @returns {number} Returns the timestamp. + * @example + * + * _.defer(function(stamp) { + * console.log(_.now() - stamp); + * }, _.now()); + * // => Logs the number of milliseconds it took for the deferred invocation. + */ + var now = function() { + return root.Date.now(); + }; + + /** + * Creates a debounced function that delays invoking `func` until after `wait` + * milliseconds have elapsed since the last time the debounced function was + * invoked. The debounced function comes with a `cancel` method to cancel + * delayed `func` invocations and a `flush` method to immediately invoke them. + * Provide `options` to indicate whether `func` should be invoked on the + * leading and/or trailing edge of the `wait` timeout. The `func` is invoked + * with the last arguments provided to the debounced function. Subsequent + * calls to the debounced function return the result of the last `func` + * invocation. + * + * **Note:** If `leading` and `trailing` options are `true`, `func` is + * invoked on the trailing edge of the timeout only if the debounced function + * is invoked more than once during the `wait` timeout. + * + * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred + * until to the next tick, similar to `setTimeout` with a timeout of `0`. + * + * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) + * for details over the differences between `_.debounce` and `_.throttle`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {Function} func The function to debounce. + * @param {number} [wait=0] The number of milliseconds to delay. + * @param {Object} [options={}] The options object. + * @param {boolean} [options.leading=false] + * Specify invoking on the leading edge of the timeout. + * @param {number} [options.maxWait] + * The maximum time `func` is allowed to be delayed before it's invoked. + * @param {boolean} [options.trailing=true] + * Specify invoking on the trailing edge of the timeout. + * @returns {Function} Returns the new debounced function. + * @example + * + * // Avoid costly calculations while the window size is in flux. + * jQuery(window).on('resize', _.debounce(calculateLayout, 150)); + * + * // Invoke `sendMail` when clicked, debouncing subsequent calls. + * jQuery(element).on('click', _.debounce(sendMail, 300, { + * 'leading': true, + * 'trailing': false + * })); + * + * // Ensure `batchLog` is invoked once after 1 second of debounced calls. + * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 }); + * var source = new EventSource('/stream'); + * jQuery(source).on('message', debounced); + * + * // Cancel the trailing debounced invocation. + * jQuery(window).on('popstate', debounced.cancel); + */ + function debounce(func, wait, options) { + var lastArgs, + lastThis, + maxWait, + result, + timerId, + lastCallTime, + lastInvokeTime = 0, + leading = false, + maxing = false, + trailing = true; + + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + wait = toNumber(wait) || 0; + if (isObject(options)) { + leading = !!options.leading; + maxing = 'maxWait' in options; + maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait; + trailing = 'trailing' in options ? !!options.trailing : trailing; + } + + function invokeFunc(time) { + var args = lastArgs, + thisArg = lastThis; + + lastArgs = lastThis = undefined; + lastInvokeTime = time; + result = func.apply(thisArg, args); + return result; + } + + function leadingEdge(time) { + // Reset any `maxWait` timer. + lastInvokeTime = time; + // Start the timer for the trailing edge. + timerId = setTimeout(timerExpired, wait); + // Invoke the leading edge. + return leading ? invokeFunc(time) : result; + } + + function remainingWait(time) { + var timeSinceLastCall = time - lastCallTime, + timeSinceLastInvoke = time - lastInvokeTime, + result = wait - timeSinceLastCall; + + return maxing ? nativeMin(result, maxWait - timeSinceLastInvoke) : result; + } + + function shouldInvoke(time) { + var timeSinceLastCall = time - lastCallTime, + timeSinceLastInvoke = time - lastInvokeTime; + + // Either this is the first call, activity has stopped and we're at the + // trailing edge, the system time has gone backwards and we're treating + // it as the trailing edge, or we've hit the `maxWait` limit. + return (lastCallTime === undefined || (timeSinceLastCall >= wait) || + (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait)); + } + + function timerExpired() { + var time = now(); + if (shouldInvoke(time)) { + return trailingEdge(time); + } + // Restart the timer. + timerId = setTimeout(timerExpired, remainingWait(time)); + } + + function trailingEdge(time) { + timerId = undefined; + + // Only invoke if we have `lastArgs` which means `func` has been + // debounced at least once. + if (trailing && lastArgs) { + return invokeFunc(time); + } + lastArgs = lastThis = undefined; + return result; + } + + function cancel() { + if (timerId !== undefined) { + clearTimeout(timerId); + } + lastInvokeTime = 0; + lastArgs = lastCallTime = lastThis = timerId = undefined; + } + + function flush() { + return timerId === undefined ? result : trailingEdge(now()); + } + + function debounced() { + var time = now(), + isInvoking = shouldInvoke(time); + + lastArgs = arguments; + lastThis = this; + lastCallTime = time; + + if (isInvoking) { + if (timerId === undefined) { + return leadingEdge(lastCallTime); + } + if (maxing) { + // Handle invocations in a tight loop. + timerId = setTimeout(timerExpired, wait); + return invokeFunc(lastCallTime); + } + } + if (timerId === undefined) { + timerId = setTimeout(timerExpired, wait); + } + return result; + } + debounced.cancel = cancel; + debounced.flush = flush; + return debounced; + } + + /** + * Creates a throttled function that only invokes `func` at most once per + * every `wait` milliseconds. The throttled function comes with a `cancel` + * method to cancel delayed `func` invocations and a `flush` method to + * immediately invoke them. Provide `options` to indicate whether `func` + * should be invoked on the leading and/or trailing edge of the `wait` + * timeout. The `func` is invoked with the last arguments provided to the + * throttled function. Subsequent calls to the throttled function return the + * result of the last `func` invocation. + * + * **Note:** If `leading` and `trailing` options are `true`, `func` is + * invoked on the trailing edge of the timeout only if the throttled function + * is invoked more than once during the `wait` timeout. + * + * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred + * until to the next tick, similar to `setTimeout` with a timeout of `0`. + * + * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) + * for details over the differences between `_.throttle` and `_.debounce`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {Function} func The function to throttle. + * @param {number} [wait=0] The number of milliseconds to throttle invocations to. + * @param {Object} [options={}] The options object. + * @param {boolean} [options.leading=true] + * Specify invoking on the leading edge of the timeout. + * @param {boolean} [options.trailing=true] + * Specify invoking on the trailing edge of the timeout. + * @returns {Function} Returns the new throttled function. + * @example + * + * // Avoid excessively updating the position while scrolling. + * jQuery(window).on('scroll', _.throttle(updatePosition, 100)); + * + * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes. + * var throttled = _.throttle(renewToken, 300000, { 'trailing': false }); + * jQuery(element).on('click', throttled); + * + * // Cancel the trailing throttled invocation. + * jQuery(window).on('popstate', throttled.cancel); + */ + function throttle(func, wait, options) { + var leading = true, + trailing = true; + + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT); + } + if (isObject(options)) { + leading = 'leading' in options ? !!options.leading : leading; + trailing = 'trailing' in options ? !!options.trailing : trailing; + } + return debounce(func, wait, { + 'leading': leading, + 'maxWait': wait, + 'trailing': trailing + }); + } + + /** + * Checks if `value` is the + * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) + * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an object, else `false`. + * @example + * + * _.isObject({}); + * // => true + * + * _.isObject([1, 2, 3]); + * // => true + * + * _.isObject(_.noop); + * // => true + * + * _.isObject(null); + * // => false + */ + function isObject(value) { + var type = typeof value; + return !!value && (type == 'object' || type == 'function'); + } + + /** + * Checks if `value` is object-like. A value is object-like if it's not `null` + * and has a `typeof` result of "object". + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is object-like, else `false`. + * @example + * + * _.isObjectLike({}); + * // => true + * + * _.isObjectLike([1, 2, 3]); + * // => true + * + * _.isObjectLike(_.noop); + * // => false + * + * _.isObjectLike(null); + * // => false + */ + function isObjectLike(value) { + return !!value && typeof value == 'object'; + } + + /** + * Checks if `value` is classified as a `Symbol` primitive or object. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a symbol, else `false`. + * @example + * + * _.isSymbol(Symbol.iterator); + * // => true + * + * _.isSymbol('abc'); + * // => false + */ + function isSymbol(value) { + return typeof value == 'symbol' || + (isObjectLike(value) && objectToString.call(value) == symbolTag); + } + + /** + * Converts `value` to a number. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to process. + * @returns {number} Returns the number. + * @example + * + * _.toNumber(3.2); + * // => 3.2 + * + * _.toNumber(Number.MIN_VALUE); + * // => 5e-324 + * + * _.toNumber(Infinity); + * // => Infinity + * + * _.toNumber('3.2'); + * // => 3.2 + */ + function toNumber(value) { + if (typeof value == 'number') { + return value; + } + if (isSymbol(value)) { + return NAN; + } + if (isObject(value)) { + var other = typeof value.valueOf == 'function' ? value.valueOf() : value; + value = isObject(other) ? (other + '') : other; + } + if (typeof value != 'string') { + return value === 0 ? value : +value; + } + value = value.replace(reTrim, ''); + var isBinary = reIsBinary.test(value); + return (isBinary || reIsOctal.test(value)) + ? freeParseInt(value.slice(2), isBinary ? 2 : 8) + : (reIsBadHex.test(value) ? NAN : +value); + } + + var lodash_throttle = throttle; + + /** + * lodash (Custom Build) + * Build: `lodash modularize exports="npm" -o ./` + * Copyright jQuery Foundation and other contributors + * Released under MIT license + * Based on Underscore.js 1.8.3 + * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors + */ + + /** Used as the `TypeError` message for "Functions" methods. */ + var FUNC_ERROR_TEXT$1 = 'Expected a function'; + + /** Used as references for various `Number` constants. */ + var NAN$1 = 0 / 0; + + /** `Object#toString` result references. */ + var symbolTag$1 = '[object Symbol]'; + + /** Used to match leading and trailing whitespace. */ + var reTrim$1 = /^\s+|\s+$/g; + + /** Used to detect bad signed hexadecimal string values. */ + var reIsBadHex$1 = /^[-+]0x[0-9a-f]+$/i; + + /** Used to detect binary string values. */ + var reIsBinary$1 = /^0b[01]+$/i; + + /** Used to detect octal string values. */ + var reIsOctal$1 = /^0o[0-7]+$/i; + + /** Built-in method references without a dependency on `root`. */ + var freeParseInt$1 = parseInt; + + /** Detect free variable `global` from Node.js. */ + var freeGlobal$1 = typeof commonjsGlobal == 'object' && commonjsGlobal && commonjsGlobal.Object === Object && commonjsGlobal; + + /** Detect free variable `self`. */ + var freeSelf$1 = typeof self == 'object' && self && self.Object === Object && self; + + /** Used as a reference to the global object. */ + var root$1 = freeGlobal$1 || freeSelf$1 || Function('return this')(); + + /** Used for built-in method references. */ + var objectProto$1 = Object.prototype; + + /** + * Used to resolve the + * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) + * of values. + */ + var objectToString$1 = objectProto$1.toString; + + /* Built-in method references for those with the same name as other `lodash` methods. */ + var nativeMax$1 = Math.max, + nativeMin$1 = Math.min; + + /** + * Gets the timestamp of the number of milliseconds that have elapsed since + * the Unix epoch (1 January 1970 00:00:00 UTC). + * + * @static + * @memberOf _ + * @since 2.4.0 + * @category Date + * @returns {number} Returns the timestamp. + * @example + * + * _.defer(function(stamp) { + * console.log(_.now() - stamp); + * }, _.now()); + * // => Logs the number of milliseconds it took for the deferred invocation. + */ + var now$1 = function() { + return root$1.Date.now(); + }; + + /** + * Creates a debounced function that delays invoking `func` until after `wait` + * milliseconds have elapsed since the last time the debounced function was + * invoked. The debounced function comes with a `cancel` method to cancel + * delayed `func` invocations and a `flush` method to immediately invoke them. + * Provide `options` to indicate whether `func` should be invoked on the + * leading and/or trailing edge of the `wait` timeout. The `func` is invoked + * with the last arguments provided to the debounced function. Subsequent + * calls to the debounced function return the result of the last `func` + * invocation. + * + * **Note:** If `leading` and `trailing` options are `true`, `func` is + * invoked on the trailing edge of the timeout only if the debounced function + * is invoked more than once during the `wait` timeout. + * + * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred + * until to the next tick, similar to `setTimeout` with a timeout of `0`. + * + * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) + * for details over the differences between `_.debounce` and `_.throttle`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {Function} func The function to debounce. + * @param {number} [wait=0] The number of milliseconds to delay. + * @param {Object} [options={}] The options object. + * @param {boolean} [options.leading=false] + * Specify invoking on the leading edge of the timeout. + * @param {number} [options.maxWait] + * The maximum time `func` is allowed to be delayed before it's invoked. + * @param {boolean} [options.trailing=true] + * Specify invoking on the trailing edge of the timeout. + * @returns {Function} Returns the new debounced function. + * @example + * + * // Avoid costly calculations while the window size is in flux. + * jQuery(window).on('resize', _.debounce(calculateLayout, 150)); + * + * // Invoke `sendMail` when clicked, debouncing subsequent calls. + * jQuery(element).on('click', _.debounce(sendMail, 300, { + * 'leading': true, + * 'trailing': false + * })); + * + * // Ensure `batchLog` is invoked once after 1 second of debounced calls. + * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 }); + * var source = new EventSource('/stream'); + * jQuery(source).on('message', debounced); + * + * // Cancel the trailing debounced invocation. + * jQuery(window).on('popstate', debounced.cancel); + */ + function debounce$1(func, wait, options) { + var lastArgs, + lastThis, + maxWait, + result, + timerId, + lastCallTime, + lastInvokeTime = 0, + leading = false, + maxing = false, + trailing = true; + + if (typeof func != 'function') { + throw new TypeError(FUNC_ERROR_TEXT$1); + } + wait = toNumber$1(wait) || 0; + if (isObject$1(options)) { + leading = !!options.leading; + maxing = 'maxWait' in options; + maxWait = maxing ? nativeMax$1(toNumber$1(options.maxWait) || 0, wait) : maxWait; + trailing = 'trailing' in options ? !!options.trailing : trailing; + } + + function invokeFunc(time) { + var args = lastArgs, + thisArg = lastThis; + + lastArgs = lastThis = undefined; + lastInvokeTime = time; + result = func.apply(thisArg, args); + return result; + } + + function leadingEdge(time) { + // Reset any `maxWait` timer. + lastInvokeTime = time; + // Start the timer for the trailing edge. + timerId = setTimeout(timerExpired, wait); + // Invoke the leading edge. + return leading ? invokeFunc(time) : result; + } + + function remainingWait(time) { + var timeSinceLastCall = time - lastCallTime, + timeSinceLastInvoke = time - lastInvokeTime, + result = wait - timeSinceLastCall; + + return maxing ? nativeMin$1(result, maxWait - timeSinceLastInvoke) : result; + } + + function shouldInvoke(time) { + var timeSinceLastCall = time - lastCallTime, + timeSinceLastInvoke = time - lastInvokeTime; + + // Either this is the first call, activity has stopped and we're at the + // trailing edge, the system time has gone backwards and we're treating + // it as the trailing edge, or we've hit the `maxWait` limit. + return (lastCallTime === undefined || (timeSinceLastCall >= wait) || + (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait)); + } + + function timerExpired() { + var time = now$1(); + if (shouldInvoke(time)) { + return trailingEdge(time); + } + // Restart the timer. + timerId = setTimeout(timerExpired, remainingWait(time)); + } + + function trailingEdge(time) { + timerId = undefined; + + // Only invoke if we have `lastArgs` which means `func` has been + // debounced at least once. + if (trailing && lastArgs) { + return invokeFunc(time); + } + lastArgs = lastThis = undefined; + return result; + } + + function cancel() { + if (timerId !== undefined) { + clearTimeout(timerId); + } + lastInvokeTime = 0; + lastArgs = lastCallTime = lastThis = timerId = undefined; + } + + function flush() { + return timerId === undefined ? result : trailingEdge(now$1()); + } + + function debounced() { + var time = now$1(), + isInvoking = shouldInvoke(time); + + lastArgs = arguments; + lastThis = this; + lastCallTime = time; + + if (isInvoking) { + if (timerId === undefined) { + return leadingEdge(lastCallTime); + } + if (maxing) { + // Handle invocations in a tight loop. + timerId = setTimeout(timerExpired, wait); + return invokeFunc(lastCallTime); + } + } + if (timerId === undefined) { + timerId = setTimeout(timerExpired, wait); + } + return result; + } + debounced.cancel = cancel; + debounced.flush = flush; + return debounced; + } + + /** + * Checks if `value` is the + * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) + * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an object, else `false`. + * @example + * + * _.isObject({}); + * // => true + * + * _.isObject([1, 2, 3]); + * // => true + * + * _.isObject(_.noop); + * // => true + * + * _.isObject(null); + * // => false + */ + function isObject$1(value) { + var type = typeof value; + return !!value && (type == 'object' || type == 'function'); + } + + /** + * Checks if `value` is object-like. A value is object-like if it's not `null` + * and has a `typeof` result of "object". + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is object-like, else `false`. + * @example + * + * _.isObjectLike({}); + * // => true + * + * _.isObjectLike([1, 2, 3]); + * // => true + * + * _.isObjectLike(_.noop); + * // => false + * + * _.isObjectLike(null); + * // => false + */ + function isObjectLike$1(value) { + return !!value && typeof value == 'object'; + } + + /** + * Checks if `value` is classified as a `Symbol` primitive or object. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a symbol, else `false`. + * @example + * + * _.isSymbol(Symbol.iterator); + * // => true + * + * _.isSymbol('abc'); + * // => false + */ + function isSymbol$1(value) { + return typeof value == 'symbol' || + (isObjectLike$1(value) && objectToString$1.call(value) == symbolTag$1); + } + + /** + * Converts `value` to a number. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to process. + * @returns {number} Returns the number. + * @example + * + * _.toNumber(3.2); + * // => 3.2 + * + * _.toNumber(Number.MIN_VALUE); + * // => 5e-324 + * + * _.toNumber(Infinity); + * // => Infinity + * + * _.toNumber('3.2'); + * // => 3.2 + */ + function toNumber$1(value) { + if (typeof value == 'number') { + return value; + } + if (isSymbol$1(value)) { + return NAN$1; + } + if (isObject$1(value)) { + var other = typeof value.valueOf == 'function' ? value.valueOf() : value; + value = isObject$1(other) ? (other + '') : other; + } + if (typeof value != 'string') { + return value === 0 ? value : +value; + } + value = value.replace(reTrim$1, ''); + var isBinary = reIsBinary$1.test(value); + return (isBinary || reIsOctal$1.test(value)) + ? freeParseInt$1(value.slice(2), isBinary ? 2 : 8) + : (reIsBadHex$1.test(value) ? NAN$1 : +value); + } + + var lodash_debounce = debounce$1; + + /** + * lodash (Custom Build) + * Build: `lodash modularize exports="npm" -o ./` + * Copyright jQuery Foundation and other contributors + * Released under MIT license + * Based on Underscore.js 1.8.3 + * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors + */ + + /** Used as the `TypeError` message for "Functions" methods. */ + var FUNC_ERROR_TEXT$2 = 'Expected a function'; + + /** Used to stand-in for `undefined` hash values. */ + var HASH_UNDEFINED = '__lodash_hash_undefined__'; + + /** `Object#toString` result references. */ + var funcTag = '[object Function]', + genTag = '[object GeneratorFunction]'; + + /** + * Used to match `RegExp` + * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns). + */ + var reRegExpChar = /[\\^$.*+?()[\]{}|]/g; + + /** Used to detect host constructors (Safari). */ + var reIsHostCtor = /^\[object .+?Constructor\]$/; + + /** Detect free variable `global` from Node.js. */ + var freeGlobal$2 = typeof commonjsGlobal == 'object' && commonjsGlobal && commonjsGlobal.Object === Object && commonjsGlobal; + + /** Detect free variable `self`. */ + var freeSelf$2 = typeof self == 'object' && self && self.Object === Object && self; + + /** Used as a reference to the global object. */ + var root$2 = freeGlobal$2 || freeSelf$2 || Function('return this')(); + + /** + * Gets the value at `key` of `object`. + * + * @private + * @param {Object} [object] The object to query. + * @param {string} key The key of the property to get. + * @returns {*} Returns the property value. + */ + function getValue(object, key) { + return object == null ? undefined : object[key]; + } + + /** + * Checks if `value` is a host object in IE < 9. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a host object, else `false`. + */ + function isHostObject(value) { + // Many host objects are `Object` objects that can coerce to strings + // despite having improperly defined `toString` methods. + var result = false; + if (value != null && typeof value.toString != 'function') { + try { + result = !!(value + ''); + } catch (e) {} + } + return result; + } + + /** Used for built-in method references. */ + var arrayProto = Array.prototype, + funcProto = Function.prototype, + objectProto$2 = Object.prototype; + + /** Used to detect overreaching core-js shims. */ + var coreJsData = root$2['__core-js_shared__']; + + /** Used to detect methods masquerading as native. */ + var maskSrcKey = (function() { + var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || ''); + return uid ? ('Symbol(src)_1.' + uid) : ''; + }()); + + /** Used to resolve the decompiled source of functions. */ + var funcToString = funcProto.toString; + + /** Used to check objects for own properties. */ + var hasOwnProperty$1 = objectProto$2.hasOwnProperty; + + /** + * Used to resolve the + * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) + * of values. + */ + var objectToString$2 = objectProto$2.toString; + + /** Used to detect if a method is native. */ + var reIsNative = RegExp('^' + + funcToString.call(hasOwnProperty$1).replace(reRegExpChar, '\\$&') + .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' + ); + + /** Built-in value references. */ + var splice = arrayProto.splice; + + /* Built-in method references that are verified to be native. */ + var Map$1 = getNative(root$2, 'Map'), + nativeCreate = getNative(Object, 'create'); + + /** + * Creates a hash object. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ + function Hash(entries) { + var index = -1, + length = entries ? entries.length : 0; + + this.clear(); + while (++index < length) { + var entry = entries[index]; + this.set(entry[0], entry[1]); + } + } + + /** + * Removes all key-value entries from the hash. + * + * @private + * @name clear + * @memberOf Hash + */ + function hashClear() { + this.__data__ = nativeCreate ? nativeCreate(null) : {}; + } + + /** + * Removes `key` and its value from the hash. + * + * @private + * @name delete + * @memberOf Hash + * @param {Object} hash The hash to modify. + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ + function hashDelete(key) { + return this.has(key) && delete this.__data__[key]; + } + + /** + * Gets the hash value for `key`. + * + * @private + * @name get + * @memberOf Hash + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ + function hashGet(key) { + var data = this.__data__; + if (nativeCreate) { + var result = data[key]; + return result === HASH_UNDEFINED ? undefined : result; + } + return hasOwnProperty$1.call(data, key) ? data[key] : undefined; + } + + /** + * Checks if a hash value for `key` exists. + * + * @private + * @name has + * @memberOf Hash + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function hashHas(key) { + var data = this.__data__; + return nativeCreate ? data[key] !== undefined : hasOwnProperty$1.call(data, key); + } + + /** + * Sets the hash `key` to `value`. + * + * @private + * @name set + * @memberOf Hash + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the hash instance. + */ + function hashSet(key, value) { + var data = this.__data__; + data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value; + return this; + } + + // Add methods to `Hash`. + Hash.prototype.clear = hashClear; + Hash.prototype['delete'] = hashDelete; + Hash.prototype.get = hashGet; + Hash.prototype.has = hashHas; + Hash.prototype.set = hashSet; + + /** + * Creates an list cache object. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ + function ListCache(entries) { + var index = -1, + length = entries ? entries.length : 0; + + this.clear(); + while (++index < length) { + var entry = entries[index]; + this.set(entry[0], entry[1]); + } + } + + /** + * Removes all key-value entries from the list cache. + * + * @private + * @name clear + * @memberOf ListCache + */ + function listCacheClear() { + this.__data__ = []; + } + + /** + * Removes `key` and its value from the list cache. + * + * @private + * @name delete + * @memberOf ListCache + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ + function listCacheDelete(key) { + var data = this.__data__, + index = assocIndexOf(data, key); + + if (index < 0) { + return false; + } + var lastIndex = data.length - 1; + if (index == lastIndex) { + data.pop(); + } else { + splice.call(data, index, 1); + } + return true; + } + + /** + * Gets the list cache value for `key`. + * + * @private + * @name get + * @memberOf ListCache + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ + function listCacheGet(key) { + var data = this.__data__, + index = assocIndexOf(data, key); + + return index < 0 ? undefined : data[index][1]; + } + + /** + * Checks if a list cache value for `key` exists. + * + * @private + * @name has + * @memberOf ListCache + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function listCacheHas(key) { + return assocIndexOf(this.__data__, key) > -1; + } + + /** + * Sets the list cache `key` to `value`. + * + * @private + * @name set + * @memberOf ListCache + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the list cache instance. + */ + function listCacheSet(key, value) { + var data = this.__data__, + index = assocIndexOf(data, key); + + if (index < 0) { + data.push([key, value]); + } else { + data[index][1] = value; + } + return this; + } + + // Add methods to `ListCache`. + ListCache.prototype.clear = listCacheClear; + ListCache.prototype['delete'] = listCacheDelete; + ListCache.prototype.get = listCacheGet; + ListCache.prototype.has = listCacheHas; + ListCache.prototype.set = listCacheSet; + + /** + * Creates a map cache object to store key-value pairs. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ + function MapCache(entries) { + var index = -1, + length = entries ? entries.length : 0; + + this.clear(); + while (++index < length) { + var entry = entries[index]; + this.set(entry[0], entry[1]); + } + } + + /** + * Removes all key-value entries from the map. + * + * @private + * @name clear + * @memberOf MapCache + */ + function mapCacheClear() { + this.__data__ = { + 'hash': new Hash, + 'map': new (Map$1 || ListCache), + 'string': new Hash + }; + } + + /** + * Removes `key` and its value from the map. + * + * @private + * @name delete + * @memberOf MapCache + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ + function mapCacheDelete(key) { + return getMapData(this, key)['delete'](key); + } + + /** + * Gets the map value for `key`. + * + * @private + * @name get + * @memberOf MapCache + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ + function mapCacheGet(key) { + return getMapData(this, key).get(key); + } + + /** + * Checks if a map value for `key` exists. + * + * @private + * @name has + * @memberOf MapCache + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + function mapCacheHas(key) { + return getMapData(this, key).has(key); + } + + /** + * Sets the map `key` to `value`. + * + * @private + * @name set + * @memberOf MapCache + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the map cache instance. + */ + function mapCacheSet(key, value) { + getMapData(this, key).set(key, value); + return this; + } + + // Add methods to `MapCache`. + MapCache.prototype.clear = mapCacheClear; + MapCache.prototype['delete'] = mapCacheDelete; + MapCache.prototype.get = mapCacheGet; + MapCache.prototype.has = mapCacheHas; + MapCache.prototype.set = mapCacheSet; + + /** + * Gets the index at which the `key` is found in `array` of key-value pairs. + * + * @private + * @param {Array} array The array to inspect. + * @param {*} key The key to search for. + * @returns {number} Returns the index of the matched value, else `-1`. + */ + function assocIndexOf(array, key) { + var length = array.length; + while (length--) { + if (eq(array[length][0], key)) { + return length; + } + } + return -1; + } + + /** + * The base implementation of `_.isNative` without bad shim checks. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a native function, + * else `false`. + */ + function baseIsNative(value) { + if (!isObject$2(value) || isMasked(value)) { + return false; + } + var pattern = (isFunction(value) || isHostObject(value)) ? reIsNative : reIsHostCtor; + return pattern.test(toSource(value)); + } + + /** + * Gets the data for `map`. + * + * @private + * @param {Object} map The map to query. + * @param {string} key The reference key. + * @returns {*} Returns the map data. + */ + function getMapData(map, key) { + var data = map.__data__; + return isKeyable(key) + ? data[typeof key == 'string' ? 'string' : 'hash'] + : data.map; + } + + /** + * Gets the native function at `key` of `object`. + * + * @private + * @param {Object} object The object to query. + * @param {string} key The key of the method to get. + * @returns {*} Returns the function if it's native, else `undefined`. + */ + function getNative(object, key) { + var value = getValue(object, key); + return baseIsNative(value) ? value : undefined; + } + + /** + * Checks if `value` is suitable for use as unique object key. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is suitable, else `false`. + */ + function isKeyable(value) { + var type = typeof value; + return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean') + ? (value !== '__proto__') + : (value === null); + } + + /** + * Checks if `func` has its source masked. + * + * @private + * @param {Function} func The function to check. + * @returns {boolean} Returns `true` if `func` is masked, else `false`. + */ + function isMasked(func) { + return !!maskSrcKey && (maskSrcKey in func); + } + + /** + * Converts `func` to its source code. + * + * @private + * @param {Function} func The function to process. + * @returns {string} Returns the source code. + */ + function toSource(func) { + if (func != null) { + try { + return funcToString.call(func); + } catch (e) {} + try { + return (func + ''); + } catch (e) {} + } + return ''; + } + + /** + * Creates a function that memoizes the result of `func`. If `resolver` is + * provided, it determines the cache key for storing the result based on the + * arguments provided to the memoized function. By default, the first argument + * provided to the memoized function is used as the map cache key. The `func` + * is invoked with the `this` binding of the memoized function. + * + * **Note:** The cache is exposed as the `cache` property on the memoized + * function. Its creation may be customized by replacing the `_.memoize.Cache` + * constructor with one whose instances implement the + * [`Map`](http://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-map-prototype-object) + * method interface of `delete`, `get`, `has`, and `set`. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Function + * @param {Function} func The function to have its output memoized. + * @param {Function} [resolver] The function to resolve the cache key. + * @returns {Function} Returns the new memoized function. + * @example + * + * var object = { 'a': 1, 'b': 2 }; + * var other = { 'c': 3, 'd': 4 }; + * + * var values = _.memoize(_.values); + * values(object); + * // => [1, 2] + * + * values(other); + * // => [3, 4] + * + * object.a = 2; + * values(object); + * // => [1, 2] + * + * // Modify the result cache. + * values.cache.set(object, ['a', 'b']); + * values(object); + * // => ['a', 'b'] + * + * // Replace `_.memoize.Cache`. + * _.memoize.Cache = WeakMap; + */ + function memoize(func, resolver) { + if (typeof func != 'function' || (resolver && typeof resolver != 'function')) { + throw new TypeError(FUNC_ERROR_TEXT$2); + } + var memoized = function() { + var args = arguments, + key = resolver ? resolver.apply(this, args) : args[0], + cache = memoized.cache; + + if (cache.has(key)) { + return cache.get(key); + } + var result = func.apply(this, args); + memoized.cache = cache.set(key, result); + return result; + }; + memoized.cache = new (memoize.Cache || MapCache); + return memoized; + } + + // Assign cache to `_.memoize`. + memoize.Cache = MapCache; + + /** + * Performs a + * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * comparison between two values to determine if they are equivalent. + * + * @static + * @memberOf _ + * @since 4.0.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. + * @example + * + * var object = { 'a': 1 }; + * var other = { 'a': 1 }; + * + * _.eq(object, object); + * // => true + * + * _.eq(object, other); + * // => false + * + * _.eq('a', 'a'); + * // => true + * + * _.eq('a', Object('a')); + * // => false + * + * _.eq(NaN, NaN); + * // => true + */ + function eq(value, other) { + return value === other || (value !== value && other !== other); + } + + /** + * Checks if `value` is classified as a `Function` object. + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a function, else `false`. + * @example + * + * _.isFunction(_); + * // => true + * + * _.isFunction(/abc/); + * // => false + */ + function isFunction(value) { + // The use of `Object#toString` avoids issues with the `typeof` operator + // in Safari 8-9 which returns 'object' for typed array and other constructors. + var tag = isObject$2(value) ? objectToString$2.call(value) : ''; + return tag == funcTag || tag == genTag; + } + + /** + * Checks if `value` is the + * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) + * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) + * + * @static + * @memberOf _ + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an object, else `false`. + * @example + * + * _.isObject({}); + * // => true + * + * _.isObject([1, 2, 3]); + * // => true + * + * _.isObject(_.noop); + * // => true + * + * _.isObject(null); + * // => false + */ + function isObject$2(value) { + var type = typeof value; + return !!value && (type == 'object' || type == 'function'); + } + + var lodash_memoize = memoize; + + /** + * A collection of shims that provide minimal functionality of the ES6 collections. + * + * These implementations are not meant to be used outside of the ResizeObserver + * modules as they cover only a limited range of use cases. + */ + /* eslint-disable require-jsdoc, valid-jsdoc */ + var MapShim = (function () { + if (typeof Map !== 'undefined') { + return Map; + } + + /** + * Returns index in provided array that matches the specified key. + * + * @param {Array} arr + * @param {*} key + * @returns {number} + */ + function getIndex(arr, key) { + var result = -1; + + arr.some(function (entry, index) { + if (entry[0] === key) { + result = index; + + return true; + } + + return false; + }); + + return result; + } + + return (function () { + function anonymous() { + this.__entries__ = []; + } + + var prototypeAccessors = { size: { configurable: true } }; + + /** + * @returns {boolean} + */ + prototypeAccessors.size.get = function () { + return this.__entries__.length; + }; + + /** + * @param {*} key + * @returns {*} + */ + anonymous.prototype.get = function (key) { + var index = getIndex(this.__entries__, key); + var entry = this.__entries__[index]; + + return entry && entry[1]; + }; + + /** + * @param {*} key + * @param {*} value + * @returns {void} + */ + anonymous.prototype.set = function (key, value) { + var index = getIndex(this.__entries__, key); + + if (~index) { + this.__entries__[index][1] = value; + } else { + this.__entries__.push([key, value]); + } + }; + + /** + * @param {*} key + * @returns {void} + */ + anonymous.prototype.delete = function (key) { + var entries = this.__entries__; + var index = getIndex(entries, key); + + if (~index) { + entries.splice(index, 1); + } + }; + + /** + * @param {*} key + * @returns {void} + */ + anonymous.prototype.has = function (key) { + return !!~getIndex(this.__entries__, key); + }; + + /** + * @returns {void} + */ + anonymous.prototype.clear = function () { + this.__entries__.splice(0); + }; + + /** + * @param {Function} callback + * @param {*} [ctx=null] + * @returns {void} + */ + anonymous.prototype.forEach = function (callback, ctx) { + var this$1 = this; + if ( ctx === void 0 ) ctx = null; + + for (var i = 0, list = this$1.__entries__; i < list.length; i += 1) { + var entry = list[i]; + + callback.call(ctx, entry[1], entry[0]); + } + }; + + Object.defineProperties( anonymous.prototype, prototypeAccessors ); + + return anonymous; + }()); + })(); + + /** + * Detects whether window and document objects are available in current environment. + */ + var isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined' && window.document === document; + + // Returns global object of a current environment. + var global$1 = (function () { + if (typeof global !== 'undefined' && global.Math === Math) { + return global; + } + + if (typeof self !== 'undefined' && self.Math === Math) { + return self; + } + + if (typeof window !== 'undefined' && window.Math === Math) { + return window; + } + + // eslint-disable-next-line no-new-func + return Function('return this')(); + })(); + + /** + * A shim for the requestAnimationFrame which falls back to the setTimeout if + * first one is not supported. + * + * @returns {number} Requests' identifier. + */ + var requestAnimationFrame$1 = (function () { + if (typeof requestAnimationFrame === 'function') { + // It's required to use a bounded function because IE sometimes throws + // an "Invalid calling object" error if rAF is invoked without the global + // object on the left hand side. + return requestAnimationFrame.bind(global$1); + } + + return function (callback) { return setTimeout(function () { return callback(Date.now()); }, 1000 / 60); }; + })(); + + // Defines minimum timeout before adding a trailing call. + var trailingTimeout = 2; + + /** + * Creates a wrapper function which ensures that provided callback will be + * invoked only once during the specified delay period. + * + * @param {Function} callback - Function to be invoked after the delay period. + * @param {number} delay - Delay after which to invoke callback. + * @returns {Function} + */ + var throttle$1 = function (callback, delay) { + var leadingCall = false, + trailingCall = false, + lastCallTime = 0; + + /** + * Invokes the original callback function and schedules new invocation if + * the "proxy" was called during current request. + * + * @returns {void} + */ + function resolvePending() { + if (leadingCall) { + leadingCall = false; + + callback(); + } + + if (trailingCall) { + proxy(); + } + } + + /** + * Callback invoked after the specified delay. It will further postpone + * invocation of the original function delegating it to the + * requestAnimationFrame. + * + * @returns {void} + */ + function timeoutCallback() { + requestAnimationFrame$1(resolvePending); + } + + /** + * Schedules invocation of the original function. + * + * @returns {void} + */ + function proxy() { + var timeStamp = Date.now(); + + if (leadingCall) { + // Reject immediately following calls. + if (timeStamp - lastCallTime < trailingTimeout) { + return; + } + + // Schedule new call to be in invoked when the pending one is resolved. + // This is important for "transitions" which never actually start + // immediately so there is a chance that we might miss one if change + // happens amids the pending invocation. + trailingCall = true; + } else { + leadingCall = true; + trailingCall = false; + + setTimeout(timeoutCallback, delay); + } + + lastCallTime = timeStamp; + } + + return proxy; + }; + + // Minimum delay before invoking the update of observers. + var REFRESH_DELAY = 20; + + // A list of substrings of CSS properties used to find transition events that + // might affect dimensions of observed elements. + var transitionKeys = ['top', 'right', 'bottom', 'left', 'width', 'height', 'size', 'weight']; + + // Check if MutationObserver is available. + var mutationObserverSupported = typeof MutationObserver !== 'undefined'; + + /** + * Singleton controller class which handles updates of ResizeObserver instances. + */ + var ResizeObserverController = function() { + this.connected_ = false; + this.mutationEventsAdded_ = false; + this.mutationsObserver_ = null; + this.observers_ = []; + + this.onTransitionEnd_ = this.onTransitionEnd_.bind(this); + this.refresh = throttle$1(this.refresh.bind(this), REFRESH_DELAY); + }; + + /** + * Adds observer to observers list. + * + * @param {ResizeObserverSPI} observer - Observer to be added. + * @returns {void} + */ + + + /** + * Holds reference to the controller's instance. + * + * @private {ResizeObserverController} + */ + + + /** + * Keeps reference to the instance of MutationObserver. + * + * @private {MutationObserver} + */ + + /** + * Indicates whether DOM listeners have been added. + * + * @private {boolean} + */ + ResizeObserverController.prototype.addObserver = function (observer) { + if (!~this.observers_.indexOf(observer)) { + this.observers_.push(observer); + } + + // Add listeners if they haven't been added yet. + if (!this.connected_) { + this.connect_(); + } + }; + + /** + * Removes observer from observers list. + * + * @param {ResizeObserverSPI} observer - Observer to be removed. + * @returns {void} + */ + ResizeObserverController.prototype.removeObserver = function (observer) { + var observers = this.observers_; + var index = observers.indexOf(observer); + + // Remove observer if it's present in registry. + if (~index) { + observers.splice(index, 1); + } + + // Remove listeners if controller has no connected observers. + if (!observers.length && this.connected_) { + this.disconnect_(); + } + }; + + /** + * Invokes the update of observers. It will continue running updates insofar + * it detects changes. + * + * @returns {void} + */ + ResizeObserverController.prototype.refresh = function () { + var changesDetected = this.updateObservers_(); + + // Continue running updates if changes have been detected as there might + // be future ones caused by CSS transitions. + if (changesDetected) { + this.refresh(); + } + }; + + /** + * Updates every observer from observers list and notifies them of queued + * entries. + * + * @private + * @returns {boolean} Returns "true" if any observer has detected changes in + * dimensions of it's elements. + */ + ResizeObserverController.prototype.updateObservers_ = function () { + // Collect observers that have active observations. + var activeObservers = this.observers_.filter(function (observer) { + return observer.gatherActive(), observer.hasActive(); + }); + + // Deliver notifications in a separate cycle in order to avoid any + // collisions between observers, e.g. when multiple instances of + // ResizeObserver are tracking the same element and the callback of one + // of them changes content dimensions of the observed target. Sometimes + // this may result in notifications being blocked for the rest of observers. + activeObservers.forEach(function (observer) { return observer.broadcastActive(); }); + + return activeObservers.length > 0; + }; + + /** + * Initializes DOM listeners. + * + * @private + * @returns {void} + */ + ResizeObserverController.prototype.connect_ = function () { + // Do nothing if running in a non-browser environment or if listeners + // have been already added. + if (!isBrowser || this.connected_) { + return; + } + + // Subscription to the "Transitionend" event is used as a workaround for + // delayed transitions. This way it's possible to capture at least the + // final state of an element. + document.addEventListener('transitionend', this.onTransitionEnd_); + + window.addEventListener('resize', this.refresh); + + if (mutationObserverSupported) { + this.mutationsObserver_ = new MutationObserver(this.refresh); + + this.mutationsObserver_.observe(document, { + attributes: true, + childList: true, + characterData: true, + subtree: true + }); + } else { + document.addEventListener('DOMSubtreeModified', this.refresh); + + this.mutationEventsAdded_ = true; + } + + this.connected_ = true; + }; + + /** + * Removes DOM listeners. + * + * @private + * @returns {void} + */ + ResizeObserverController.prototype.disconnect_ = function () { + // Do nothing if running in a non-browser environment or if listeners + // have been already removed. + if (!isBrowser || !this.connected_) { + return; + } + + document.removeEventListener('transitionend', this.onTransitionEnd_); + window.removeEventListener('resize', this.refresh); + + if (this.mutationsObserver_) { + this.mutationsObserver_.disconnect(); + } + + if (this.mutationEventsAdded_) { + document.removeEventListener('DOMSubtreeModified', this.refresh); + } + + this.mutationsObserver_ = null; + this.mutationEventsAdded_ = false; + this.connected_ = false; + }; + + /** + * "Transitionend" event handler. + * + * @private + * @param {TransitionEvent} event + * @returns {void} + */ + ResizeObserverController.prototype.onTransitionEnd_ = function (ref) { + var propertyName = ref.propertyName; if ( propertyName === void 0 ) propertyName = ''; + + // Detect whether transition may affect dimensions of an element. + var isReflowProperty = transitionKeys.some(function (key) { + return !!~propertyName.indexOf(key); + }); + + if (isReflowProperty) { + this.refresh(); + } + }; + + /** + * Returns instance of the ResizeObserverController. + * + * @returns {ResizeObserverController} + */ + ResizeObserverController.getInstance = function () { + if (!this.instance_) { + this.instance_ = new ResizeObserverController(); + } + + return this.instance_; + }; + + ResizeObserverController.instance_ = null; + + /** + * Defines non-writable/enumerable properties of the provided target object. + * + * @param {Object} target - Object for which to define properties. + * @param {Object} props - Properties to be defined. + * @returns {Object} Target object. + */ + var defineConfigurable = (function (target, props) { + for (var i = 0, list = Object.keys(props); i < list.length; i += 1) { + var key = list[i]; + + Object.defineProperty(target, key, { + value: props[key], + enumerable: false, + writable: false, + configurable: true + }); + } + + return target; + }); + + /** + * Returns the global object associated with provided element. + * + * @param {Object} target + * @returns {Object} + */ + var getWindowOf = (function (target) { + // Assume that the element is an instance of Node, which means that it + // has the "ownerDocument" property from which we can retrieve a + // corresponding global object. + var ownerGlobal = target && target.ownerDocument && target.ownerDocument.defaultView; + + // Return the local global object if it's not possible extract one from + // provided element. + return ownerGlobal || global$1; + }); + + // Placeholder of an empty content rectangle. + var emptyRect = createRectInit(0, 0, 0, 0); + + /** + * Converts provided string to a number. + * + * @param {number|string} value + * @returns {number} + */ + function toFloat(value) { + return parseFloat(value) || 0; + } + + /** + * Extracts borders size from provided styles. + * + * @param {CSSStyleDeclaration} styles + * @param {...string} positions - Borders positions (top, right, ...) + * @returns {number} + */ + function getBordersSize(styles) { + var positions = [], len = arguments.length - 1; + while ( len-- > 0 ) positions[ len ] = arguments[ len + 1 ]; + + return positions.reduce(function (size, position) { + var value = styles['border-' + position + '-width']; + + return size + toFloat(value); + }, 0); + } + + /** + * Extracts paddings sizes from provided styles. + * + * @param {CSSStyleDeclaration} styles + * @returns {Object} Paddings box. + */ + function getPaddings(styles) { + var positions = ['top', 'right', 'bottom', 'left']; + var paddings = {}; + + for (var i = 0, list = positions; i < list.length; i += 1) { + var position = list[i]; + + var value = styles['padding-' + position]; + + paddings[position] = toFloat(value); + } + + return paddings; + } + + /** + * Calculates content rectangle of provided SVG element. + * + * @param {SVGGraphicsElement} target - Element content rectangle of which needs + * to be calculated. + * @returns {DOMRectInit} + */ + function getSVGContentRect(target) { + var bbox = target.getBBox(); + + return createRectInit(0, 0, bbox.width, bbox.height); + } + + /** + * Calculates content rectangle of provided HTMLElement. + * + * @param {HTMLElement} target - Element for which to calculate the content rectangle. + * @returns {DOMRectInit} + */ + function getHTMLElementContentRect(target) { + // Client width & height properties can't be + // used exclusively as they provide rounded values. + var clientWidth = target.clientWidth; + var clientHeight = target.clientHeight; + + // By this condition we can catch all non-replaced inline, hidden and + // detached elements. Though elements with width & height properties less + // than 0.5 will be discarded as well. + // + // Without it we would need to implement separate methods for each of + // those cases and it's not possible to perform a precise and performance + // effective test for hidden elements. E.g. even jQuery's ':visible' filter + // gives wrong results for elements with width & height less than 0.5. + if (!clientWidth && !clientHeight) { + return emptyRect; + } + + var styles = getWindowOf(target).getComputedStyle(target); + var paddings = getPaddings(styles); + var horizPad = paddings.left + paddings.right; + var vertPad = paddings.top + paddings.bottom; + + // Computed styles of width & height are being used because they are the + // only dimensions available to JS that contain non-rounded values. It could + // be possible to utilize the getBoundingClientRect if only it's data wasn't + // affected by CSS transformations let alone paddings, borders and scroll bars. + var width = toFloat(styles.width), + height = toFloat(styles.height); + + // Width & height include paddings and borders when the 'border-box' box + // model is applied (except for IE). + if (styles.boxSizing === 'border-box') { + // Following conditions are required to handle Internet Explorer which + // doesn't include paddings and borders to computed CSS dimensions. + // + // We can say that if CSS dimensions + paddings are equal to the "client" + // properties then it's either IE, and thus we don't need to subtract + // anything, or an element merely doesn't have paddings/borders styles. + if (Math.round(width + horizPad) !== clientWidth) { + width -= getBordersSize(styles, 'left', 'right') + horizPad; + } + + if (Math.round(height + vertPad) !== clientHeight) { + height -= getBordersSize(styles, 'top', 'bottom') + vertPad; + } + } + + // Following steps can't be applied to the document's root element as its + // client[Width/Height] properties represent viewport area of the window. + // Besides, it's as well not necessary as the itself neither has + // rendered scroll bars nor it can be clipped. + if (!isDocumentElement(target)) { + // In some browsers (only in Firefox, actually) CSS width & height + // include scroll bars size which can be removed at this step as scroll + // bars are the only difference between rounded dimensions + paddings + // and "client" properties, though that is not always true in Chrome. + var vertScrollbar = Math.round(width + horizPad) - clientWidth; + var horizScrollbar = Math.round(height + vertPad) - clientHeight; + + // Chrome has a rather weird rounding of "client" properties. + // E.g. for an element with content width of 314.2px it sometimes gives + // the client width of 315px and for the width of 314.7px it may give + // 314px. And it doesn't happen all the time. So just ignore this delta + // as a non-relevant. + if (Math.abs(vertScrollbar) !== 1) { + width -= vertScrollbar; + } + + if (Math.abs(horizScrollbar) !== 1) { + height -= horizScrollbar; + } + } + + return createRectInit(paddings.left, paddings.top, width, height); + } + + /** + * Checks whether provided element is an instance of the SVGGraphicsElement. + * + * @param {Element} target - Element to be checked. + * @returns {boolean} + */ + var isSVGGraphicsElement = (function () { + // Some browsers, namely IE and Edge, don't have the SVGGraphicsElement + // interface. + if (typeof SVGGraphicsElement !== 'undefined') { + return function (target) { return target instanceof getWindowOf(target).SVGGraphicsElement; }; + } + + // If it's so, then check that element is at least an instance of the + // SVGElement and that it has the "getBBox" method. + // eslint-disable-next-line no-extra-parens + return function (target) { return target instanceof getWindowOf(target).SVGElement && typeof target.getBBox === 'function'; }; + })(); + + /** + * Checks whether provided element is a document element (). + * + * @param {Element} target - Element to be checked. + * @returns {boolean} + */ + function isDocumentElement(target) { + return target === getWindowOf(target).document.documentElement; + } + + /** + * Calculates an appropriate content rectangle for provided html or svg element. + * + * @param {Element} target - Element content rectangle of which needs to be calculated. + * @returns {DOMRectInit} + */ + function getContentRect(target) { + if (!isBrowser) { + return emptyRect; + } + + if (isSVGGraphicsElement(target)) { + return getSVGContentRect(target); + } + + return getHTMLElementContentRect(target); + } + + /** + * Creates rectangle with an interface of the DOMRectReadOnly. + * Spec: https://drafts.fxtf.org/geometry/#domrectreadonly + * + * @param {DOMRectInit} rectInit - Object with rectangle's x/y coordinates and dimensions. + * @returns {DOMRectReadOnly} + */ + function createReadOnlyRect(ref) { + var x = ref.x; + var y = ref.y; + var width = ref.width; + var height = ref.height; + + // If DOMRectReadOnly is available use it as a prototype for the rectangle. + var Constr = typeof DOMRectReadOnly !== 'undefined' ? DOMRectReadOnly : Object; + var rect = Object.create(Constr.prototype); + + // Rectangle's properties are not writable and non-enumerable. + defineConfigurable(rect, { + x: x, y: y, width: width, height: height, + top: y, + right: x + width, + bottom: height + y, + left: x + }); + + return rect; + } + + /** + * Creates DOMRectInit object based on the provided dimensions and the x/y coordinates. + * Spec: https://drafts.fxtf.org/geometry/#dictdef-domrectinit + * + * @param {number} x - X coordinate. + * @param {number} y - Y coordinate. + * @param {number} width - Rectangle's width. + * @param {number} height - Rectangle's height. + * @returns {DOMRectInit} + */ + function createRectInit(x, y, width, height) { + return { x: x, y: y, width: width, height: height }; + } + + /** + * Class that is responsible for computations of the content rectangle of + * provided DOM element and for keeping track of it's changes. + */ + var ResizeObservation = function(target) { + this.broadcastWidth = 0; + this.broadcastHeight = 0; + this.contentRect_ = createRectInit(0, 0, 0, 0); + + this.target = target; + }; + + /** + * Updates content rectangle and tells whether it's width or height properties + * have changed since the last broadcast. + * + * @returns {boolean} + */ + + + /** + * Reference to the last observed content rectangle. + * + * @private {DOMRectInit} + */ + + + /** + * Broadcasted width of content rectangle. + * + * @type {number} + */ + ResizeObservation.prototype.isActive = function () { + var rect = getContentRect(this.target); + + this.contentRect_ = rect; + + return rect.width !== this.broadcastWidth || rect.height !== this.broadcastHeight; + }; + + /** + * Updates 'broadcastWidth' and 'broadcastHeight' properties with a data + * from the corresponding properties of the last observed content rectangle. + * + * @returns {DOMRectInit} Last observed content rectangle. + */ + ResizeObservation.prototype.broadcastRect = function () { + var rect = this.contentRect_; + + this.broadcastWidth = rect.width; + this.broadcastHeight = rect.height; + + return rect; + }; + + var ResizeObserverEntry = function(target, rectInit) { + var contentRect = createReadOnlyRect(rectInit); + + // According to the specification following properties are not writable + // and are also not enumerable in the native implementation. + // + // Property accessors are not being used as they'd require to define a + // private WeakMap storage which may cause memory leaks in browsers that + // don't support this type of collections. + defineConfigurable(this, { target: target, contentRect: contentRect }); + }; + + var ResizeObserverSPI = function(callback, controller, callbackCtx) { + this.activeObservations_ = []; + this.observations_ = new MapShim(); + + if (typeof callback !== 'function') { + throw new TypeError('The callback provided as parameter 1 is not a function.'); + } + + this.callback_ = callback; + this.controller_ = controller; + this.callbackCtx_ = callbackCtx; + }; + + /** + * Starts observing provided element. + * + * @param {Element} target - Element to be observed. + * @returns {void} + */ + + + /** + * Registry of the ResizeObservation instances. + * + * @private {Map} + */ + + + /** + * Public ResizeObserver instance which will be passed to the callback + * function and used as a value of it's "this" binding. + * + * @private {ResizeObserver} + */ + + /** + * Collection of resize observations that have detected changes in dimensions + * of elements. + * + * @private {Array} + */ + ResizeObserverSPI.prototype.observe = function (target) { + if (!arguments.length) { + throw new TypeError('1 argument required, but only 0 present.'); + } + + // Do nothing if current environment doesn't have the Element interface. + if (typeof Element === 'undefined' || !(Element instanceof Object)) { + return; + } + + if (!(target instanceof getWindowOf(target).Element)) { + throw new TypeError('parameter 1 is not of type "Element".'); + } + + var observations = this.observations_; + + // Do nothing if element is already being observed. + if (observations.has(target)) { + return; + } + + observations.set(target, new ResizeObservation(target)); + + this.controller_.addObserver(this); + + // Force the update of observations. + this.controller_.refresh(); + }; + + /** + * Stops observing provided element. + * + * @param {Element} target - Element to stop observing. + * @returns {void} + */ + ResizeObserverSPI.prototype.unobserve = function (target) { + if (!arguments.length) { + throw new TypeError('1 argument required, but only 0 present.'); + } + + // Do nothing if current environment doesn't have the Element interface. + if (typeof Element === 'undefined' || !(Element instanceof Object)) { + return; + } + + if (!(target instanceof getWindowOf(target).Element)) { + throw new TypeError('parameter 1 is not of type "Element".'); + } + + var observations = this.observations_; + + // Do nothing if element is not being observed. + if (!observations.has(target)) { + return; + } + + observations.delete(target); + + if (!observations.size) { + this.controller_.removeObserver(this); + } + }; + + /** + * Stops observing all elements. + * + * @returns {void} + */ + ResizeObserverSPI.prototype.disconnect = function () { + this.clearActive(); + this.observations_.clear(); + this.controller_.removeObserver(this); + }; + + /** + * Collects observation instances the associated element of which has changed + * it's content rectangle. + * + * @returns {void} + */ + ResizeObserverSPI.prototype.gatherActive = function () { + var this$1 = this; + + this.clearActive(); + + this.observations_.forEach(function (observation) { + if (observation.isActive()) { + this$1.activeObservations_.push(observation); + } + }); + }; + + /** + * Invokes initial callback function with a list of ResizeObserverEntry + * instances collected from active resize observations. + * + * @returns {void} + */ + ResizeObserverSPI.prototype.broadcastActive = function () { + // Do nothing if observer doesn't have active observations. + if (!this.hasActive()) { + return; + } + + var ctx = this.callbackCtx_; + + // Create ResizeObserverEntry instance for every active observation. + var entries = this.activeObservations_.map(function (observation) { + return new ResizeObserverEntry(observation.target, observation.broadcastRect()); + }); + + this.callback_.call(ctx, entries, ctx); + this.clearActive(); + }; + + /** + * Clears the collection of active observations. + * + * @returns {void} + */ + ResizeObserverSPI.prototype.clearActive = function () { + this.activeObservations_.splice(0); + }; + + /** + * Tells whether observer has active observations. + * + * @returns {boolean} + */ + ResizeObserverSPI.prototype.hasActive = function () { + return this.activeObservations_.length > 0; + }; + + // Registry of internal observers. If WeakMap is not available use current shim + // for the Map collection as it has all required methods and because WeakMap + // can't be fully polyfilled anyway. + var observers = typeof WeakMap !== 'undefined' ? new WeakMap() : new MapShim(); + + /** + * ResizeObserver API. Encapsulates the ResizeObserver SPI implementation + * exposing only those methods and properties that are defined in the spec. + */ + var ResizeObserver = function(callback) { + if (!(this instanceof ResizeObserver)) { + throw new TypeError('Cannot call a class as a function.'); + } + if (!arguments.length) { + throw new TypeError('1 argument required, but only 0 present.'); + } + + var controller = ResizeObserverController.getInstance(); + var observer = new ResizeObserverSPI(callback, controller, this); + + observers.set(this, observer); + }; + + // Expose public methods of ResizeObserver. + ['observe', 'unobserve', 'disconnect'].forEach(function (method) { + ResizeObserver.prototype[method] = function () { + return (ref = observers.get(this))[method].apply(ref, arguments); + var ref; + }; + }); + + var index = (function () { + // Export existing implementation if available. + if (typeof global$1.ResizeObserver !== 'undefined') { + return global$1.ResizeObserver; + } + + return ResizeObserver; + })(); + + var canUseDOM = !!( + typeof window !== 'undefined' && + window.document && + window.document.createElement + ); + + var canUseDom = canUseDOM; + + var SimpleBar = + /*#__PURE__*/ + function () { + function SimpleBar(element, options) { + var _this = this; + + _classCallCheck(this, SimpleBar); + + this.onScroll = function () { + if (!_this.scrollXTicking) { + window.requestAnimationFrame(_this.scrollX); + _this.scrollXTicking = true; + } + + if (!_this.scrollYTicking) { + window.requestAnimationFrame(_this.scrollY); + _this.scrollYTicking = true; + } + }; + + this.scrollX = function () { + if (_this.axis.x.isOverflowing) { + _this.showScrollbar('x'); + + _this.positionScrollbar('x'); + } + + _this.scrollXTicking = false; + }; + + this.scrollY = function () { + if (_this.axis.y.isOverflowing) { + _this.showScrollbar('y'); + + _this.positionScrollbar('y'); + } + + _this.scrollYTicking = false; + }; + + this.onMouseEnter = function () { + _this.showScrollbar('x'); + + _this.showScrollbar('y'); + }; + + this.onMouseMove = function (e) { + _this.mouseX = e.clientX; + _this.mouseY = e.clientY; + + if (_this.axis.x.isOverflowing || _this.axis.x.forceVisible) { + _this.onMouseMoveForAxis('x'); + } + + if (_this.axis.y.isOverflowing || _this.axis.y.forceVisible) { + _this.onMouseMoveForAxis('y'); + } + }; + + this.onMouseLeave = function () { + _this.onMouseMove.cancel(); + + if (_this.axis.x.isOverflowing || _this.axis.x.forceVisible) { + _this.onMouseLeaveForAxis('x'); + } + + if (_this.axis.y.isOverflowing || _this.axis.y.forceVisible) { + _this.onMouseLeaveForAxis('y'); + } + + _this.mouseX = -1; + _this.mouseY = -1; + }; + + this.onWindowResize = function () { + // Recalculate scrollbarWidth in case it's a zoom + _this.scrollbarWidth = scrollbarWidth(); + + _this.hideNativeScrollbar(); + }; + + this.hideScrollbars = function () { + _this.axis.x.track.rect = _this.axis.x.track.el.getBoundingClientRect(); + _this.axis.y.track.rect = _this.axis.y.track.el.getBoundingClientRect(); + + if (!_this.isWithinBounds(_this.axis.y.track.rect)) { + _this.axis.y.scrollbar.el.classList.remove(_this.classNames.visible); + + _this.axis.y.isVisible = false; + } + + if (!_this.isWithinBounds(_this.axis.x.track.rect)) { + _this.axis.x.scrollbar.el.classList.remove(_this.classNames.visible); + + _this.axis.x.isVisible = false; + } + }; + + this.onPointerEvent = function (e) { + var isWithinBoundsY, isWithinBoundsX; + + if (_this.axis.x.isOverflowing || _this.axis.x.forceVisible) { + isWithinBoundsX = _this.isWithinBounds(_this.axis.x.scrollbar.rect); + } + + if (_this.axis.y.isOverflowing || _this.axis.y.forceVisible) { + isWithinBoundsY = _this.isWithinBounds(_this.axis.y.scrollbar.rect); + } // If any pointer event is called on the scrollbar + + + if (isWithinBoundsY || isWithinBoundsX) { + // Preventing the event's default action stops text being + // selectable during the drag. + e.preventDefault(); // Prevent event leaking + + e.stopPropagation(); + + if (e.type === 'mousedown') { + if (isWithinBoundsY) { + _this.onDragStart(e, 'y'); + } + + if (isWithinBoundsX) { + _this.onDragStart(e, 'x'); + } + } + } + }; + + this.drag = function (e) { + var eventOffset; + var track = _this.axis[_this.draggedAxis].track; + var trackSize = track.rect[_this.axis[_this.draggedAxis].sizeAttr]; + var scrollbar = _this.axis[_this.draggedAxis].scrollbar; + e.preventDefault(); + e.stopPropagation(); + + if (_this.draggedAxis === 'y') { + eventOffset = e.pageY; + } else { + eventOffset = e.pageX; + } // Calculate how far the user's mouse is from the top/left of the scrollbar (minus the dragOffset). + + + var dragPos = eventOffset - track.rect[_this.axis[_this.draggedAxis].offsetAttr] - _this.axis[_this.draggedAxis].dragOffset; // Convert the mouse position into a percentage of the scrollbar height/width. + + var dragPerc = dragPos / track.rect[_this.axis[_this.draggedAxis].sizeAttr]; // Scroll the content by the same percentage. + + var scrollPos = dragPerc * _this.contentEl[_this.axis[_this.draggedAxis].scrollSizeAttr]; // Fix browsers inconsistency on RTL + + if (_this.draggedAxis === 'x') { + scrollPos = _this.isRtl && SimpleBar.getRtlHelpers().isRtlScrollbarInverted ? scrollPos - (trackSize + scrollbar.size) : scrollPos; + scrollPos = _this.isRtl && SimpleBar.getRtlHelpers().isRtlScrollingInverted ? -scrollPos : scrollPos; + } + + _this.contentEl[_this.axis[_this.draggedAxis].scrollOffsetAttr] = scrollPos; + }; + + this.onEndDrag = function (e) { + e.preventDefault(); + e.stopPropagation(); + document.removeEventListener('mousemove', _this.drag); + document.removeEventListener('mouseup', _this.onEndDrag); + }; + + this.el = element; + this.flashTimeout; + this.contentEl; + this.offsetEl; + this.maskEl; + this.globalObserver; + this.mutationObserver; + this.resizeObserver; + this.scrollbarWidth; + this.minScrollbarWidth = 20; + this.options = _objectSpread({}, SimpleBar.defaultOptions, options); + this.classNames = _objectSpread({}, SimpleBar.defaultOptions.classNames, this.options.classNames); + this.isRtl; + this.axis = { + x: { + scrollOffsetAttr: 'scrollLeft', + sizeAttr: 'width', + scrollSizeAttr: 'scrollWidth', + offsetAttr: 'left', + overflowAttr: 'overflowX', + dragOffset: 0, + isOverflowing: true, + isVisible: false, + forceVisible: false, + track: {}, + scrollbar: {} + }, + y: { + scrollOffsetAttr: 'scrollTop', + sizeAttr: 'height', + scrollSizeAttr: 'scrollHeight', + offsetAttr: 'top', + overflowAttr: 'overflowY', + dragOffset: 0, + isOverflowing: true, + isVisible: false, + forceVisible: false, + track: {}, + scrollbar: {} + } + }; + this.recalculate = lodash_throttle(this.recalculate.bind(this), 64); + this.onMouseMove = lodash_throttle(this.onMouseMove.bind(this), 64); + this.hideScrollbars = lodash_debounce(this.hideScrollbars.bind(this), this.options.timeout); + this.onWindowResize = lodash_debounce(this.onWindowResize.bind(this), 64, { + leading: true + }); + SimpleBar.getRtlHelpers = lodash_memoize(SimpleBar.getRtlHelpers); // getContentElement is deprecated + + this.getContentElement = this.getScrollElement; + this.init(); + } + /** + * Static properties + */ + + /** + * Helper to fix browsers inconsistency on RTL: + * - Firefox inverts the scrollbar initial position + * - IE11 inverts both scrollbar position and scrolling offset + * Directly inspired by @KingSora's OverlayScrollbars https://github.com/KingSora/OverlayScrollbars/blob/master/js/OverlayScrollbars.js#L1634 + */ + + + _createClass(SimpleBar, [{ + key: "init", + value: function init() { + // Save a reference to the instance, so we know this DOM node has already been instancied + this.el.SimpleBar = this; + this.initDOM(); // We stop here on server-side + + if (canUseDom) { + // Recalculate scrollbarWidth in case it's a zoom + this.scrollbarWidth = scrollbarWidth(); + this.recalculate(); + this.initListeners(); + } + } + }, { + key: "initDOM", + value: function initDOM() { + var _this2 = this; + + // make sure this element doesn't have the elements yet + if (Array.from(this.el.children).filter(function (child) { + return child.classList.contains(_this2.classNames.wrapper); + }).length) { + // assume that element has his DOM already initiated + this.wrapperEl = this.el.querySelector(".".concat(this.classNames.wrapper)); + this.contentEl = this.el.querySelector(".".concat(this.classNames.content)); + this.offsetEl = this.el.querySelector(".".concat(this.classNames.offset)); + this.maskEl = this.el.querySelector(".".concat(this.classNames.mask)); + this.placeholderEl = this.el.querySelector(".".concat(this.classNames.placeholder)); + this.heightAutoObserverWrapperEl = this.el.querySelector(".".concat(this.classNames.heightAutoObserverWrapperEl)); + this.heightAutoObserverEl = this.el.querySelector(".".concat(this.classNames.heightAutoObserverEl)); + this.axis.x.track.el = this.el.querySelector(".".concat(this.classNames.track, ".").concat(this.classNames.horizontal)); + this.axis.y.track.el = this.el.querySelector(".".concat(this.classNames.track, ".").concat(this.classNames.vertical)); + } else { + // Prepare DOM + this.wrapperEl = document.createElement('div'); + this.contentEl = document.createElement('div'); + this.offsetEl = document.createElement('div'); + this.maskEl = document.createElement('div'); + this.placeholderEl = document.createElement('div'); + this.heightAutoObserverWrapperEl = document.createElement('div'); + this.heightAutoObserverEl = document.createElement('div'); + this.wrapperEl.classList.add(this.classNames.wrapper); + this.contentEl.classList.add(this.classNames.content); + this.offsetEl.classList.add(this.classNames.offset); + this.maskEl.classList.add(this.classNames.mask); + this.placeholderEl.classList.add(this.classNames.placeholder); + this.heightAutoObserverWrapperEl.classList.add(this.classNames.heightAutoObserverWrapperEl); + this.heightAutoObserverEl.classList.add(this.classNames.heightAutoObserverEl); + + while (this.el.firstChild) { + this.contentEl.appendChild(this.el.firstChild); + } + + this.offsetEl.appendChild(this.contentEl); + this.maskEl.appendChild(this.offsetEl); + this.heightAutoObserverWrapperEl.appendChild(this.heightAutoObserverEl); + this.wrapperEl.appendChild(this.heightAutoObserverWrapperEl); + this.wrapperEl.appendChild(this.maskEl); + this.wrapperEl.appendChild(this.placeholderEl); + this.el.appendChild(this.wrapperEl); + } + + if (!this.axis.x.track.el || !this.axis.y.track.el) { + var track = document.createElement('div'); + var scrollbar = document.createElement('div'); + track.classList.add(this.classNames.track); + scrollbar.classList.add(this.classNames.scrollbar); + + if (!this.options.autoHide) { + scrollbar.classList.add(this.classNames.visible); + } + + track.appendChild(scrollbar); + this.axis.x.track.el = track.cloneNode(true); + this.axis.x.track.el.classList.add(this.classNames.horizontal); + this.axis.y.track.el = track.cloneNode(true); + this.axis.y.track.el.classList.add(this.classNames.vertical); + this.el.appendChild(this.axis.x.track.el); + this.el.appendChild(this.axis.y.track.el); + } + + this.axis.x.scrollbar.el = this.axis.x.track.el.querySelector(".".concat(this.classNames.scrollbar)); + this.axis.y.scrollbar.el = this.axis.y.track.el.querySelector(".".concat(this.classNames.scrollbar)); + this.el.setAttribute('data-simplebar', 'init'); + } + }, { + key: "initListeners", + value: function initListeners() { + var _this3 = this; + + // Event listeners + if (this.options.autoHide) { + this.el.addEventListener('mouseenter', this.onMouseEnter); + } + + ['mousedown', 'click', 'dblclick', 'touchstart', 'touchend', 'touchmove'].forEach(function (e) { + _this3.el.addEventListener(e, _this3.onPointerEvent, true); + }); + this.el.addEventListener('mousemove', this.onMouseMove); + this.el.addEventListener('mouseleave', this.onMouseLeave); + this.contentEl.addEventListener('scroll', this.onScroll); // Browser zoom triggers a window resize + + window.addEventListener('resize', this.onWindowResize); // MutationObserver is IE11+ + + if (typeof MutationObserver !== 'undefined') { + // create an observer instance + this.mutationObserver = new MutationObserver(function (mutations) { + mutations.forEach(function (mutation) { + if (mutation.target === _this3.el || !_this3.isChildNode(mutation.target) || mutation.addedNodes.length) { + _this3.recalculate(); + } + }); + }); // pass in the target node, as well as the observer options + + this.mutationObserver.observe(this.el, { + attributes: true, + childList: true, + characterData: true, + subtree: true + }); + } + + this.resizeObserver = new index(this.recalculate); + this.resizeObserver.observe(this.el); + } + }, { + key: "recalculate", + value: function recalculate() { + var isHeightAuto = this.heightAutoObserverEl.offsetHeight <= 1; + this.elStyles = window.getComputedStyle(this.el); + this.isRtl = this.elStyles.direction === 'rtl'; + this.contentEl.style.padding = "".concat(this.elStyles.paddingTop, " ").concat(this.elStyles.paddingRight, " ").concat(this.elStyles.paddingBottom, " ").concat(this.elStyles.paddingLeft); + this.contentEl.style.height = isHeightAuto ? 'auto' : '100%'; + this.placeholderEl.style.width = "".concat(this.contentEl.scrollWidth, "px"); + this.placeholderEl.style.height = "".concat(this.contentEl.scrollHeight, "px"); + this.wrapperEl.style.margin = "-".concat(this.elStyles.paddingTop, " -").concat(this.elStyles.paddingRight, " -").concat(this.elStyles.paddingBottom, " -").concat(this.elStyles.paddingLeft); + this.axis.x.track.rect = this.axis.x.track.el.getBoundingClientRect(); + this.axis.y.track.rect = this.axis.y.track.el.getBoundingClientRect(); // Set isOverflowing to false if scrollbar is not necessary (content is shorter than offset) + + this.axis.x.isOverflowing = (this.scrollbarWidth ? this.contentEl.scrollWidth : this.contentEl.scrollWidth - this.minScrollbarWidth) > Math.ceil(this.axis.x.track.rect.width); + this.axis.y.isOverflowing = (this.scrollbarWidth ? this.contentEl.scrollHeight : this.contentEl.scrollHeight - this.minScrollbarWidth) > Math.ceil(this.axis.y.track.rect.height); // Set isOverflowing to false if user explicitely set hidden overflow + + this.axis.x.isOverflowing = this.elStyles.overflowX === 'hidden' ? false : this.axis.x.isOverflowing; + this.axis.y.isOverflowing = this.elStyles.overflowY === 'hidden' ? false : this.axis.y.isOverflowing; + this.axis.x.forceVisible = this.options.forceVisible === "x" || this.options.forceVisible === true; + this.axis.y.forceVisible = this.options.forceVisible === "y" || this.options.forceVisible === true; + this.axis.x.scrollbar.size = this.getScrollbarSize('x'); + this.axis.y.scrollbar.size = this.getScrollbarSize('y'); + this.axis.x.scrollbar.el.style.width = "".concat(this.axis.x.scrollbar.size, "px"); + this.axis.y.scrollbar.el.style.height = "".concat(this.axis.y.scrollbar.size, "px"); + this.positionScrollbar('x'); + this.positionScrollbar('y'); + this.toggleTrackVisibility('x'); + this.toggleTrackVisibility('y'); + this.hideNativeScrollbar(); + } + /** + * Calculate scrollbar size + */ + + }, { + key: "getScrollbarSize", + value: function getScrollbarSize() { + var axis = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'y'; + var contentSize = this.scrollbarWidth ? this.contentEl[this.axis[axis].scrollSizeAttr] : this.contentEl[this.axis[axis].scrollSizeAttr] - this.minScrollbarWidth; + var trackSize = this.axis[axis].track.rect[this.axis[axis].sizeAttr]; + var scrollbarSize; + + if (!this.axis[axis].isOverflowing) { + return; + } + + var scrollbarRatio = trackSize / contentSize; // Calculate new height/position of drag handle. + + scrollbarSize = Math.max(~~(scrollbarRatio * trackSize), this.options.scrollbarMinSize); + + if (this.options.scrollbarMaxSize) { + scrollbarSize = Math.min(scrollbarSize, this.options.scrollbarMaxSize); + } + + return scrollbarSize; + } + }, { + key: "positionScrollbar", + value: function positionScrollbar() { + var axis = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'y'; + var contentSize = this.contentEl[this.axis[axis].scrollSizeAttr]; + var trackSize = this.axis[axis].track.rect[this.axis[axis].sizeAttr]; + var hostSize = parseInt(this.elStyles[this.axis[axis].sizeAttr], 10); + var scrollbar = this.axis[axis].scrollbar; + var scrollOffset = this.contentEl[this.axis[axis].scrollOffsetAttr]; + scrollOffset = axis === 'x' && this.isRtl && SimpleBar.getRtlHelpers().isRtlScrollingInverted ? -scrollOffset : scrollOffset; + var scrollPourcent = scrollOffset / (contentSize - hostSize); + var handleOffset = ~~((trackSize - scrollbar.size) * scrollPourcent); + handleOffset = axis === 'x' && this.isRtl && SimpleBar.getRtlHelpers().isRtlScrollbarInverted ? handleOffset + (trackSize - scrollbar.size) : handleOffset; + scrollbar.el.style.transform = axis === 'x' ? "translate3d(".concat(handleOffset, "px, 0, 0)") : "translate3d(0, ".concat(handleOffset, "px, 0)"); + } + }, { + key: "toggleTrackVisibility", + value: function toggleTrackVisibility() { + var axis = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'y'; + var track = this.axis[axis].track.el; + var scrollbar = this.axis[axis].scrollbar.el; + + if (this.axis[axis].isOverflowing || this.axis[axis].forceVisible) { + track.style.visibility = 'visible'; + this.contentEl.style[this.axis[axis].overflowAttr] = 'scroll'; + } else { + track.style.visibility = 'hidden'; + this.contentEl.style[this.axis[axis].overflowAttr] = 'hidden'; + } // Even if forceVisible is enabled, scrollbar itself should be hidden + + + if (this.axis[axis].isOverflowing) { + scrollbar.style.visibility = 'visible'; + } else { + scrollbar.style.visibility = 'hidden'; + } + } + }, { + key: "hideNativeScrollbar", + value: function hideNativeScrollbar() { + this.offsetEl.style[this.isRtl ? 'left' : 'right'] = this.axis.y.isOverflowing || this.axis.y.forceVisible ? "-".concat(this.scrollbarWidth || this.minScrollbarWidth, "px") : 0; + this.offsetEl.style.bottom = this.axis.x.isOverflowing || this.axis.x.forceVisible ? "-".concat(this.scrollbarWidth || this.minScrollbarWidth, "px") : 0; // If floating scrollbar + + if (!this.scrollbarWidth) { + var paddingDirection = [this.isRtl ? 'paddingLeft' : 'paddingRight']; + this.contentEl.style[paddingDirection] = this.axis.y.isOverflowing || this.axis.y.forceVisible ? "calc(".concat(this.elStyles[paddingDirection], " + ").concat(this.minScrollbarWidth, "px)") : this.elStyles[paddingDirection]; + this.contentEl.style.paddingBottom = this.axis.x.isOverflowing || this.axis.x.forceVisible ? "calc(".concat(this.elStyles.paddingBottom, " + ").concat(this.minScrollbarWidth, "px)") : this.elStyles.paddingBottom; + } + } + /** + * On scroll event handling + */ + + }, { + key: "onMouseMoveForAxis", + value: function onMouseMoveForAxis() { + var axis = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'y'; + this.axis[axis].track.rect = this.axis[axis].track.el.getBoundingClientRect(); + this.axis[axis].scrollbar.rect = this.axis[axis].scrollbar.el.getBoundingClientRect(); + var isWithinScrollbarBoundsX = this.isWithinBounds(this.axis[axis].scrollbar.rect); + + if (isWithinScrollbarBoundsX) { + this.axis[axis].scrollbar.el.classList.add(this.classNames.hover); + } else { + this.axis[axis].scrollbar.el.classList.remove(this.classNames.hover); + } + + if (this.isWithinBounds(this.axis[axis].track.rect)) { + this.showScrollbar(axis); + this.axis[axis].track.el.classList.add(this.classNames.hover); + } else { + this.axis[axis].track.el.classList.remove(this.classNames.hover); + } + } + }, { + key: "onMouseLeaveForAxis", + value: function onMouseLeaveForAxis() { + var axis = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'y'; + this.axis[axis].track.el.classList.remove(this.classNames.hover); + this.axis[axis].scrollbar.el.classList.remove(this.classNames.hover); + } + }, { + key: "showScrollbar", + + /** + * Show scrollbar + */ + value: function showScrollbar() { + var axis = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'y'; + var scrollbar = this.axis[axis].scrollbar.el; + + if (!this.axis[axis].isVisible) { + scrollbar.classList.add(this.classNames.visible); + this.axis[axis].isVisible = true; + } + + if (this.options.autoHide) { + this.hideScrollbars(); + } + } + /** + * Hide Scrollbar + */ + + }, { + key: "onDragStart", + + /** + * on scrollbar handle drag movement starts + */ + value: function onDragStart(e) { + var axis = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'y'; + var scrollbar = this.axis[axis].scrollbar.el; // Measure how far the user's mouse is from the top of the scrollbar drag handle. + + var eventOffset = axis === 'y' ? e.pageY : e.pageX; + this.axis[axis].dragOffset = eventOffset - scrollbar.getBoundingClientRect()[this.axis[axis].offsetAttr]; + this.draggedAxis = axis; + document.addEventListener('mousemove', this.drag); + document.addEventListener('mouseup', this.onEndDrag); + } + /** + * Drag scrollbar handle + */ + + }, { + key: "getScrollElement", + + /** + * Getter for original scrolling element + */ + value: function getScrollElement() { + return this.contentEl; + } + }, { + key: "removeListeners", + value: function removeListeners() { + // Event listeners + if (this.options.autoHide) { + this.el.removeEventListener('mouseenter', this.onMouseEnter); + } + + this.contentEl.removeEventListener('scroll', this.onScroll); + window.removeEventListener('resize', this.onWindowResize); + this.mutationObserver && this.mutationObserver.disconnect(); + this.resizeObserver.disconnect(); + } + /** + * UnMount mutation observer and delete SimpleBar instance from DOM element + */ + + }, { + key: "unMount", + value: function unMount() { + this.removeListeners(); + this.el.SimpleBar = null; + } + /** + * Recursively walks up the parent nodes looking for this.el + */ + + }, { + key: "isChildNode", + value: function isChildNode(el) { + if (el === null) return false; + if (el === this.el) return true; + return this.isChildNode(el.parentNode); + } + /** + * Check if mouse is within bounds + */ + + }, { + key: "isWithinBounds", + value: function isWithinBounds(bbox) { + return this.mouseX >= bbox.left && this.mouseX <= bbox.left + bbox.width && this.mouseY >= bbox.top && this.mouseY <= bbox.top + bbox.height; + } + }], [{ + key: "getRtlHelpers", + value: function getRtlHelpers() { + var dummyDiv = document.createElement('div'); + dummyDiv.innerHTML = '
    '; + var scrollbarDummyEl = dummyDiv.firstElementChild; + document.body.appendChild(scrollbarDummyEl); + var dummyContainerChild = scrollbarDummyEl.firstElementChild; + scrollbarDummyEl.scrollLeft = 0; + var dummyContainerOffset = SimpleBar.getOffset(scrollbarDummyEl); + var dummyContainerChildOffset = SimpleBar.getOffset(dummyContainerChild); + scrollbarDummyEl.scrollLeft = 999; + var dummyContainerScrollOffsetAfterScroll = SimpleBar.getOffset(dummyContainerChild); + return { + // determines if the scrolling is responding with negative values + isRtlScrollingInverted: dummyContainerOffset.left !== dummyContainerChildOffset.left && dummyContainerChildOffset.left - dummyContainerScrollOffsetAfterScroll.left !== 0, + // determines if the origin scrollbar position is inverted or not (positioned on left or right) + isRtlScrollbarInverted: dummyContainerOffset.left !== dummyContainerChildOffset.left + }; + } + }, { + key: "initHtmlApi", + value: function initHtmlApi() { + this.initDOMLoadedElements = this.initDOMLoadedElements.bind(this); // MutationObserver is IE11+ + + if (typeof MutationObserver !== 'undefined') { + // Mutation observer to observe dynamically added elements + this.globalObserver = new MutationObserver(function (mutations) { + mutations.forEach(function (mutation) { + Array.from(mutation.addedNodes).forEach(function (addedNode) { + if (addedNode.nodeType === 1) { + if (addedNode.hasAttribute('data-simplebar')) { + !addedNode.SimpleBar && new SimpleBar(addedNode, SimpleBar.getElOptions(addedNode)); + } else { + Array.from(addedNode.querySelectorAll('[data-simplebar]')).forEach(function (el) { + !el.SimpleBar && new SimpleBar(el, SimpleBar.getElOptions(el)); + }); + } + } + }); + Array.from(mutation.removedNodes).forEach(function (removedNode) { + if (removedNode.nodeType === 1) { + if (removedNode.hasAttribute('data-simplebar')) { + removedNode.SimpleBar && removedNode.SimpleBar.unMount(); + } else { + Array.from(removedNode.querySelectorAll('[data-simplebar]')).forEach(function (el) { + el.SimpleBar && el.SimpleBar.unMount(); + }); + } + } + }); + }); + }); + this.globalObserver.observe(document, { + childList: true, + subtree: true + }); + } // Taken from jQuery `ready` function + // Instantiate elements already present on the page + + + if (document.readyState === 'complete' || document.readyState !== 'loading' && !document.documentElement.doScroll) { + // Handle it asynchronously to allow scripts the opportunity to delay init + window.setTimeout(this.initDOMLoadedElements); + } else { + document.addEventListener('DOMContentLoaded', this.initDOMLoadedElements); + window.addEventListener('load', this.initDOMLoadedElements); + } + } // Helper function to retrieve options from element attributes + + }, { + key: "getElOptions", + value: function getElOptions(el) { + var options = Array.from(el.attributes).reduce(function (acc, attribute) { + var option = attribute.name.match(/data-simplebar-(.+)/); + + if (option) { + var key = option[1].replace(/\W+(.)/g, function (x, chr) { + return chr.toUpperCase(); + }); + + switch (attribute.value) { + case 'true': + acc[key] = true; + break; + + case 'false': + acc[key] = false; + break; + + case undefined: + acc[key] = true; + break; + + default: + acc[key] = attribute.value; + } + } + + return acc; + }, {}); + return options; + } + }, { + key: "removeObserver", + value: function removeObserver() { + this.globalObserver.disconnect(); + } + }, { + key: "initDOMLoadedElements", + value: function initDOMLoadedElements() { + document.removeEventListener('DOMContentLoaded', this.initDOMLoadedElements); + window.removeEventListener('load', this.initDOMLoadedElements); + Array.from(document.querySelectorAll('[data-simplebar]')).forEach(function (el) { + if (!el.SimpleBar) new SimpleBar(el, SimpleBar.getElOptions(el)); + }); + } + }, { + key: "getOffset", + value: function getOffset(el) { + var rect = el.getBoundingClientRect(); + return { + top: rect.top + (window.pageYOffset || document.documentElement.scrollTop), + left: rect.left + (window.pageXOffset || document.documentElement.scrollLeft) + }; + } + }]); + + return SimpleBar; + }(); + /** + * HTML API + * Called only in a browser env. + */ + + + SimpleBar.defaultOptions = { + autoHide: true, + forceVisible: false, + classNames: { + content: 'simplebar-content', + offset: 'simplebar-offset', + mask: 'simplebar-mask', + wrapper: 'simplebar-wrapper', + placeholder: 'simplebar-placeholder', + scrollbar: 'simplebar-scrollbar', + track: 'simplebar-track', + heightAutoObserverWrapperEl: 'simplebar-height-auto-observer-wrapper', + heightAutoObserverEl: 'simplebar-height-auto-observer', + visible: 'simplebar-visible', + horizontal: 'simplebar-horizontal', + vertical: 'simplebar-vertical', + hover: 'simplebar-hover' + }, + scrollbarMinSize: 25, + scrollbarMaxSize: 0, + timeout: 1000 + }; + + if (canUseDom) { + SimpleBar.initHtmlApi(); + } + + return SimpleBar; + +}))); \ No newline at end of file diff --git a/muk_web_theme/static/src/fonts/Roboto-Black-webfont.eot b/muk_web_theme/static/src/fonts/Roboto-Black-webfont.eot new file mode 100644 index 0000000..fa326d1 Binary files /dev/null and b/muk_web_theme/static/src/fonts/Roboto-Black-webfont.eot differ diff --git a/muk_web_theme/static/src/fonts/Roboto-Black-webfont.svg b/muk_web_theme/static/src/fonts/Roboto-Black-webfont.svg new file mode 100644 index 0000000..945dec6 --- /dev/null +++ b/muk_web_theme/static/src/fonts/Roboto-Black-webfont.svg @@ -0,0 +1,642 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/muk_web_theme/static/src/fonts/Roboto-Black-webfont.ttf b/muk_web_theme/static/src/fonts/Roboto-Black-webfont.ttf new file mode 100644 index 0000000..3c3b2b8 Binary files /dev/null and b/muk_web_theme/static/src/fonts/Roboto-Black-webfont.ttf differ diff --git a/muk_web_theme/static/src/fonts/Roboto-Black-webfont.woff b/muk_web_theme/static/src/fonts/Roboto-Black-webfont.woff new file mode 100644 index 0000000..0229086 Binary files /dev/null and b/muk_web_theme/static/src/fonts/Roboto-Black-webfont.woff differ diff --git a/muk_web_theme/static/src/fonts/Roboto-BlackItalic-webfont.eot b/muk_web_theme/static/src/fonts/Roboto-BlackItalic-webfont.eot new file mode 100644 index 0000000..a2aebfb Binary files /dev/null and b/muk_web_theme/static/src/fonts/Roboto-BlackItalic-webfont.eot differ diff --git a/muk_web_theme/static/src/fonts/Roboto-BlackItalic-webfont.svg b/muk_web_theme/static/src/fonts/Roboto-BlackItalic-webfont.svg new file mode 100644 index 0000000..c9cc3cd --- /dev/null +++ b/muk_web_theme/static/src/fonts/Roboto-BlackItalic-webfont.svg @@ -0,0 +1,642 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/muk_web_theme/static/src/fonts/Roboto-BlackItalic-webfont.ttf b/muk_web_theme/static/src/fonts/Roboto-BlackItalic-webfont.ttf new file mode 100644 index 0000000..2020dcb Binary files /dev/null and b/muk_web_theme/static/src/fonts/Roboto-BlackItalic-webfont.ttf differ diff --git a/muk_web_theme/static/src/fonts/Roboto-BlackItalic-webfont.woff b/muk_web_theme/static/src/fonts/Roboto-BlackItalic-webfont.woff new file mode 100644 index 0000000..1875c0b Binary files /dev/null and b/muk_web_theme/static/src/fonts/Roboto-BlackItalic-webfont.woff differ diff --git a/muk_web_theme/static/src/fonts/Roboto-Bold-webfont.eot b/muk_web_theme/static/src/fonts/Roboto-Bold-webfont.eot new file mode 100644 index 0000000..b73776e Binary files /dev/null and b/muk_web_theme/static/src/fonts/Roboto-Bold-webfont.eot differ diff --git a/muk_web_theme/static/src/fonts/Roboto-Bold-webfont.svg b/muk_web_theme/static/src/fonts/Roboto-Bold-webfont.svg new file mode 100644 index 0000000..43b5ed2 --- /dev/null +++ b/muk_web_theme/static/src/fonts/Roboto-Bold-webfont.svg @@ -0,0 +1,593 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/muk_web_theme/static/src/fonts/Roboto-Bold-webfont.ttf b/muk_web_theme/static/src/fonts/Roboto-Bold-webfont.ttf new file mode 100644 index 0000000..1da7276 Binary files /dev/null and b/muk_web_theme/static/src/fonts/Roboto-Bold-webfont.ttf differ diff --git a/muk_web_theme/static/src/fonts/Roboto-Bold-webfont.woff b/muk_web_theme/static/src/fonts/Roboto-Bold-webfont.woff new file mode 100644 index 0000000..0c69948 Binary files /dev/null and b/muk_web_theme/static/src/fonts/Roboto-Bold-webfont.woff differ diff --git a/muk_web_theme/static/src/fonts/Roboto-BoldItalic-webfont.eot b/muk_web_theme/static/src/fonts/Roboto-BoldItalic-webfont.eot new file mode 100644 index 0000000..b803ec1 Binary files /dev/null and b/muk_web_theme/static/src/fonts/Roboto-BoldItalic-webfont.eot differ diff --git a/muk_web_theme/static/src/fonts/Roboto-BoldItalic-webfont.svg b/muk_web_theme/static/src/fonts/Roboto-BoldItalic-webfont.svg new file mode 100644 index 0000000..f877a3c --- /dev/null +++ b/muk_web_theme/static/src/fonts/Roboto-BoldItalic-webfont.svg @@ -0,0 +1,642 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/muk_web_theme/static/src/fonts/Roboto-BoldItalic-webfont.ttf b/muk_web_theme/static/src/fonts/Roboto-BoldItalic-webfont.ttf new file mode 100644 index 0000000..78bab05 Binary files /dev/null and b/muk_web_theme/static/src/fonts/Roboto-BoldItalic-webfont.ttf differ diff --git a/muk_web_theme/static/src/fonts/Roboto-BoldItalic-webfont.woff b/muk_web_theme/static/src/fonts/Roboto-BoldItalic-webfont.woff new file mode 100644 index 0000000..99de61a Binary files /dev/null and b/muk_web_theme/static/src/fonts/Roboto-BoldItalic-webfont.woff differ diff --git a/muk_web_theme/static/src/fonts/Roboto-Italic-webfont.eot b/muk_web_theme/static/src/fonts/Roboto-Italic-webfont.eot new file mode 100644 index 0000000..b708f04 Binary files /dev/null and b/muk_web_theme/static/src/fonts/Roboto-Italic-webfont.eot differ diff --git a/muk_web_theme/static/src/fonts/Roboto-Italic-webfont.svg b/muk_web_theme/static/src/fonts/Roboto-Italic-webfont.svg new file mode 100644 index 0000000..49ddd4a --- /dev/null +++ b/muk_web_theme/static/src/fonts/Roboto-Italic-webfont.svg @@ -0,0 +1,642 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/muk_web_theme/static/src/fonts/Roboto-Italic-webfont.ttf b/muk_web_theme/static/src/fonts/Roboto-Italic-webfont.ttf new file mode 100644 index 0000000..ae258e8 Binary files /dev/null and b/muk_web_theme/static/src/fonts/Roboto-Italic-webfont.ttf differ diff --git a/muk_web_theme/static/src/fonts/Roboto-Italic-webfont.woff b/muk_web_theme/static/src/fonts/Roboto-Italic-webfont.woff new file mode 100644 index 0000000..dd74244 Binary files /dev/null and b/muk_web_theme/static/src/fonts/Roboto-Italic-webfont.woff differ diff --git a/muk_web_theme/static/src/fonts/Roboto-Light-webfont.eot b/muk_web_theme/static/src/fonts/Roboto-Light-webfont.eot new file mode 100644 index 0000000..072cdc4 Binary files /dev/null and b/muk_web_theme/static/src/fonts/Roboto-Light-webfont.eot differ diff --git a/muk_web_theme/static/src/fonts/Roboto-Light-webfont.svg b/muk_web_theme/static/src/fonts/Roboto-Light-webfont.svg new file mode 100644 index 0000000..db6a617 --- /dev/null +++ b/muk_web_theme/static/src/fonts/Roboto-Light-webfont.svg @@ -0,0 +1,641 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/muk_web_theme/static/src/fonts/Roboto-Light-webfont.ttf b/muk_web_theme/static/src/fonts/Roboto-Light-webfont.ttf new file mode 100644 index 0000000..3b2fea0 Binary files /dev/null and b/muk_web_theme/static/src/fonts/Roboto-Light-webfont.ttf differ diff --git a/muk_web_theme/static/src/fonts/Roboto-Light-webfont.woff b/muk_web_theme/static/src/fonts/Roboto-Light-webfont.woff new file mode 100644 index 0000000..cc534a3 Binary files /dev/null and b/muk_web_theme/static/src/fonts/Roboto-Light-webfont.woff differ diff --git a/muk_web_theme/static/src/fonts/Roboto-LightItalic-webfont.eot b/muk_web_theme/static/src/fonts/Roboto-LightItalic-webfont.eot new file mode 100644 index 0000000..77396a1 Binary files /dev/null and b/muk_web_theme/static/src/fonts/Roboto-LightItalic-webfont.eot differ diff --git a/muk_web_theme/static/src/fonts/Roboto-LightItalic-webfont.svg b/muk_web_theme/static/src/fonts/Roboto-LightItalic-webfont.svg new file mode 100644 index 0000000..4bd14bc --- /dev/null +++ b/muk_web_theme/static/src/fonts/Roboto-LightItalic-webfont.svg @@ -0,0 +1,641 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/muk_web_theme/static/src/fonts/Roboto-LightItalic-webfont.ttf b/muk_web_theme/static/src/fonts/Roboto-LightItalic-webfont.ttf new file mode 100644 index 0000000..b9b3811 Binary files /dev/null and b/muk_web_theme/static/src/fonts/Roboto-LightItalic-webfont.ttf differ diff --git a/muk_web_theme/static/src/fonts/Roboto-LightItalic-webfont.woff b/muk_web_theme/static/src/fonts/Roboto-LightItalic-webfont.woff new file mode 100644 index 0000000..3071ff4 Binary files /dev/null and b/muk_web_theme/static/src/fonts/Roboto-LightItalic-webfont.woff differ diff --git a/muk_web_theme/static/src/fonts/Roboto-Medium-webfont.eot b/muk_web_theme/static/src/fonts/Roboto-Medium-webfont.eot new file mode 100644 index 0000000..f9ad995 Binary files /dev/null and b/muk_web_theme/static/src/fonts/Roboto-Medium-webfont.eot differ diff --git a/muk_web_theme/static/src/fonts/Roboto-Medium-webfont.svg b/muk_web_theme/static/src/fonts/Roboto-Medium-webfont.svg new file mode 100644 index 0000000..4ce289d --- /dev/null +++ b/muk_web_theme/static/src/fonts/Roboto-Medium-webfont.svg @@ -0,0 +1,593 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/muk_web_theme/static/src/fonts/Roboto-Medium-webfont.ttf b/muk_web_theme/static/src/fonts/Roboto-Medium-webfont.ttf new file mode 100644 index 0000000..8aa64d8 Binary files /dev/null and b/muk_web_theme/static/src/fonts/Roboto-Medium-webfont.ttf differ diff --git a/muk_web_theme/static/src/fonts/Roboto-Medium-webfont.woff b/muk_web_theme/static/src/fonts/Roboto-Medium-webfont.woff new file mode 100644 index 0000000..cd810ef Binary files /dev/null and b/muk_web_theme/static/src/fonts/Roboto-Medium-webfont.woff differ diff --git a/muk_web_theme/static/src/fonts/Roboto-MediumItalic-webfont.eot b/muk_web_theme/static/src/fonts/Roboto-MediumItalic-webfont.eot new file mode 100644 index 0000000..a03fe4b Binary files /dev/null and b/muk_web_theme/static/src/fonts/Roboto-MediumItalic-webfont.eot differ diff --git a/muk_web_theme/static/src/fonts/Roboto-MediumItalic-webfont.svg b/muk_web_theme/static/src/fonts/Roboto-MediumItalic-webfont.svg new file mode 100644 index 0000000..904d7c5 --- /dev/null +++ b/muk_web_theme/static/src/fonts/Roboto-MediumItalic-webfont.svg @@ -0,0 +1,642 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/muk_web_theme/static/src/fonts/Roboto-MediumItalic-webfont.ttf b/muk_web_theme/static/src/fonts/Roboto-MediumItalic-webfont.ttf new file mode 100644 index 0000000..6439927 Binary files /dev/null and b/muk_web_theme/static/src/fonts/Roboto-MediumItalic-webfont.ttf differ diff --git a/muk_web_theme/static/src/fonts/Roboto-MediumItalic-webfont.woff b/muk_web_theme/static/src/fonts/Roboto-MediumItalic-webfont.woff new file mode 100644 index 0000000..69a1458 Binary files /dev/null and b/muk_web_theme/static/src/fonts/Roboto-MediumItalic-webfont.woff differ diff --git a/muk_web_theme/static/src/fonts/Roboto-Regular-webfont.eot b/muk_web_theme/static/src/fonts/Roboto-Regular-webfont.eot new file mode 100644 index 0000000..9b5e8e4 Binary files /dev/null and b/muk_web_theme/static/src/fonts/Roboto-Regular-webfont.eot differ diff --git a/muk_web_theme/static/src/fonts/Roboto-Regular-webfont.svg b/muk_web_theme/static/src/fonts/Roboto-Regular-webfont.svg new file mode 100644 index 0000000..de7d77f --- /dev/null +++ b/muk_web_theme/static/src/fonts/Roboto-Regular-webfont.svg @@ -0,0 +1,621 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/muk_web_theme/static/src/fonts/Roboto-Regular-webfont.ttf b/muk_web_theme/static/src/fonts/Roboto-Regular-webfont.ttf new file mode 100644 index 0000000..44dd78d Binary files /dev/null and b/muk_web_theme/static/src/fonts/Roboto-Regular-webfont.ttf differ diff --git a/muk_web_theme/static/src/fonts/Roboto-Regular-webfont.woff b/muk_web_theme/static/src/fonts/Roboto-Regular-webfont.woff new file mode 100644 index 0000000..bfa05d5 Binary files /dev/null and b/muk_web_theme/static/src/fonts/Roboto-Regular-webfont.woff differ diff --git a/muk_web_theme/static/src/fonts/Roboto-Thin-webfont.eot b/muk_web_theme/static/src/fonts/Roboto-Thin-webfont.eot new file mode 100644 index 0000000..2284a3b Binary files /dev/null and b/muk_web_theme/static/src/fonts/Roboto-Thin-webfont.eot differ diff --git a/muk_web_theme/static/src/fonts/Roboto-Thin-webfont.svg b/muk_web_theme/static/src/fonts/Roboto-Thin-webfont.svg new file mode 100644 index 0000000..7394e3d --- /dev/null +++ b/muk_web_theme/static/src/fonts/Roboto-Thin-webfont.svg @@ -0,0 +1,631 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/muk_web_theme/static/src/fonts/Roboto-Thin-webfont.ttf b/muk_web_theme/static/src/fonts/Roboto-Thin-webfont.ttf new file mode 100644 index 0000000..18919f7 Binary files /dev/null and b/muk_web_theme/static/src/fonts/Roboto-Thin-webfont.ttf differ diff --git a/muk_web_theme/static/src/fonts/Roboto-Thin-webfont.woff b/muk_web_theme/static/src/fonts/Roboto-Thin-webfont.woff new file mode 100644 index 0000000..f10b831 Binary files /dev/null and b/muk_web_theme/static/src/fonts/Roboto-Thin-webfont.woff differ diff --git a/muk_web_theme/static/src/fonts/Roboto-ThinItalic-webfont.eot b/muk_web_theme/static/src/fonts/Roboto-ThinItalic-webfont.eot new file mode 100644 index 0000000..e6291f2 Binary files /dev/null and b/muk_web_theme/static/src/fonts/Roboto-ThinItalic-webfont.eot differ diff --git a/muk_web_theme/static/src/fonts/Roboto-ThinItalic-webfont.svg b/muk_web_theme/static/src/fonts/Roboto-ThinItalic-webfont.svg new file mode 100644 index 0000000..951cccd --- /dev/null +++ b/muk_web_theme/static/src/fonts/Roboto-ThinItalic-webfont.svg @@ -0,0 +1,631 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/muk_web_theme/static/src/fonts/Roboto-ThinItalic-webfont.ttf b/muk_web_theme/static/src/fonts/Roboto-ThinItalic-webfont.ttf new file mode 100644 index 0000000..a4e7ae0 Binary files /dev/null and b/muk_web_theme/static/src/fonts/Roboto-ThinItalic-webfont.ttf differ diff --git a/muk_web_theme/static/src/fonts/Roboto-ThinItalic-webfont.woff b/muk_web_theme/static/src/fonts/Roboto-ThinItalic-webfont.woff new file mode 100644 index 0000000..9ef17a8 Binary files /dev/null and b/muk_web_theme/static/src/fonts/Roboto-ThinItalic-webfont.woff differ diff --git a/muk_web_theme/static/src/img/background.png b/muk_web_theme/static/src/img/background.png new file mode 100644 index 0000000..b7788b9 Binary files /dev/null and b/muk_web_theme/static/src/img/background.png differ diff --git a/muk_web_theme/static/src/js/chrome/apps.js b/muk_web_theme/static/src/js/chrome/apps.js new file mode 100644 index 0000000..0b39ebe --- /dev/null +++ b/muk_web_theme/static/src/js/chrome/apps.js @@ -0,0 +1,182 @@ +/********************************************************************************** +* +* Copyright (C) 2017 MuK IT GmbH +* +* 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 . +* +**********************************************************************************/ + +odoo.define('muk_web_theme.AppsMenu', function (require) { +"use strict"; + +var core = require('web.core'); +var config = require("web.config"); +var session = require("web.session"); + +var AppsMenu = require("web.AppsMenu"); + +var _t = core._t; +var QWeb = core.qweb; + +AppsMenu.include({ + events: _.extend({}, AppsMenu.prototype.events, { + "keydown .mk_search_input input": "_onSearchResultsNavigate", + "click .mk_menu_search_result": "_onSearchResultChosen", + "shown.bs.dropdown": "_onMenuShow", + "hidden.bs.dropdown": "_onMenuHide", + }), + init: function (parent, menuData) { + this._super.apply(this, arguments); + for (var n in this._apps) { + this._apps[n].web_icon_data = menuData.children[n].web_icon_data; + } + this._searchableMenus = _.reduce( + menuData.children, this._findNames.bind(this), {} + ); + this._search_def = $.Deferred(); + }, + start: function () { + this._setBackgroundImage(); + this.$search_container = this.$(".mk_search_container"); + this.$search_input = this.$(".mk_search_input input"); + this.$search_results = this.$(".mk_search_results"); + return this._super.apply(this, arguments); + }, + _findNames: function (memo, menu) { + if (menu.action) { + var key = menu.parent_id ? menu.parent_id[1] + "/" : ""; + memo[key + menu.name] = menu; + } + if (menu.children.length) { + _.reduce(menu.children, this._findNames.bind(this), memo); + } + return memo; + }, + _setBackgroundImage: function () { + var url = session.url('/web/image', { + model: 'res.company', + id: session.company_id, + field: 'background_image', + }); + this.$('.dropdown-menu').css({ + "background-size": "cover", + "background-image": "url(" + url + ")" + }); + }, + _menuInfo: function (key) { + var original = this._searchableMenus[key]; + return _.extend({ + action_id: parseInt(original.action.split(',')[1], 10), + }, original); + }, + _onMenuShow: function(event) { + this._searchFocus(); + }, + _onMenuHide: function(event) { + this._searchReset(); + + }, + _searchFocus: function () { + if (!config.device.isMobile) { + this.$search_input.focus(); + } + }, + _searchReset: function () { + this.$search_container.removeClass("has-results"); + this.$search_results.empty(); + this.$search_input.val(""); + }, + _searchMenusSchedule: function () { + this._search_def.reject(); + this._search_def = $.Deferred(); + setTimeout(this._search_def.resolve.bind(this._search_def), 50); + this._search_def.done(this._searchMenus.bind(this)); + }, + _searchMenus: function () { + var query = this.$search_input.val(); + if (query === "") { + this.$search_container.removeClass("has-results"); + this.$search_results.empty(); + return; + } + var results = fuzzy.filter(query, _.keys(this._searchableMenus), { + pre: "", + post: "", + }); + this.$search_container.toggleClass("has-results", Boolean(results.length)); + this.$search_results.html(QWeb.render("muk_web_theme.MenuSearchResults", { + results: results, + widget: this, + })); + }, + _onSearchResultChosen: function (event) { + event.preventDefault(); + var $result = $(event.currentTarget), + text = $result.text().trim(), + data = $result.data(), + suffix = ~text.indexOf("/") ? "/" : ""; + this.trigger_up("menu_clicked", { + action_id: data.actionId, + id: data.menuId, + previous_menu_id: data.parentId, + }); + var app = _.find(this._apps, function (_app) { + return text.indexOf(_app.name + suffix) === 0; + }); + core.bus.trigger("change_menu_section", app.menuID); + }, + _onSearchResultsNavigate: function (event) { + if (this.$search_results.is(":empty")) { + this._searchMenusSchedule(); + return; + } + var all = this.$search_results.find(".mk_menu_search_result"), + pre_focused = all.filter(".active") || $(all[0]), + offset = all.index(pre_focused), + key = event.key; + if (key === "Tab") { + event.preventDefault(); + key = event.shiftKey ? "ArrowUp" : "ArrowDown"; + } + switch (key) { + case "Enter": + pre_focused.click(); + break; + case "ArrowUp": + offset--; + break; + case "ArrowDown": + offset++; + break; + default: + this._searchMenusSchedule(); + return; + } + if (offset < 0) { + offset = all.length + offset; + } else if (offset >= all.length) { + offset -= all.length; + } + var new_focused = $(all[offset]); + pre_focused.removeClass("active"); + new_focused.addClass("active"); + this.$search_results.scrollTo(new_focused, { + offset: { + top: this.$search_results.height() * -0.5, + }, + }); + }, +}); + +}); \ No newline at end of file diff --git a/muk_web_theme/static/src/js/chrome/appsbar.js b/muk_web_theme/static/src/js/chrome/appsbar.js new file mode 100644 index 0000000..17209d0 --- /dev/null +++ b/muk_web_theme/static/src/js/chrome/appsbar.js @@ -0,0 +1,68 @@ +/********************************************************************************** +* +* Copyright (C) 2017 MuK IT GmbH +* +* 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 . +* +**********************************************************************************/ + +odoo.define('muk_web_theme.AppsBar', function (require) { +"use strict"; + +var core = require('web.core'); +var config = require("web.config"); + +var Widget = require('web.Widget'); + +var _t = core._t; +var QWeb = core.qweb; + +var AppsBar = Widget.extend({ + events: _.extend({}, Widget.prototype.events, { + 'click .nav-link': '_onAppsMenuItemClicked', + }), + template: "muk_web_theme.AppsBarMenu", + init: function (parent, menu) { + this._super.apply(this, arguments); + this._apps = _.map(menu.children, function (app) { + return { + actionID: parseInt(app.action.split(',')[1]), + web_icon_data: app.web_icon_data, + menuID: app.id, + name: app.name, + xmlID: app.xmlid, + }; + }); + }, + getApps: function () { + return this._apps; + }, + _openApp: function (app) { + this.trigger_up('app_clicked', { + action_id: app.actionID, + menu_id: app.menuID, + }); + }, + _onAppsMenuItemClicked: function (ev) { + var $target = $(ev.currentTarget); + var actionID = $target.data('action-id'); + var menuID = $target.data('menu-id'); + var app = _.findWhere(this._apps, { actionID: actionID, menuID: menuID }); + this._openApp(app); + }, +}); + +return AppsBar; + +}); \ No newline at end of file diff --git a/muk_web_theme/static/src/js/chrome/menu.js b/muk_web_theme/static/src/js/chrome/menu.js new file mode 100644 index 0000000..0ad5f5a --- /dev/null +++ b/muk_web_theme/static/src/js/chrome/menu.js @@ -0,0 +1,76 @@ +/********************************************************************************** +* +* Copyright (C) 2017 MuK IT GmbH +* +* 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 . +* +**********************************************************************************/ + +odoo.define('muk_web_theme.Menu', function (require) { +"use strict"; + +var core = require('web.core'); +var config = require("web.config"); + +var Menu = require("web.Menu"); +var AppsBar = require("muk_web_theme.AppsBar"); + +var _t = core._t; +var QWeb = core.qweb; + +Menu.include({ + events: _.extend({}, Menu.prototype.events, { + "click .mk_menu_mobile_section": "_onMobileSectionClick", + "click .o_menu_sections [role=menuitem]": "_hideMobileSubmenus", + "show.bs.dropdown .o_menu_systray, .o_menu_apps": "_hideMobileSubmenus", + }), + menusTemplate: config.device.isMobile ? + 'muk_web_theme.MobileMenu.sections' : Menu.prototype.menusTemplate, + start: function () { + this.$menu_toggle = this.$(".mk_menu_sections_toggle"); + this.$menu_apps_sidebar = this.$('.mk_apps_sidebar_panel'); + this._appsBar = new AppsBar(this, this.menu_data); + this._appsBar.appendTo(this.$menu_apps_sidebar); + new SimpleBar(this.$menu_apps_sidebar[0]); + return this._super.apply(this, arguments); + }, + _hideMobileSubmenus: function () { + if (this.$menu_toggle.is(":visible") && this.$section_placeholder.is(":visible")) { + this.$section_placeholder.collapse("hide"); + } + }, + _updateMenuBrand: function () { + if (!config.device.isMobile) { + return this._super.apply(this, arguments); + } + }, + _onMobileSectionClick: function (event) { + event.preventDefault(); + event.stopPropagation(); + var $section = $(event.currentTarget); + if ($section.hasClass('show')) { + $section.removeClass('show'); + $section.find('.show').removeClass('show'); + $section.find('.fa-chevron-down').show(); + $section.find('.fa-chevron-right').hide(); + } else { + $section.addClass('show'); + $section.find('ul:first').addClass('show'); + $section.find('.fa-chevron-down:first').hide(); + $section.find('.fa-chevron-right:first').show(); + } + }, +}); + +}); \ No newline at end of file diff --git a/muk_web_theme/static/src/js/chrome/sidebar.js b/muk_web_theme/static/src/js/chrome/sidebar.js new file mode 100644 index 0000000..12e52e3 --- /dev/null +++ b/muk_web_theme/static/src/js/chrome/sidebar.js @@ -0,0 +1,47 @@ +/********************************************************************************** +* +* Copyright (C) 2017 MuK IT GmbH +* +* 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 . +* +**********************************************************************************/ + +odoo.define('muk_web_theme.Sidebar', function (require) { +"use strict"; + +var core = require('web.core'); +var config = require("web.config"); + +var Sidebar = require('web.Sidebar'); + +var _t = core._t; +var QWeb = core.qweb; + +Sidebar.include({ + init: function () { + this._super.apply(this, arguments); + if (config.device.isMobile) { + _.each(this.sections, function(element) { + if(element.name === 'print') { + element.icon = 'fa fa-print'; + } + if(element.name === 'other') { + element.icon = 'fa fa-magic'; + } + }); + } + }, +}); + +}); \ No newline at end of file diff --git a/muk_web_theme/static/src/js/fields/status.js b/muk_web_theme/static/src/js/fields/status.js new file mode 100644 index 0000000..ca241a7 --- /dev/null +++ b/muk_web_theme/static/src/js/fields/status.js @@ -0,0 +1,41 @@ +/********************************************************************************** +* +* Copyright (C) 2017 MuK IT GmbH +* +* 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 . +* +**********************************************************************************/ + +odoo.define('muk_web_theme.FieldStatus', function (require) { +"use strict"; + +var core = require('web.core'); +var config = require("web.config"); +var fields = require('web.relational_fields'); + +var _t = core._t; +var QWeb = core.qweb; + +fields.FieldStatus.include({ + _setState: function () { + this._super.apply(this, arguments); + if (config.device.isMobile) { + _.map(this.status_information, function (value) { + value.fold = true; + }); + } + }, +}); + +}); \ No newline at end of file diff --git a/muk_web_theme/static/src/js/views/form_renderer.js b/muk_web_theme/static/src/js/views/form_renderer.js new file mode 100644 index 0000000..fb9cd14 --- /dev/null +++ b/muk_web_theme/static/src/js/views/form_renderer.js @@ -0,0 +1,49 @@ +/********************************************************************************** +* +* Copyright (C) 2017 MuK IT GmbH +* +* 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 . +* +**********************************************************************************/ + +odoo.define('muk_web_theme.FormRenderer', function (require) { +"use strict"; + +var dom = require('web.dom'); +var core = require('web.core'); +var config = require("web.config"); + +var FormRenderer = require('web.FormRenderer'); + +var _t = core._t; +var QWeb = core.qweb; + +FormRenderer.include({ + _renderHeaderButtons: function () { + var $buttons = this._super.apply(this, arguments); + if (config.device.isMobile) { + var $dropdown = $(QWeb.render('muk_web_theme.MenuStatusbarButtons')); + $buttons.addClass("dropdown-menu").appendTo($dropdown); + $buttons.addClass("dropdown-menu"); + return $dropdown; + } + return $buttons; + }, + _updateView: function () { + this._super.apply(this, arguments); + this.$('.nav-tabs').scrollingTabs(); + }, +}); + +}); \ No newline at end of file diff --git a/muk_web_theme/static/src/js/views/search_view.js b/muk_web_theme/static/src/js/views/search_view.js new file mode 100644 index 0000000..81a57a7 --- /dev/null +++ b/muk_web_theme/static/src/js/views/search_view.js @@ -0,0 +1,40 @@ +/********************************************************************************** +* +* Copyright (C) 2017 MuK IT GmbH +* +* 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 . +* +**********************************************************************************/ + +odoo.define('muk_web_theme.SearchView', function (require) { +"use strict"; + +var core = require('web.core'); +var config = require("web.config"); + +var SearchView = require('web.SearchView'); + +var _t = core._t; +var QWeb = core.qweb; + +SearchView.include({ + start: function () { + if (config.device.isMobile) { + this.$('.o_enable_searchview').text(_t("Search")); + } + return this._super.apply(this, arguments); + }, +}); + +}); \ No newline at end of file diff --git a/muk_web_theme/static/src/scss/apps.scss b/muk_web_theme/static/src/scss/apps.scss new file mode 100644 index 0000000..b93d627 --- /dev/null +++ b/muk_web_theme/static/src/scss/apps.scss @@ -0,0 +1,114 @@ +/********************************************************************************** +* +* Copyright (C) 2017 MuK IT GmbH +* +* 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 . +* +**********************************************************************************/ + +.o_menu_apps { + .full { + width: 46px; + font-size: 18px; + text-align: center; + } + .dropdown-menu.show { + @include mk-full-screen-sections(); + align-content: flex-start; + overflow: inital; + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: flex-start; + @include media-breakpoint-up(lg) { + padding: { + left: 20vw; + right: 20vw; + } + } + .o_app { + display: flex; + margin-top: 20px; + align-items: center; + flex-direction: column; + justify-content: flex-start; + width: percentage(1/3); + @include media-breakpoint-up(sm) { + width: percentage(1/4); + } + @include media-breakpoint-up(md) { + width: percentage(1/6); + } + &:hover, &:active, &.active { + background: none; + .o-app-icon { + box-shadow: 0 8px 15px -10px black; + transform: translateY(-1px); + } + } + } + .has-results ~ .o_app { + display: none; + } + .o-app-icon { + height: auto; + max-width: 7rem; + width: 100%; + } + .o-app-name { + color: $gray-100; + } + .form-row { + width: 100%; + } + .mk_search_container { + margin-top: 25px; + .mk_search_input { + input:focus { + box-shadow: none; + border: 1px solid $gray-400; + } + } + &.has-results { + height: 100%; + .mk_search_input { + height: 3em; + } + .mk_search_results { + height: calc(100% - 3em); + .mk_menu_search_result { + display: block; + cursor: pointer; + align-items: center; + padding-left: 3rem; + padding-right: 1rem; + background-position: left; + background-repeat: no-repeat; + background-size: contain; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + color: $gray-100; + &:hover, &:active, &.active { + background-color: rgba(255, 255, 255, 0.05); + } + } + } + } + @include media-breakpoint-down(md) { + padding-right: 5px; + } + } + } +} \ No newline at end of file diff --git a/muk_web_theme/static/src/scss/appsbar.scss b/muk_web_theme/static/src/scss/appsbar.scss new file mode 100644 index 0000000..b1d32ce --- /dev/null +++ b/muk_web_theme/static/src/scss/appsbar.scss @@ -0,0 +1,111 @@ +/********************************************************************************** +* +* Copyright (C) 2017 MuK IT GmbH +* +* 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 . +* +**********************************************************************************/ + +.mk_apps_sidebar_panel { + height: 100%; + position: fixed; + overflow-y: auto; + top: $o-navbar-height; + background-color: $black; + .mk_apps_sidebar { + padding: 0; + white-space: nowrap; + .mk_apps_sidebar_menu { + list-style: none; + margin: 0; + padding: 0; + > li { + margin: 0; + padding: 0; + border: 0px; + display: block; + > a { + margin: 0; + border: 0px; + display: block; + cursor: pointer; + font-size: 13px; + padding: 8px 11px; + position: relative; + text-decoration: none; + font-weight: 300; + color: gray('300'); + overflow: hidden; + text-overflow: ellipsis; + .mk_apps_sidebar_icon { + width: 22px; + height: 22px; + margin-right: 5px; + } + } + } + > li:hover > a { + background: $o-brand-primary; + } + } + } +} + +@include media-breakpoint-up(lg) { + .mk_sidebar_type_large { + .mk_apps_sidebar_panel { + width: $mk-sidebar-large-width; + } + .o_main { + padding-left: $mk-sidebar-large-width; + } + } + .mk_sidebar_type_small { + .mk_apps_sidebar_panel { + width: $mk-sidebar-small-width; + .mk_apps_sidebar_name { + display: none; + } + } + .o_main { + padding-left: $mk-sidebar-small-width; + } + } + .mk_sidebar_type_invisible { + .mk_apps_sidebar_panel { + display: none; + } + } +} + +@include media-breakpoint-down(md) { + .mk_apps_sidebar_panel { + width: $mk-sidebar-small-width; + .mk_apps_sidebar_name { + display: none; + } + } + .o_main { + padding-left: $mk-sidebar-small-width; + } +} + +@include media-breakpoint-down(sm) { + .mk_apps_sidebar_panel { + display: none; + } + .o_main { + padding-left: 0; + } +} \ No newline at end of file diff --git a/muk_web_theme/static/src/scss/colors.scss b/muk_web_theme/static/src/scss/colors.scss new file mode 100644 index 0000000..633d277 --- /dev/null +++ b/muk_web_theme/static/src/scss/colors.scss @@ -0,0 +1,29 @@ +/********************************************************************************** +* +* Copyright (C) 2017 MuK IT GmbH +* +* 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 . +* +**********************************************************************************/ + +//---------------------------------------------------------- +// Colors +//---------------------------------------------------------- + +$o-brand-odoo: #243742; +$o-brand-primary: #5D8DA8; + +$mk-brand-gradient-start: lighten($o-brand-odoo, 10%); +$mk-brand-gradient-end: lighten($o-brand-odoo, 20%); + diff --git a/muk_web_theme/static/src/scss/control_panel.scss b/muk_web_theme/static/src/scss/control_panel.scss new file mode 100644 index 0000000..ca6623d --- /dev/null +++ b/muk_web_theme/static/src/scss/control_panel.scss @@ -0,0 +1,76 @@ +/********************************************************************************** +* +* Copyright (C) 2017 MuK IT GmbH +* +* 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 . +* +**********************************************************************************/ + +@include media-breakpoint-down(sm) { + .o_control_panel { + .breadcrumb, .o_cp_buttons, .o_cp_left, .o_cp_right, .o_cp_searchview { + flex: 1 1 100%; + @include media-breakpoint-up(md) { + flex-basis: 50%; + } + } + .breadcrumb { + max-width: 80%; + overflow: hidden; + display: block; + white-space: nowrap; + text-overflow: ellipsis; + } + .o_cp_searchview { + overflow: hidden; + max-width: 20%; + min-width: 20%; + text-align: right; + .o_searchview { + border: none; + padding: 3.5px 5px; + .o_enable_searchview { + margin-left: 0; + box-shadow: none; + &:before { + padding-right: 5px; + } + } + } + } + .o_cp_left, .o_cp_right { + width: auto; + flex: 1 1 auto; + } + .o_cp_buttons .btn.d-block:not(.d-none) { + display: inline-block !important; + } + .o_cp_switch_buttons.show { + .dropdown-menu { + align-content: center; + display: flex; + flex-direction: row; + justify-content: space-around; + padding: 0; + .btn { + border: { + bottom: 0; + radius: 0; + top: 0; + } + } + } + } + } +} \ No newline at end of file diff --git a/muk_web_theme/static/src/scss/fields.scss b/muk_web_theme/static/src/scss/fields.scss new file mode 100644 index 0000000..7162d7f --- /dev/null +++ b/muk_web_theme/static/src/scss/fields.scss @@ -0,0 +1,30 @@ +/********************************************************************************** +* +* Copyright (C) 2017 MuK IT GmbH +* +* 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 . +* +**********************************************************************************/ + +.o_field_widget { + &.o_field_many2one .o_external_button { + color: $o-brand-odoo + } +} + +.o_required_modifier { + &.o_input, .o_input { + background-color: lighten($o-brand-primary, 35%) ! important; + } +} \ No newline at end of file diff --git a/muk_web_theme/static/src/scss/fonts.scss b/muk_web_theme/static/src/scss/fonts.scss new file mode 100644 index 0000000..2d05aee --- /dev/null +++ b/muk_web_theme/static/src/scss/fonts.scss @@ -0,0 +1,47 @@ +/********************************************************************************** +* +* Copyright (C) 2017 MuK IT GmbH +* +* 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 . +* +**********************************************************************************/ + +$font-path: '../fonts'; + +@mixin mk-font-face($name, $weight, $style) { + @font-face { + font-family: 'Roboto'; + src: url('#{$font-path}/Roboto-#{$name}-webfont.eot'); + src: local('Roboto #{$name}'), local('Roboto-#{$name}'), + url('#{$font-path}/Roboto-#{$name}-webfont.eot?#iefix') format('embedded-opentype'), + url('#{$font-path}/Roboto-#{$name}-webfont.woff') format('woff'), + url('#{$font-path}/Roboto-#{$name}-webfont.ttf') format('truetype'), + url('#{$font-path}/Roboto-#{$name}-webfont.svg#Roboto') format('svg'); + font-weight: $weight; + font-style: $style; + } +} + +@include mk-font-face('Thin', 100, normal); +@include mk-font-face('ThinItalic', 100, italic); +@include mk-font-face('Light', 300, normal); +@include mk-font-face('LightItalic', 300, italic); +@include mk-font-face('Regular', 400, normal); +@include mk-font-face('Italic', 400, italic); +@include mk-font-face('Medium', 500, normal); +@include mk-font-face('MediumItalic', 500, italic); +@include mk-font-face('Bold', 700, normal); +@include mk-font-face('BoldItalic', 700, italic); +@include mk-font-face('Black', 900, normal); +@include mk-font-face('BlackItalic', 900, italic); diff --git a/muk_web_theme/static/src/scss/form_view.scss b/muk_web_theme/static/src/scss/form_view.scss new file mode 100644 index 0000000..edf2e3a --- /dev/null +++ b/muk_web_theme/static/src/scss/form_view.scss @@ -0,0 +1,123 @@ +/********************************************************************************** +* +* Copyright (C) 2017 MuK IT GmbH +* +* 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 . +* +**********************************************************************************/ + +.o_form_view { + .o_form_sheet_bg { + background: $o-webclient-background-color; + border-bottom: 1px solid gray('300'); + > .o_form_sheet { + background-color: $o-view-background-color; + border: 1px solid gray('400'); + box-shadow: 0 5px 20px -15px black; + max-width: calc(100% - #{$o-horizontal-padding*2}); + } + .oe_button_box { + .btn.oe_stat_button > .o_button_icon { + color: $o-brand-odoo; + } + } + .o_notebook { + .table-responsive { + overflow-x: inherit; + } + } + } + @include media-breakpoint-down(sm) { + .o_form_sheet, .oe_chatter { + min-width: 100% !important; + max-width: 100% !important; + } + .o_group_col_6 { + width: 100%; + } + .o_statusbar_buttons_dropdown { + border: { + bottom: 0; + radius: 0; + top: 0; + } + height: 100%; + } + .o_statusbar_buttons > .btn { + border-radius: 0; + border: 0; + width: 100%; + margin-bottom: 0.2rem; + &:last-child { + margin-bottom: 0; + } + } + .o_statusbar_status { + .o_arrow_button:first-child::before { + content: none; + display: none; + } + } + .app_settings_block { + .row { + margin: 0; + } + } + } + @include media-breakpoint-down(xs) { + .oe_button_box { + float: none; + display: block; + text-align: right; + margin-top: -17px; + margin-bottom: $o-sheet-vpadding; + margin-left: -$o-horizontal-padding; + width: calc(100% + #{$o-horizontal-padding*2}); + > .btn.oe_stat_button { + flex: 0 0 auto !important; + width: percentage(1/3) !important; + } + > .btn.o_button_more, > .o_dropdown_more { + flex: 0 0 auto !important; + width: calc(#{percentage(1/3)} + 3px) !important; + } + &, & + .oe_avatar { + + .oe_title { + width: 100%; + } + } + } + } +} + +.mk_chatter_position_sided { + @include media-breakpoint-up(xl) { + .o_form_view:not(.o_form_nosheet) { + display: flex; + flex-flow: row nowrap; + height: 100%; + .o_form_sheet_bg { + flex: 1 1 65%; + overflow: auto; + } + .o_chatter { + border-left: 1px solid gray('400'); + flex: 1 1 35%; + max-width: 40%; + min-width: 30%; + overflow: auto; + } + } + } +} \ No newline at end of file diff --git a/muk_web_theme/static/src/scss/layout.scss b/muk_web_theme/static/src/scss/layout.scss new file mode 100644 index 0000000..b1e6a2b --- /dev/null +++ b/muk_web_theme/static/src/scss/layout.scss @@ -0,0 +1,58 @@ +/********************************************************************************** +* +* Copyright (C) 2017 MuK IT GmbH +* +* 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 . +* +**********************************************************************************/ + +//---------------------------------------------------------- +// Helper +//---------------------------------------------------------- + +$mk-brand-gradient: linear-gradient(to right bottom, $mk-brand-gradient-start, $mk-brand-gradient-end); + +//---------------------------------------------------------- +// Web Client +//---------------------------------------------------------- + +.o_web_client { + .o_main .o_main_content { + @include media-breakpoint-down(sm) { + overflow: auto; + .o_content { + overflow: initial; + } + } + } + .o_loading { + position: fixed; + right: 0; + bottom: 0; + color: white; + padding: 5px; + opacity: 0.8; + z-index: 1052; + border-radius: 0; + background-color: $o-brand-odoo; + } +} + +//---------------------------------------------------------- +// Views +//---------------------------------------------------------- + +.o_content, .modal-content { + max-width: 100%; +} diff --git a/muk_web_theme/static/src/scss/list_view.scss b/muk_web_theme/static/src/scss/list_view.scss new file mode 100644 index 0000000..2b7b1b9 --- /dev/null +++ b/muk_web_theme/static/src/scss/list_view.scss @@ -0,0 +1,64 @@ +/********************************************************************************** +* +* Copyright (C) 2017 MuK IT GmbH +* +* 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 . +* +**********************************************************************************/ + +.o_list_view.table, +.o_list_view.table thead, +.o_list_view.table tfoot, +.o_list_view.table td, +.o_list_view.table th { + border: none; +} + +.o_list_view.table thead { + background-color: white; + th.o_column_sortable:hover { + background-color: white; + } +} + +.table-striped > tbody > tr { + background-color: white; + &:nth-of-type(2n+1) { + background-color: gray('200'); + } + &:hover { + background-color: gray('300'); + } +} + +.o_list_view_grouped.table tbody tr { + &:nth-of-type(2n+1) { + background-color: white; + } + &.o_group_header { + background-image: none; + background-color: gray('300'); + box-shadow: inset 0 1px 0 gray('400'); + } + &:hover { + background-color: gray('300'); + } +} + +.o_list_view tfoot { + background-color: $o-brand-odoo; + color: white; + cursor: default; + font-weight: normal; +} diff --git a/muk_web_theme/static/src/scss/mixins.scss b/muk_web_theme/static/src/scss/mixins.scss new file mode 100644 index 0000000..7e5e81c --- /dev/null +++ b/muk_web_theme/static/src/scss/mixins.scss @@ -0,0 +1,32 @@ +/********************************************************************************** +* +* Copyright (C) 2017 MuK IT GmbH +* +* 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 . +* +**********************************************************************************/ + +@mixin mk-full-screen-sections { + border: none; + box-shadow: none; + display: flex; + flex-direction: column; + height: calc(100vh - #{$o-navbar-height}); + max-height: calc(100vh - #{$o-navbar-height}); + position: fixed; + width: 100vw; + z-index: 100; + top: $o-navbar-height !important; + transform: none !important; +} \ No newline at end of file diff --git a/muk_web_theme/static/src/scss/navbar.scss b/muk_web_theme/static/src/scss/navbar.scss new file mode 100644 index 0000000..f71c792 --- /dev/null +++ b/muk_web_theme/static/src/scss/navbar.scss @@ -0,0 +1,119 @@ +/********************************************************************************** +* +* Copyright (C) 2017 MuK IT GmbH +* +* 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 . +* +**********************************************************************************/ + +@include media-breakpoint-down(sm) { + .o_main_navbar { + display: flex; + > .dropdown { + display: flex; + .navbar-toggler { + color: white; + } + .o_menu_sections, .o_menu_systray { + padding: 0; + } + } + .o_menu_systray { + margin-left: auto; + } + .o_debug_manager { + .dropdown-menu.show { + @include mk-full-screen-sections(); + } + } + .o_user_menu { + > .dropdown-toggle { + padding: 0 10px; + white-space: nowrap; + &::before, &::after { + content: normal; + } + } + .dropdown-menu.show { + @include mk-full-screen-sections(); + } + } + .o_menu_brand, .o_menu_sections, .oe_topbar_name { + display: none; + } + .o-menu-toggle { + cursor: pointer; + padding: 0 $o-horizontal-padding; + } + .o_mail_systray_dropdown.show { + @include mk-full-screen-sections(); + .o_mail_preview_image { + align-items: center; + display: flex; + flex-direction: row; + img { + display: block; + height: auto; + } + } + } + .dropdown-menu .dropdown-item { + padding: { + bottom: 0.5rem; + top: 0.5rem; + } + } + .o_menu_sections.show { + @include mk-full-screen-sections(); + background: $mk-brand-gradient; + .dropdown-toggle, .dropdown-item { + padding: 0; + color: white; + height: 46px; + line-height: 46px; + background: none; + span { + padding-left: 15px; + font-size: initial; + } + } + .mk_menu_mobile_section { + .fa { + padding-top: 14px; + padding-right: 15px; + } + .dropdown-menu { + padding: 0; + width: 100%; + border: none; + min-width: auto; + box-shadow: none; + background: none; + position: relative; + } + @for $index from 1 through 5 { + .o_menu_entry_lvl_#{$index} { + margin-left: ($index - 1) * 1.5rem; + } + } + @for $index from 2 through 5 { + .o_menu_header_lvl_#{$index} { + margin-left: ($index - 1) * 1.5rem; + } + } + } + + } + } +} \ No newline at end of file diff --git a/muk_web_theme/static/src/scss/variables.scss b/muk_web_theme/static/src/scss/variables.scss new file mode 100644 index 0000000..81f2abd --- /dev/null +++ b/muk_web_theme/static/src/scss/variables.scss @@ -0,0 +1,46 @@ +/********************************************************************************** +* +* Copyright (C) 2017 MuK IT GmbH +* +* 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 . +* +**********************************************************************************/ + +//---------------------------------------------------------- +// Fonts +//---------------------------------------------------------- + +$font-family-sans-serif: 'Roboto'; + +//---------------------------------------------------------- +// Components +//---------------------------------------------------------- + +$border-radius: 0; +$border-radius-lg: 0; +$border-radius-sm: 0; + +//---------------------------------------------------------- +// Links +//---------------------------------------------------------- + +$link-color: darken($o-brand-primary, 8%); +$font-family-sans-serif: 'Roboto'; + +//---------------------------------------------------------- +// AppBarMenu +//---------------------------------------------------------- + +$mk-sidebar-large-width: 146px; +$mk-sidebar-small-width: 46px; \ No newline at end of file diff --git a/muk_web_theme/static/src/xml/apps.xml b/muk_web_theme/static/src/xml/apps.xml new file mode 100644 index 0000000..73097c0 --- /dev/null +++ b/muk_web_theme/static/src/xml/apps.xml @@ -0,0 +1,68 @@ + + + + + + + + + h + + + fa fa-th + + +
    +
    +
    +
    +
    + +
    +
    + +
    +
    +
    +
    + + + + + + + + + + + + + + + + +
    + + + + \ No newline at end of file diff --git a/muk_web_theme/static/src/xml/appsbar.xml b/muk_web_theme/static/src/xml/appsbar.xml new file mode 100644 index 0000000..c2f9e18 --- /dev/null +++ b/muk_web_theme/static/src/xml/appsbar.xml @@ -0,0 +1,42 @@ + + + + + + + +
    + +
    +
    + +
    \ No newline at end of file diff --git a/muk_web_theme/static/src/xml/navbar.xml b/muk_web_theme/static/src/xml/navbar.xml new file mode 100644 index 0000000..297b05e --- /dev/null +++ b/muk_web_theme/static/src/xml/navbar.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + +
    + + + + + + +
  • + + + + + +
  • +
    + +
  • + + +
  • +
    +
    + + + + +
    + + + + + + + +
    +
    +
    + + \ No newline at end of file diff --git a/muk_web_theme/static/src/xml/sidebar.xml b/muk_web_theme/static/src/xml/sidebar.xml new file mode 100644 index 0000000..d7fe158 --- /dev/null +++ b/muk_web_theme/static/src/xml/sidebar.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/muk_web_theme/static/src/xml/views.xml b/muk_web_theme/static/src/xml/views.xml new file mode 100644 index 0000000..933247c --- /dev/null +++ b/muk_web_theme/static/src/xml/views.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/muk_web_theme/template/assets.xml b/muk_web_theme/template/assets.xml new file mode 100644 index 0000000..3f3f396 --- /dev/null +++ b/muk_web_theme/template/assets.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + +