diff --git a/muk_web_utils/LICENSE b/muk_web_utils/LICENSE new file mode 100644 index 0000000..153d416 --- /dev/null +++ b/muk_web_utils/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 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. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser 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 +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. \ No newline at end of file diff --git a/muk_web_utils/README.rst b/muk_web_utils/README.rst new file mode 100644 index 0000000..1d9ac57 --- /dev/null +++ b/muk_web_utils/README.rst @@ -0,0 +1,113 @@ +============= +MuK Web Utils +============= + +Technical module to provide some utility features and libraries that can be used +in other applications. This module has no direct effect on the running system. + +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 +============= + +This module has no direct visible effect on the system. It provide utility features. + +Credits +======= + +Contributors +------------ + +* Mathias Markl + +Images +------------ + +Some pictures are based on or inspired by the icon set of Font Awesome: + +* `Font Awesome `_ + +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_utils/__init__.py b/muk_web_utils/__init__.py new file mode 100644 index 0000000..bbab24a --- /dev/null +++ b/muk_web_utils/__init__.py @@ -0,0 +1,24 @@ +################################################################################### +# +# Copyright (c) 2017-2019 MuK IT GmbH. +# +# This file is part of MuK Web Utils +# (see https://mukit.at). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# +################################################################################### + +from . import models +from . import controllers \ No newline at end of file diff --git a/muk_web_utils/__manifest__.py b/muk_web_utils/__manifest__.py new file mode 100644 index 0000000..9fd7dbc --- /dev/null +++ b/muk_web_utils/__manifest__.py @@ -0,0 +1,58 @@ +################################################################################### +# +# Copyright (c) 2017-2019 MuK IT GmbH. +# +# This file is part of MuK Web Utils +# (see https://mukit.at). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# +################################################################################### + +{ + "name": "MuK Web Utils", + "summary": """Utility Features""", + "version": "13.0.1.0.0", + "category": "Extra Tools", + "license": "LGPL-3", + "author": "MuK IT", + "website": "http://www.mukit.at", + 'live_test_url': 'https://mukit.at/r/SgN', + "contributors": [ + "Mathias Markl ", + "Benedikt Jilek ", + ], + "depends": [ + "web_editor", + "muk_autovacuum", + ], + "data": [ + "template/assets.xml", + "views/res_config_settings_view.xml", + "data/autovacuum.xml", + ], + "qweb": [ + "static/src/xml/*.xml", + ], + "images": [ + 'static/description/banner.png' + ], + "external_dependencies": { + "python": [], + "bin": [], + }, + "application": False, + "installable": True, + 'auto_install': False, +} diff --git a/muk_web_utils/controllers/__init__.py b/muk_web_utils/controllers/__init__.py new file mode 100644 index 0000000..5caf3fa --- /dev/null +++ b/muk_web_utils/controllers/__init__.py @@ -0,0 +1,24 @@ +################################################################################### +# +# Copyright (c) 2017-2019 MuK IT GmbH. +# +# This file is part of MuK Web Utils +# (see https://mukit.at). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# +################################################################################### + +from . import backend +from . import attachment diff --git a/muk_web_utils/controllers/attachment.py b/muk_web_utils/controllers/attachment.py new file mode 100644 index 0000000..6f62a21 --- /dev/null +++ b/muk_web_utils/controllers/attachment.py @@ -0,0 +1,56 @@ +################################################################################### +# +# Copyright (c) 2017-2019 MuK IT GmbH. +# +# This file is part of MuK Web Utils +# (see https://mukit.at). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# +################################################################################### + +import json +import base64 +import logging + +from odoo import http, SUPERUSER_ID +from odoo.http import request +from odoo.tools.misc import str2bool + +_logger = logging.getLogger(__name__) + +class AttachmentController(http.Controller): + + @http.route('/utils/attachment/add', type='http', auth="user", methods=['POST']) + def add_attachment(self, ufile, temporary=False, **kw): + tmp = temporary and str2bool(temporary) or False + name = "Access Attachment: %s" % ufile.filename + attachment = request.env['ir.attachment'].create({ + 'name': tmp and "%s (Temporary)" % name or name, + 'datas': base64.b64encode(ufile.read()), + 'datas_fname': ufile.filename, + 'type': 'binary', + 'public': False, + 'temporary': tmp, + }) + attachment.generate_access_token() + if ufile.mimetype and ufile.mimetype != 'application/octet-stream': + attachment.with_user(request.env['res.users'].browse(SUPERUSER_ID)).write({ + 'mimetype': ufile.mimetype, + }) + base_url = request.env['ir.config_parameter'].with_user(request.env['res.users'].browse(SUPERUSER_ID)).get_param('web.base.url') + result = attachment.read(['name', 'datas_fname', 'mimetype', 'checksum', 'access_token'])[0] + result['url'] = '%s/web/content/%s?access_token=%s' % (base_url, attachment.id, attachment.access_token) + return json.dumps(result) + \ No newline at end of file diff --git a/muk_web_utils/controllers/backend.py b/muk_web_utils/controllers/backend.py new file mode 100644 index 0000000..b17f109 --- /dev/null +++ b/muk_web_utils/controllers/backend.py @@ -0,0 +1,37 @@ +################################################################################### +# +# Copyright (c) 2017-2019 MuK IT GmbH. +# +# This file is part of MuK Web Utils +# (see https://mukit.at). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# +################################################################################### + +import logging + +from odoo import http, SUPERUSER_ID +from odoo.http import request + +_logger = logging.getLogger(__name__) + +class BackendController(http.Controller): + + @http.route('/config/muk_web_utils.binary_max_size', type='json', auth="user") + def max_upload_size(self, **kw): + params = request.env['ir.config_parameter'].with_user(request.env['res.users'].browse(SUPERUSER_ID)) + return { + 'max_upload_size': int(params.get_param('muk_web_utils.binary_max_size', default=25)) + } \ No newline at end of file diff --git a/muk_web_utils/data/autovacuum.xml b/muk_web_utils/data/autovacuum.xml new file mode 100644 index 0000000..c3c3f22 --- /dev/null +++ b/muk_web_utils/data/autovacuum.xml @@ -0,0 +1,36 @@ + + + + + + + + Delete temporary Attachments + + + domain + [('temporary','=',True), ('create_date', '<=', (datetime.datetime.utcnow() - datetime.timedelta(hours=24)).strftime('%Y-%m-%d %H:%M:%S'))] + + + + \ No newline at end of file diff --git a/muk_web_utils/doc/changelog.rst b/muk_web_utils/doc/changelog.rst new file mode 100644 index 0000000..faf2ed6 --- /dev/null +++ b/muk_web_utils/doc/changelog.rst @@ -0,0 +1,54 @@ +`2.9.0` +------- + +- Temporary attachments + +`2.8.0` +------- + +- Added path field widgets + +`2.7.0` +------- + +- Added color index field widget + +`2.6.0` +------- + +- Moved editor features to separate module + +`2.5.0` +------- + +- Added color field widget + +`2.4.0` +------- + +- Added widget to share binary fields + +`2.3.0` +------- + +- Added custom colors to snippet options + +`2.2.0` +------- + +- Added widget to share text fields + +`2.1.0` +------- + +- Automatic labels on settings + +`2.0.0` +------- + +- Migrated to Python 3 + +`1.0.0` +------- + +- Init version diff --git a/muk_web_utils/doc/index.rst b/muk_web_utils/doc/index.rst new file mode 100644 index 0000000..1d9ac57 --- /dev/null +++ b/muk_web_utils/doc/index.rst @@ -0,0 +1,113 @@ +============= +MuK Web Utils +============= + +Technical module to provide some utility features and libraries that can be used +in other applications. This module has no direct effect on the running system. + +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 +============= + +This module has no direct visible effect on the system. It provide utility features. + +Credits +======= + +Contributors +------------ + +* Mathias Markl + +Images +------------ + +Some pictures are based on or inspired by the icon set of Font Awesome: + +* `Font Awesome `_ + +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_utils/i18n/de.po b/muk_web_utils/i18n/de.po new file mode 100644 index 0000000..9b89532 --- /dev/null +++ b/muk_web_utils/i18n/de.po @@ -0,0 +1,340 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * muk_web_utils +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.0-20190128\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-02-27 09:28+0000\n" +"PO-Revision-Date: 2019-06-27 08:23+0000\n" +"Last-Translator: MuK IT \n" +"Language-Team: German \n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 3.7\n" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/color.js:113 +#, python-format +msgid "'%s' is not a correct color index (0-12)" +msgstr "%s' ist kein korrekter Farbindex (0-12)." + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/color.js:68 +#, python-format +msgid "'%s' is not a correct color value" +msgstr "%s' ist kein korrekter Farbwert." + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/share.js:175 +#, python-format +msgid "<%= name %> shared a file!" +msgstr "<%= name %> teilte eine Datei!" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/share.js:115 +#: code:addons/muk_web_utils/static/src/js/fields/share.js:144 +#, python-format +msgid "<%= name %> shared a message!" +msgstr "<%= name %> teilte eine Nachricht!" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/share.js:116 +#: code:addons/muk_web_utils/static/src/js/fields/share.js:145 +#: code:addons/muk_web_utils/static/src/js/fields/share.js:176 +#, python-format +msgid "<%= value %>" +msgstr "<%= Wert %>" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/core/dialog.js:51 +#: code:addons/muk_web_utils/static/src/js/fields/module.js:50 +#, python-format +msgid "Cancel" +msgstr "Abbrechen" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/module.xml:30 +#, python-format +msgid "Click on the download button to be redirected to the store and download the corresponding module." +msgstr "" +"Klicken Sie auf den Download-Button, um in den Shop weitergeleitet zu werden " +"und das entsprechende Modul herunterzuladen." + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:46 +#, python-format +msgid "Color 0" +msgstr "Farbe 0" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:47 +#, python-format +msgid "Color 1" +msgstr "Farbe 1" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:56 +#, python-format +msgid "Color 10" +msgstr "Farbe 10" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:57 +#, python-format +msgid "Color 11" +msgstr "Farbe 11" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:48 +#, python-format +msgid "Color 2" +msgstr "Farbe 2" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:49 +#, python-format +msgid "Color 3" +msgstr "Farbe 3" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:50 +#, python-format +msgid "Color 4" +msgstr "Farbe 4" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:51 +#, python-format +msgid "Color 5" +msgstr "Farbe 5" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:52 +#, python-format +msgid "Color 6" +msgstr "Farbe 6" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:53 +#, python-format +msgid "Color 7" +msgstr "Farbe 7" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:54 +#, python-format +msgid "Color 8" +msgstr "Farbe 8" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:55 +#, python-format +msgid "Color 9" +msgstr "Farbe 9" + +#. module: muk_web_utils +#: model:ir.model,name:muk_web_utils.model_res_config_settings +msgid "Config Settings" +msgstr "Konfiguration " + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/module.js:45 +#: code:addons/muk_web_utils/static/src/xml/share.xml:97 +#, python-format +msgid "Download" +msgstr "Herunterladen" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/core/dropzone.js:108 +#, python-format +msgid "Drop files here to upload!" +msgstr "Lassen Sie Dateien hier zum Hochladen fallen!" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:45 +#, python-format +msgid "External" +msgstr "Extern" + +#. module: muk_web_utils +#: model:ir.model.fields,field_description:muk_web_utils.field_res_config_settings__binary_max_size +msgid "File Size Limit" +msgstr "Dateigrößenbegrenzung" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:35 +#, python-format +msgid "Internal" +msgstr "Intern" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/copy.js:107 +#, python-format +msgid "Link Copied!" +msgstr "Link kopiert!" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:48 +#, python-format +msgid "Mail" +msgstr "Mail" + +#. module: muk_web_utils +#: model_terms:ir.ui.view,arch_db:muk_web_utils.res_config_settings_view_form +msgid "Maximum allowed file size in MB" +msgstr "Maximal zulässige Dateigröße in MB" + +#. module: muk_web_utils +#: model:ir.model.fields,help:muk_web_utils.field_res_config_settings__binary_max_size +msgid "Maximum allowed file size in megabytes. Note that this setting only adjusts\n" +" the binary widgets accordingly. The maximum file size on your server can probably\n" +" be restricted in several places. Note that a large file size limit and therefore\n" +" large files in your system can significantly limit performance." +msgstr "" +"Maximal zulässige Dateigröße in Megabyte. Beachten Sie, dass diese " +"Einstellung nur angepasst wird._x000D_\n" +" die binären Widgets entsprechend. Die maximale Dateigröße auf " +"Ihrem Server kann wahrscheinlich_x000D_\n" +" an mehreren Stellen eingeschränkt sein. Beachten Sie, dass eine " +"große Dateigröße und damit eine große Dateigröße begrenzt ist._x000D_\n" +" können große Dateien in Ihrem System die Leistung erheblich " +"einschränken." + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:38 +#, python-format +msgid "Message" +msgstr "Nachricht" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/module.js:59 +#: code:addons/muk_web_utils/static/src/xml/module.xml:26 +#, python-format +msgid "Missing Module" +msgstr "Fehlende Module" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:42 +#, python-format +msgid "Note" +msgstr "Hinweis" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:83 +#, python-format +msgid "Open" +msgstr "Offen" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/core/dialog.js:45 +#, python-format +msgid "Save" +msgstr "Speichern" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:52 +#, python-format +msgid "Send" +msgstr "Senden" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:27 +#, python-format +msgid "Share" +msgstr "Teilen" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/module.js:74 +#, python-format +msgid "Store" +msgstr "Laden" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/copy.js:35 +#, python-format +msgid "The field '%s' must be a binary field with an set attachment flag for the share widget to work." +msgstr "" +"Das Feld'%s' muss ein Binärfeld mit einem gesetzten Attachment-Flag sein, " +"damit das Share-Widget funktioniert." + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/module.xml:29 +#, python-format +msgid "The module could not be found on the server." +msgstr "Das Modul konnte auf dem Server nicht gefunden werden." + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/core/async.js:46 +#, python-format +msgid "Upload" +msgstr "Hochladen" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/core/async.js:47 +#, python-format +msgid "Uploading..." +msgstr "Hochladen....." + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:95 +#, python-format +msgid "shared a file with you!" +msgstr "hat eine Datei mit dir geteilt!" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:81 +#, python-format +msgid "shared a link with you!" +msgstr "einen Link mit dir geteilt!" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:87 +#, python-format +msgid "shared a message with you!" +msgstr "hat eine Nachricht mit dir geteilt!" diff --git a/muk_web_utils/i18n/es.po b/muk_web_utils/i18n/es.po new file mode 100644 index 0000000..72ab6d3 --- /dev/null +++ b/muk_web_utils/i18n/es.po @@ -0,0 +1,326 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * muk_web_utils +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.0-20190128\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-02-27 09:28+0000\n" +"PO-Revision-Date: 2019-02-27 09:28+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/color.js:113 +#, python-format +msgid "'%s' is not a correct color index (0-12)" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/color.js:68 +#, python-format +msgid "'%s' is not a correct color value" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/share.js:175 +#, python-format +msgid "<%= name %> shared a file!" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/share.js:115 +#: code:addons/muk_web_utils/static/src/js/fields/share.js:144 +#, python-format +msgid "<%= name %> shared a message!" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/share.js:116 +#: code:addons/muk_web_utils/static/src/js/fields/share.js:145 +#: code:addons/muk_web_utils/static/src/js/fields/share.js:176 +#, python-format +msgid "<%= value %>" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/core/dialog.js:51 +#: code:addons/muk_web_utils/static/src/js/fields/module.js:50 +#, python-format +msgid "Cancel" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/module.xml:30 +#, python-format +msgid "Click on the download button to be redirected to the store and download the corresponding module." +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:46 +#, python-format +msgid "Color 0" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:47 +#, python-format +msgid "Color 1" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:56 +#, python-format +msgid "Color 10" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:57 +#, python-format +msgid "Color 11" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:48 +#, python-format +msgid "Color 2" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:49 +#, python-format +msgid "Color 3" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:50 +#, python-format +msgid "Color 4" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:51 +#, python-format +msgid "Color 5" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:52 +#, python-format +msgid "Color 6" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:53 +#, python-format +msgid "Color 7" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:54 +#, python-format +msgid "Color 8" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:55 +#, python-format +msgid "Color 9" +msgstr "" + +#. module: muk_web_utils +#: model:ir.model,name:muk_web_utils.model_res_config_settings +msgid "Config Settings" +msgstr "Opciones de Configuración" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/module.js:45 +#: code:addons/muk_web_utils/static/src/xml/share.xml:97 +#, python-format +msgid "Download" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/core/dropzone.js:108 +#, python-format +msgid "Drop files here to upload!" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:45 +#, python-format +msgid "External" +msgstr "" + +#. module: muk_web_utils +#: model:ir.model.fields,field_description:muk_web_utils.field_res_config_settings__binary_max_size +msgid "File Size Limit" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:35 +#, python-format +msgid "Internal" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/copy.js:107 +#, python-format +msgid "Link Copied!" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:48 +#, python-format +msgid "Mail" +msgstr "" + +#. module: muk_web_utils +#: model_terms:ir.ui.view,arch_db:muk_web_utils.res_config_settings_view_form +msgid "Maximum allowed file size in MB" +msgstr "" + +#. module: muk_web_utils +#: model:ir.model.fields,help:muk_web_utils.field_res_config_settings__binary_max_size +msgid "Maximum allowed file size in megabytes. Note that this setting only adjusts\n" +" the binary widgets accordingly. The maximum file size on your server can probably\n" +" be restricted in several places. Note that a large file size limit and therefore\n" +" large files in your system can significantly limit performance." +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:38 +#, python-format +msgid "Message" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/module.js:59 +#: code:addons/muk_web_utils/static/src/xml/module.xml:26 +#, python-format +msgid "Missing Module" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:42 +#, python-format +msgid "Note" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:83 +#, python-format +msgid "Open" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/core/dialog.js:45 +#, python-format +msgid "Save" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:52 +#, python-format +msgid "Send" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:27 +#, python-format +msgid "Share" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/module.js:74 +#, python-format +msgid "Store" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/copy.js:35 +#, python-format +msgid "The field '%s' must be a binary field with an set attachment flag for the share widget to work." +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/module.xml:29 +#, python-format +msgid "The module could not be found on the server." +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/core/async.js:46 +#, python-format +msgid "Upload" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/core/async.js:47 +#, python-format +msgid "Uploading..." +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:95 +#, python-format +msgid "shared a file with you!" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:81 +#, python-format +msgid "shared a link with you!" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:87 +#, python-format +msgid "shared a message with you!" +msgstr "" + diff --git a/muk_web_utils/i18n/fr.po b/muk_web_utils/i18n/fr.po new file mode 100644 index 0000000..d00b7e5 --- /dev/null +++ b/muk_web_utils/i18n/fr.po @@ -0,0 +1,326 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * muk_web_utils +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.0-20190128\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-02-27 09:28+0000\n" +"PO-Revision-Date: 2019-02-27 09:28+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/color.js:113 +#, python-format +msgid "'%s' is not a correct color index (0-12)" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/color.js:68 +#, python-format +msgid "'%s' is not a correct color value" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/share.js:175 +#, python-format +msgid "<%= name %> shared a file!" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/share.js:115 +#: code:addons/muk_web_utils/static/src/js/fields/share.js:144 +#, python-format +msgid "<%= name %> shared a message!" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/share.js:116 +#: code:addons/muk_web_utils/static/src/js/fields/share.js:145 +#: code:addons/muk_web_utils/static/src/js/fields/share.js:176 +#, python-format +msgid "<%= value %>" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/core/dialog.js:51 +#: code:addons/muk_web_utils/static/src/js/fields/module.js:50 +#, python-format +msgid "Cancel" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/module.xml:30 +#, python-format +msgid "Click on the download button to be redirected to the store and download the corresponding module." +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:46 +#, python-format +msgid "Color 0" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:47 +#, python-format +msgid "Color 1" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:56 +#, python-format +msgid "Color 10" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:57 +#, python-format +msgid "Color 11" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:48 +#, python-format +msgid "Color 2" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:49 +#, python-format +msgid "Color 3" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:50 +#, python-format +msgid "Color 4" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:51 +#, python-format +msgid "Color 5" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:52 +#, python-format +msgid "Color 6" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:53 +#, python-format +msgid "Color 7" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:54 +#, python-format +msgid "Color 8" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:55 +#, python-format +msgid "Color 9" +msgstr "" + +#. module: muk_web_utils +#: model:ir.model,name:muk_web_utils.model_res_config_settings +msgid "Config Settings" +msgstr "Paramètres de config" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/module.js:45 +#: code:addons/muk_web_utils/static/src/xml/share.xml:97 +#, python-format +msgid "Download" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/core/dropzone.js:108 +#, python-format +msgid "Drop files here to upload!" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:45 +#, python-format +msgid "External" +msgstr "" + +#. module: muk_web_utils +#: model:ir.model.fields,field_description:muk_web_utils.field_res_config_settings__binary_max_size +msgid "File Size Limit" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:35 +#, python-format +msgid "Internal" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/copy.js:107 +#, python-format +msgid "Link Copied!" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:48 +#, python-format +msgid "Mail" +msgstr "" + +#. module: muk_web_utils +#: model_terms:ir.ui.view,arch_db:muk_web_utils.res_config_settings_view_form +msgid "Maximum allowed file size in MB" +msgstr "" + +#. module: muk_web_utils +#: model:ir.model.fields,help:muk_web_utils.field_res_config_settings__binary_max_size +msgid "Maximum allowed file size in megabytes. Note that this setting only adjusts\n" +" the binary widgets accordingly. The maximum file size on your server can probably\n" +" be restricted in several places. Note that a large file size limit and therefore\n" +" large files in your system can significantly limit performance." +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:38 +#, python-format +msgid "Message" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/module.js:59 +#: code:addons/muk_web_utils/static/src/xml/module.xml:26 +#, python-format +msgid "Missing Module" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:42 +#, python-format +msgid "Note" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:83 +#, python-format +msgid "Open" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/core/dialog.js:45 +#, python-format +msgid "Save" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:52 +#, python-format +msgid "Send" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:27 +#, python-format +msgid "Share" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/module.js:74 +#, python-format +msgid "Store" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/copy.js:35 +#, python-format +msgid "The field '%s' must be a binary field with an set attachment flag for the share widget to work." +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/module.xml:29 +#, python-format +msgid "The module could not be found on the server." +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/core/async.js:46 +#, python-format +msgid "Upload" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/core/async.js:47 +#, python-format +msgid "Uploading..." +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:95 +#, python-format +msgid "shared a file with you!" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:81 +#, python-format +msgid "shared a link with you!" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:87 +#, python-format +msgid "shared a message with you!" +msgstr "" + diff --git a/muk_web_utils/i18n/muk_web_utils.pot b/muk_web_utils/i18n/muk_web_utils.pot new file mode 100644 index 0000000..cefad22 --- /dev/null +++ b/muk_web_utils/i18n/muk_web_utils.pot @@ -0,0 +1,326 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * muk_web_utils +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.0-20190128\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-02-27 09:28+0000\n" +"PO-Revision-Date: 2019-02-27 09:28+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/color.js:113 +#, python-format +msgid "'%s' is not a correct color index (0-12)" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/color.js:68 +#, python-format +msgid "'%s' is not a correct color value" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/share.js:175 +#, python-format +msgid "<%= name %> shared a file!" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/share.js:115 +#: code:addons/muk_web_utils/static/src/js/fields/share.js:144 +#, python-format +msgid "<%= name %> shared a message!" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/share.js:116 +#: code:addons/muk_web_utils/static/src/js/fields/share.js:145 +#: code:addons/muk_web_utils/static/src/js/fields/share.js:176 +#, python-format +msgid "<%= value %>" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/core/dialog.js:51 +#: code:addons/muk_web_utils/static/src/js/fields/module.js:50 +#, python-format +msgid "Cancel" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/module.xml:30 +#, python-format +msgid "Click on the download button to be redirected to the store and download the corresponding module." +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:46 +#, python-format +msgid "Color 0" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:47 +#, python-format +msgid "Color 1" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:56 +#, python-format +msgid "Color 10" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:57 +#, python-format +msgid "Color 11" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:48 +#, python-format +msgid "Color 2" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:49 +#, python-format +msgid "Color 3" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:50 +#, python-format +msgid "Color 4" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:51 +#, python-format +msgid "Color 5" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:52 +#, python-format +msgid "Color 6" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:53 +#, python-format +msgid "Color 7" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:54 +#, python-format +msgid "Color 8" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:55 +#, python-format +msgid "Color 9" +msgstr "" + +#. module: muk_web_utils +#: model:ir.model,name:muk_web_utils.model_res_config_settings +msgid "Config Settings" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/module.js:45 +#: code:addons/muk_web_utils/static/src/xml/share.xml:97 +#, python-format +msgid "Download" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/core/dropzone.js:108 +#, python-format +msgid "Drop files here to upload!" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:45 +#, python-format +msgid "External" +msgstr "" + +#. module: muk_web_utils +#: model:ir.model.fields,field_description:muk_web_utils.field_res_config_settings__binary_max_size +msgid "File Size Limit" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:35 +#, python-format +msgid "Internal" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/copy.js:107 +#, python-format +msgid "Link Copied!" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:48 +#, python-format +msgid "Mail" +msgstr "" + +#. module: muk_web_utils +#: model_terms:ir.ui.view,arch_db:muk_web_utils.res_config_settings_view_form +msgid "Maximum allowed file size in MB" +msgstr "" + +#. module: muk_web_utils +#: model:ir.model.fields,help:muk_web_utils.field_res_config_settings__binary_max_size +msgid "Maximum allowed file size in megabytes. Note that this setting only adjusts\n" +" the binary widgets accordingly. The maximum file size on your server can probably\n" +" be restricted in several places. Note that a large file size limit and therefore\n" +" large files in your system can significantly limit performance." +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:38 +#, python-format +msgid "Message" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/module.js:59 +#: code:addons/muk_web_utils/static/src/xml/module.xml:26 +#, python-format +msgid "Missing Module" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:42 +#, python-format +msgid "Note" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:83 +#, python-format +msgid "Open" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/core/dialog.js:45 +#, python-format +msgid "Save" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:52 +#, python-format +msgid "Send" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:27 +#, python-format +msgid "Share" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/module.js:74 +#, python-format +msgid "Store" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/copy.js:35 +#, python-format +msgid "The field '%s' must be a binary field with an set attachment flag for the share widget to work." +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/module.xml:29 +#, python-format +msgid "The module could not be found on the server." +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/core/async.js:46 +#, python-format +msgid "Upload" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/core/async.js:47 +#, python-format +msgid "Uploading..." +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:95 +#, python-format +msgid "shared a file with you!" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:81 +#, python-format +msgid "shared a link with you!" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:87 +#, python-format +msgid "shared a message with you!" +msgstr "" + diff --git a/muk_web_utils/i18n/nl.po b/muk_web_utils/i18n/nl.po new file mode 100644 index 0000000..032b13b --- /dev/null +++ b/muk_web_utils/i18n/nl.po @@ -0,0 +1,326 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * muk_web_utils +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.0-20190128\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-02-27 09:28+0000\n" +"PO-Revision-Date: 2019-02-27 09:28+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/color.js:113 +#, python-format +msgid "'%s' is not a correct color index (0-12)" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/color.js:68 +#, python-format +msgid "'%s' is not a correct color value" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/share.js:175 +#, python-format +msgid "<%= name %> shared a file!" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/share.js:115 +#: code:addons/muk_web_utils/static/src/js/fields/share.js:144 +#, python-format +msgid "<%= name %> shared a message!" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/share.js:116 +#: code:addons/muk_web_utils/static/src/js/fields/share.js:145 +#: code:addons/muk_web_utils/static/src/js/fields/share.js:176 +#, python-format +msgid "<%= value %>" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/core/dialog.js:51 +#: code:addons/muk_web_utils/static/src/js/fields/module.js:50 +#, python-format +msgid "Cancel" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/module.xml:30 +#, python-format +msgid "Click on the download button to be redirected to the store and download the corresponding module." +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:46 +#, python-format +msgid "Color 0" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:47 +#, python-format +msgid "Color 1" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:56 +#, python-format +msgid "Color 10" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:57 +#, python-format +msgid "Color 11" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:48 +#, python-format +msgid "Color 2" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:49 +#, python-format +msgid "Color 3" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:50 +#, python-format +msgid "Color 4" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:51 +#, python-format +msgid "Color 5" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:52 +#, python-format +msgid "Color 6" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:53 +#, python-format +msgid "Color 7" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:54 +#, python-format +msgid "Color 8" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/color.xml:55 +#, python-format +msgid "Color 9" +msgstr "" + +#. module: muk_web_utils +#: model:ir.model,name:muk_web_utils.model_res_config_settings +msgid "Config Settings" +msgstr "Configuratie instellingen" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/module.js:45 +#: code:addons/muk_web_utils/static/src/xml/share.xml:97 +#, python-format +msgid "Download" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/core/dropzone.js:108 +#, python-format +msgid "Drop files here to upload!" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:45 +#, python-format +msgid "External" +msgstr "" + +#. module: muk_web_utils +#: model:ir.model.fields,field_description:muk_web_utils.field_res_config_settings__binary_max_size +msgid "File Size Limit" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:35 +#, python-format +msgid "Internal" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/copy.js:107 +#, python-format +msgid "Link Copied!" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:48 +#, python-format +msgid "Mail" +msgstr "" + +#. module: muk_web_utils +#: model_terms:ir.ui.view,arch_db:muk_web_utils.res_config_settings_view_form +msgid "Maximum allowed file size in MB" +msgstr "" + +#. module: muk_web_utils +#: model:ir.model.fields,help:muk_web_utils.field_res_config_settings__binary_max_size +msgid "Maximum allowed file size in megabytes. Note that this setting only adjusts\n" +" the binary widgets accordingly. The maximum file size on your server can probably\n" +" be restricted in several places. Note that a large file size limit and therefore\n" +" large files in your system can significantly limit performance." +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:38 +#, python-format +msgid "Message" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/module.js:59 +#: code:addons/muk_web_utils/static/src/xml/module.xml:26 +#, python-format +msgid "Missing Module" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:42 +#, python-format +msgid "Note" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:83 +#, python-format +msgid "Open" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/core/dialog.js:45 +#, python-format +msgid "Save" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:52 +#, python-format +msgid "Send" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:27 +#, python-format +msgid "Share" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/module.js:74 +#, python-format +msgid "Store" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/fields/copy.js:35 +#, python-format +msgid "The field '%s' must be a binary field with an set attachment flag for the share widget to work." +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/module.xml:29 +#, python-format +msgid "The module could not be found on the server." +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/core/async.js:46 +#, python-format +msgid "Upload" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/js/core/async.js:47 +#, python-format +msgid "Uploading..." +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:95 +#, python-format +msgid "shared a file with you!" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:81 +#, python-format +msgid "shared a link with you!" +msgstr "" + +#. module: muk_web_utils +#. openerp-web +#: code:addons/muk_web_utils/static/src/xml/share.xml:87 +#, python-format +msgid "shared a message with you!" +msgstr "" + diff --git a/muk_web_utils/models/__init__.py b/muk_web_utils/models/__init__.py new file mode 100644 index 0000000..b692fb1 --- /dev/null +++ b/muk_web_utils/models/__init__.py @@ -0,0 +1,25 @@ +################################################################################### +# +# Copyright (c) 2017-2019 MuK IT GmbH. +# +# This file is part of MuK Web Utils +# (see https://mukit.at). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# +################################################################################### + +from . import ir_attachment +from . import res_config_settings + diff --git a/muk_web_utils/models/ir_attachment.py b/muk_web_utils/models/ir_attachment.py new file mode 100644 index 0000000..bb5d745 --- /dev/null +++ b/muk_web_utils/models/ir_attachment.py @@ -0,0 +1,46 @@ +################################################################################### +# +# Copyright (c) 2017-2019 MuK IT GmbH. +# +# This file is part of MuK Web Utils +# (see https://mukit.at). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# +################################################################################### + +import base64 +import logging +import mimetypes + +from odoo import api, models, fields, _ +from odoo.exceptions import AccessError +from odoo.tools.mimetypes import guess_mimetype + +_logger = logging.getLogger(__name__) + +class IrAttachment(models.Model): + + _inherit = 'ir.attachment' + + #---------------------------------------------------------- + # Database + #---------------------------------------------------------- + + temporary = fields.Boolean( + string="Temporary", + default=False, + help="Attachments will be deleted by Autovacuum.", + ) + \ No newline at end of file diff --git a/muk_web_utils/models/res_config_settings.py b/muk_web_utils/models/res_config_settings.py new file mode 100644 index 0000000..fc1da59 --- /dev/null +++ b/muk_web_utils/models/res_config_settings.py @@ -0,0 +1,79 @@ +################################################################################### +# +# Copyright (c) 2017-2019 MuK IT GmbH. +# +# This file is part of MuK Web Utils +# (see https://mukit.at). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# +################################################################################### + +import re +import json +import logging + +from lxml import etree + +from odoo import api, fields, models + +_logger = logging.getLogger(__name__) + +class ResConfigSettings(models.TransientModel): + + _inherit = 'res.config.settings' + + #---------------------------------------------------------- + # Database + #---------------------------------------------------------- + + binary_max_size = fields.Integer( + string='File Size Limit', + required=True, + default=25, + help="""Maximum allowed file size in megabytes. Note that this setting only adjusts + the binary widgets accordingly. The maximum file size on your server can probably + be restricted in several places. Note that a large file size limit and therefore + large files in your system can significantly limit performance.""") + + #---------------------------------------------------------- + # Functions + #---------------------------------------------------------- + + def set_values(self): + res = super(ResConfigSettings, self).set_values() + param = self.env['ir.config_parameter'].with_user(self.env.ref('base.user_admin')) + param.set_param('muk_web_utils.binary_max_size', self.binary_max_size) + return res + + @api.model + def get_values(self): + res = super(ResConfigSettings, self).get_values() + params = self.env['ir.config_parameter'].with_user(self.env.ref('base.user_admin')) + res.update(binary_max_size=int(params.get_param('muk_web_utils.binary_max_size', 25))) + return res + + @api.model + def fields_view_get(self, view_id=None, view_type='form', toolbar=False, submenu=False): + ret_val = super(ResConfigSettings, self).fields_view_get( + view_id=view_id, view_type=view_type, toolbar=toolbar, submenu=submenu) + modules = self.env['ir.module.module'].with_user(self.env.ref('base.user_admin')).search([]).mapped('name') + document = etree.XML(ret_val['arch']) + for field in ret_val['fields']: + if field.startswith("module_") and field[len("module_"):] not in modules: + for node in document.xpath("//field[@name='%s']" % field): + if node.get("widget") != 'upgrade_boolean': + node.set("widget", "module_boolean") + ret_val['arch'] = etree.tostring(document, encoding='unicode') + return ret_val \ No newline at end of file diff --git a/muk_web_utils/static/description/banner.png b/muk_web_utils/static/description/banner.png new file mode 100644 index 0000000..98df572 Binary files /dev/null and b/muk_web_utils/static/description/banner.png differ diff --git a/muk_web_utils/static/description/icon.png b/muk_web_utils/static/description/icon.png new file mode 100644 index 0000000..858ea62 Binary files /dev/null and b/muk_web_utils/static/description/icon.png differ diff --git a/muk_web_utils/static/description/icon.svg b/muk_web_utils/static/description/icon.svg new file mode 100644 index 0000000..d3d63f1 --- /dev/null +++ b/muk_web_utils/static/description/icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/muk_web_utils/static/description/index.html b/muk_web_utils/static/description/index.html new file mode 100644 index 0000000..2cccde3 --- /dev/null +++ b/muk_web_utils/static/description/index.html @@ -0,0 +1,124 @@ +
+
+

MuK Web Utils

+

Utility Features

+

MuK IT GmbH - + www.mukit.at

+
+
+ +
+
+
+

Overview

+

Technical module to provide some utility + features. The module is mainly used as a dependency by other modules + and to provide a collection of common libraries. It has no direct + visible effect on the system.

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

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_utils/static/description/logo.png b/muk_web_utils/static/description/logo.png new file mode 100644 index 0000000..9427ce3 Binary files /dev/null and b/muk_web_utils/static/description/logo.png differ diff --git a/muk_web_utils/static/description/preview.png b/muk_web_utils/static/description/preview.png new file mode 100644 index 0000000..1deb1cc Binary files /dev/null and b/muk_web_utils/static/description/preview.png differ diff --git a/muk_web_utils/static/description/service_customization.png b/muk_web_utils/static/description/service_customization.png new file mode 100644 index 0000000..3eac664 Binary files /dev/null and b/muk_web_utils/static/description/service_customization.png differ diff --git a/muk_web_utils/static/description/service_development.png b/muk_web_utils/static/description/service_development.png new file mode 100644 index 0000000..580d460 Binary files /dev/null and b/muk_web_utils/static/description/service_development.png differ diff --git a/muk_web_utils/static/description/service_implementation.png b/muk_web_utils/static/description/service_implementation.png new file mode 100644 index 0000000..d64b66b Binary files /dev/null and b/muk_web_utils/static/description/service_implementation.png differ diff --git a/muk_web_utils/static/description/service_integration.png b/muk_web_utils/static/description/service_integration.png new file mode 100644 index 0000000..76c5e80 Binary files /dev/null and b/muk_web_utils/static/description/service_integration.png differ diff --git a/muk_web_utils/static/description/service_support.png b/muk_web_utils/static/description/service_support.png new file mode 100644 index 0000000..4c530fa Binary files /dev/null and b/muk_web_utils/static/description/service_support.png differ diff --git a/muk_web_utils/static/libs/simplebar/simplebar.css b/muk_web_utils/static/libs/simplebar/simplebar.css new file mode 100644 index 0000000..48dc928 --- /dev/null +++ b/muk_web_utils/static/libs/simplebar/simplebar.css @@ -0,0 +1,160 @@ +[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-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_utils/static/libs/simplebar/simplebar.js b/muk_web_utils/static/libs/simplebar/simplebar.js new file mode 100644 index 0000000..044dbdb --- /dev/null +++ b/muk_web_utils/static/libs/simplebar/simplebar.js @@ -0,0 +1,4389 @@ +/** + * SimpleBar.js - v3.1.3 + * 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 = global || self, 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; + }; + + // 7.2.1 RequireObjectCoercible(argument) + var _defined = function (it) { + if (it == undefined) throw TypeError("Can't call method on " + it); + return it; + }; + + // 7.1.13 ToObject(argument) + + var _toObject = function (it) { + return Object(_defined(it)); + }; + + // 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 + }; + + // 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); + + // `AdvanceStringIndex` abstract operation + // https://tc39.github.io/ecma262/#sec-advancestringindex + var _advanceStringIndex = function (S, index, unicode) { + return index + (unicode ? at(S, index).length : 1); + }; + + var toString = {}.toString; + + var _cof = function (it) { + return toString.call(it).slice(8, -1); + }; + + 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 _core = createCommonjsModule(function (module) { + var core = module.exports = { version: '2.6.2' }; + if (typeof __e == 'number') __e = core; // eslint-disable-line no-undef + }); + var _core_1 = _core.version; + + 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 _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: '© 2019 Denis Pushkarev (zloirock.ru)' + }); + }); + + var id = 0; + var px = Math.random(); + var _uid = function (key) { + return 'Symbol('.concat(key === undefined ? '' : key, ')_', (++id + px).toString(36)); + }; + + 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; + }); + + // getting tag from 19.1.3.6 Object.prototype.toString() + + var TAG = _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)) == 'string' ? T + // builtinTag case + : ARG ? _cof(O) + // ES3 arguments fallback + : (B = _cof(O)) == 'Object' && typeof O.callee == 'function' ? 'Arguments' : B; + }; + + var builtinExec = RegExp.prototype.exec; + + // `RegExpExec` abstract operation + // https://tc39.github.io/ecma262/#sec-regexpexec + var _regexpExecAbstract = function (R, S) { + var exec = R.exec; + if (typeof exec === 'function') { + var result = exec.call(R, S); + if (typeof result !== 'object') { + throw new TypeError('RegExp exec method returned something other than an Object or null'); + } + return result; + } + if (_classof(R) !== 'RegExp') { + throw new TypeError('RegExp#exec called on incompatible receiver'); + } + return builtinExec.call(R, S); + }; + + // 21.2.5.3 get RegExp.prototype.flags + + var _flags = function () { + var that = _anObject(this); + var result = ''; + if (that.global) result += 'g'; + if (that.ignoreCase) result += 'i'; + if (that.multiline) result += 'm'; + if (that.unicode) result += 'u'; + if (that.sticky) result += 'y'; + return result; + }; + + var nativeExec = RegExp.prototype.exec; + // This always refers to the native implementation, because the + // String#replace polyfill uses ./fix-regexp-well-known-symbol-logic.js, + // which loads this file before patching the method. + var nativeReplace = String.prototype.replace; + + var patchedExec = nativeExec; + + var LAST_INDEX = 'lastIndex'; + + var UPDATES_LAST_INDEX_WRONG = (function () { + var re1 = /a/, + re2 = /b*/g; + nativeExec.call(re1, 'a'); + nativeExec.call(re2, 'a'); + return re1[LAST_INDEX] !== 0 || re2[LAST_INDEX] !== 0; + })(); + + // nonparticipating capturing group, copied from es5-shim's String#split patch. + var NPCG_INCLUDED = /()??/.exec('')[1] !== undefined; + + var PATCH = UPDATES_LAST_INDEX_WRONG || NPCG_INCLUDED; + + if (PATCH) { + patchedExec = function exec(str) { + var re = this; + var lastIndex, reCopy, match, i; + + if (NPCG_INCLUDED) { + reCopy = new RegExp('^' + re.source + '$(?!\\s)', _flags.call(re)); + } + if (UPDATES_LAST_INDEX_WRONG) lastIndex = re[LAST_INDEX]; + + match = nativeExec.call(re, str); + + if (UPDATES_LAST_INDEX_WRONG && match) { + re[LAST_INDEX] = re.global ? match.index + match[0].length : lastIndex; + } + if (NPCG_INCLUDED && match && match.length > 1) { + // Fix browsers whose `exec` methods don't consistently return `undefined` + // for NPCG, like IE8. NOTE: This doesn' work for /(.?)?/ + // eslint-disable-next-line no-loop-func + nativeReplace.call(match[0], reCopy, function () { + for (i = 1; i < arguments.length - 2; i++) { + if (arguments[i] === undefined) match[i] = undefined; + } + }); + } + + return match; + }; + } + + var _regexpExec = patchedExec; + + 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 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 _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); + }); + }); + + 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; + + _export({ + target: 'RegExp', + proto: true, + forced: _regexpExec !== /./.exec + }, { + exec: _regexpExec + }); + + var SPECIES = _wks('species'); + + var REPLACE_SUPPORTS_NAMED_GROUPS = !_fails(function () { + // #replace needs built-in support for named groups. + // #match works fine because it just return the exec results, even if it has + // a "grops" property. + var re = /./; + re.exec = function () { + var result = []; + result.groups = { a: '7' }; + return result; + }; + return ''.replace(re, '$') !== '7'; + }); + + var SPLIT_WORKS_WITH_OVERWRITTEN_EXEC = (function () { + // Chrome 51 has a buggy "split" implementation when RegExp#exec !== nativeExec + var re = /(?:)/; + var originalExec = re.exec; + re.exec = function () { return originalExec.apply(this, arguments); }; + var result = 'ab'.split(re); + return result.length === 2 && result[0] === 'a' && result[1] === 'b'; + })(); + + var _fixReWks = function (KEY, length, exec) { + var SYMBOL = _wks(KEY); + + var DELEGATES_TO_SYMBOL = !_fails(function () { + // String methods call symbol-named RegEp methods + var O = {}; + O[SYMBOL] = function () { return 7; }; + return ''[KEY](O) != 7; + }); + + var DELEGATES_TO_EXEC = DELEGATES_TO_SYMBOL ? !_fails(function () { + // Symbol-named RegExp methods call .exec + var execCalled = false; + var re = /a/; + re.exec = function () { execCalled = true; return null; }; + if (KEY === 'split') { + // RegExp[@@split] doesn't call the regex's exec method, but first creates + // a new one. We need to return the patched regex when creating the new one. + re.constructor = {}; + re.constructor[SPECIES] = function () { return re; }; + } + re[SYMBOL](''); + return !execCalled; + }) : undefined; + + if ( + !DELEGATES_TO_SYMBOL || + !DELEGATES_TO_EXEC || + (KEY === 'replace' && !REPLACE_SUPPORTS_NAMED_GROUPS) || + (KEY === 'split' && !SPLIT_WORKS_WITH_OVERWRITTEN_EXEC) + ) { + var nativeRegExpMethod = /./[SYMBOL]; + var fns = exec( + _defined, + SYMBOL, + ''[KEY], + function maybeCallNative(nativeMethod, regexp, str, arg2, forceStringMethod) { + if (regexp.exec === _regexpExec) { + if (DELEGATES_TO_SYMBOL && !forceStringMethod) { + // The native String method already delegates to @@method (this + // polyfilled function), leasing to infinite recursion. + // We avoid it by directly calling the native @@method method. + return { done: true, value: nativeRegExpMethod.call(regexp, str, arg2) }; + } + return { done: true, value: nativeMethod.call(str, regexp, arg2) }; + } + return { done: false }; + } + ); + var strfn = fns[0]; + var rxfn = fns[1]; + + _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); } + ); + } + }; + + var max = Math.max; + var min$1 = Math.min; + var floor$1 = Math.floor; + var SUBSTITUTION_SYMBOLS = /\$([$&`']|\d\d?|<[^>]*>)/g; + var SUBSTITUTION_SYMBOLS_NO_NAMED = /\$([$&`']|\d\d?)/g; + + var maybeToString = function (it) { + return it === undefined ? it : String(it); + }; + + // @@replace logic + _fixReWks('replace', 2, function (defined, REPLACE, $replace, maybeCallNative) { + return [ + // `String.prototype.replace` method + // https://tc39.github.io/ecma262/#sec-string.prototype.replace + 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); + }, + // `RegExp.prototype[@@replace]` method + // https://tc39.github.io/ecma262/#sec-regexp.prototype-@@replace + function (regexp, replaceValue) { + var res = maybeCallNative($replace, regexp, this, replaceValue); + if (res.done) return res.value; + + var rx = _anObject(regexp); + var S = String(this); + var functionalReplace = typeof replaceValue === 'function'; + if (!functionalReplace) replaceValue = String(replaceValue); + var global = rx.global; + if (global) { + var fullUnicode = rx.unicode; + rx.lastIndex = 0; + } + var results = []; + while (true) { + var result = _regexpExecAbstract(rx, S); + if (result === null) break; + results.push(result); + if (!global) break; + var matchStr = String(result[0]); + if (matchStr === '') rx.lastIndex = _advanceStringIndex(S, _toLength(rx.lastIndex), fullUnicode); + } + var accumulatedResult = ''; + var nextSourcePosition = 0; + for (var i = 0; i < results.length; i++) { + result = results[i]; + var matched = String(result[0]); + var position = max(min$1(_toInteger(result.index), S.length), 0); + var captures = []; + // NOTE: This is equivalent to + // captures = result.slice(1).map(maybeToString) + // but for some reason `nativeSlice.call(result, 1, result.length)` (called in + // the slice polyfill when slicing native arrays) "doesn't work" in safari 9 and + // causes a crash (https://pastebin.com/N21QzeQA) when trying to debug it. + for (var j = 1; j < result.length; j++) captures.push(maybeToString(result[j])); + var namedCaptures = result.groups; + if (functionalReplace) { + var replacerArgs = [matched].concat(captures, position, S); + if (namedCaptures !== undefined) replacerArgs.push(namedCaptures); + var replacement = String(replaceValue.apply(undefined, replacerArgs)); + } else { + replacement = getSubstitution(matched, S, position, captures, namedCaptures, replaceValue); + } + if (position >= nextSourcePosition) { + accumulatedResult += S.slice(nextSourcePosition, position) + replacement; + nextSourcePosition = position + matched.length; + } + } + return accumulatedResult + S.slice(nextSourcePosition); + } + ]; + + // https://tc39.github.io/ecma262/#sec-getsubstitution + function getSubstitution(matched, str, position, captures, namedCaptures, replacement) { + var tailPos = position + matched.length; + var m = captures.length; + var symbols = SUBSTITUTION_SYMBOLS_NO_NAMED; + if (namedCaptures !== undefined) { + namedCaptures = _toObject(namedCaptures); + symbols = SUBSTITUTION_SYMBOLS; + } + return $replace.call(replacement, symbols, function (match, ch) { + var capture; + switch (ch.charAt(0)) { + case '$': return '$'; + case '&': return matched; + case '`': return str.slice(0, position); + case "'": return str.slice(tailPos); + case '<': + capture = namedCaptures[ch.slice(1, -1)]; + break; + default: // \d\d? + var n = +ch; + if (n === 0) return match; + if (n > m) { + var f = floor$1(n / 10); + if (f === 0) return match; + if (f <= m) return captures[f - 1] === undefined ? ch.charAt(1) : captures[f - 1] + ch.charAt(1); + return match; + } + capture = captures[n - 1]; + } + return capture === undefined ? '' : capture; + }); + } + }); + + 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, maybeCallNative) { + return [ + // `String.prototype.match` method + // https://tc39.github.io/ecma262/#sec-string.prototype.match + 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)); + }, + // `RegExp.prototype[@@match]` method + // https://tc39.github.io/ecma262/#sec-regexp.prototype-@@match + function (regexp) { + var res = maybeCallNative($match, regexp, this); + if (res.done) return res.value; + var rx = _anObject(regexp); + var S = String(this); + if (!rx.global) return _regexpExecAbstract(rx, S); + var fullUnicode = rx.unicode; + rx.lastIndex = 0; + var A = []; + var n = 0; + var result; + while ((result = _regexpExecAbstract(rx, S)) !== null) { + var matchStr = String(result[0]); + A[n] = matchStr; + if (matchStr === '') rx.lastIndex = _advanceStringIndex(S, _toLength(rx.lastIndex), fullUnicode); + n++; + } + return n === 0 ? null : A; + } + ]; + }); + + // 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 = {}; + + // 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 max$1 = Math.max; + var min$2 = Math.min; + var _toAbsoluteIndex = function (index, length) { + index = _toInteger(index); + return index < 0 ? max$1(index + length, 0) : min$2(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$1 = _wks('toStringTag'); + + var _setToStringTag = function (it, tag, stat) { + if (it && !_has(it = stat ? it : it.prototype, TAG$1)) def(it, TAG$1, { 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'); + }; + + // 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); + } + } + + 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; + }; + + 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 /** @class */ (function () { + function class_1() { + this.__entries__ = []; + } + Object.defineProperty(class_1.prototype, "size", { + /** + * @returns {boolean} + */ + get: function () { + return this.__entries__.length; + }, + enumerable: true, + configurable: true + }); + /** + * @param {*} key + * @returns {*} + */ + class_1.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} + */ + class_1.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} + */ + class_1.prototype.delete = function (key) { + var entries = this.__entries__; + var index = getIndex(entries, key); + if (~index) { + entries.splice(index, 1); + } + }; + /** + * @param {*} key + * @returns {void} + */ + class_1.prototype.has = function (key) { + return !!~getIndex(this.__entries__, key); + }; + /** + * @returns {void} + */ + class_1.prototype.clear = function () { + this.__entries__.splice(0); + }; + /** + * @param {Function} callback + * @param {*} [ctx=null] + * @returns {void} + */ + class_1.prototype.forEach = function (callback, ctx) { + if (ctx === void 0) { ctx = null; } + for (var _i = 0, _a = this.__entries__; _i < _a.length; _i++) { + var entry = _a[_i]; + callback.call(ctx, entry[1], entry[0]); + } + }; + return class_1; + }()); + })(); + + /** + * 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} + */ + function throttle$1 (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 = /** @class */ (function () { + /** + * Creates a new instance of ResizeObserverController. + * + * @private + */ + function ResizeObserverController() { + /** + * Indicates whether DOM listeners have been added. + * + * @private {boolean} + */ + this.connected_ = false; + /** + * Tells that controller has subscribed for Mutation Events. + * + * @private {boolean} + */ + this.mutationEventsAdded_ = false; + /** + * Keeps reference to the instance of MutationObserver. + * + * @private {MutationObserver} + */ + this.mutationsObserver_ = null; + /** + * A list of connected observers. + * + * @private {Array} + */ + 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} + */ + 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 (_a) { + var _b = _a.propertyName, propertyName = _b === void 0 ? '' : _b; + // 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_; + }; + /** + * Holds reference to the controller's instance. + * + * @private {ResizeObserverController} + */ + ResizeObserverController.instance_ = null; + return ResizeObserverController; + }()); + + /** + * 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, _a = Object.keys(props); _i < _a.length; _i++) { + var key = _a[_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 = []; + for (var _i = 1; _i < arguments.length; _i++) { + positions[_i - 1] = arguments[_i]; + } + 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, positions_1 = positions; _i < positions_1.length; _i++) { + var position = positions_1[_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, 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(_a) { + var x = _a.x, y = _a.y, width = _a.width, height = _a.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 = /** @class */ (function () { + /** + * Creates an instance of ResizeObservation. + * + * @param {Element} target - Element to be observed. + */ + function ResizeObservation(target) { + /** + * Broadcasted width of content rectangle. + * + * @type {number} + */ + this.broadcastWidth = 0; + /** + * Broadcasted height of content rectangle. + * + * @type {number} + */ + this.broadcastHeight = 0; + /** + * Reference to the last observed content rectangle. + * + * @private {DOMRectInit} + */ + 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} + */ + 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; + }; + return ResizeObservation; + }()); + + var ResizeObserverEntry = /** @class */ (function () { + /** + * Creates an instance of ResizeObserverEntry. + * + * @param {Element} target - Element that is being observed. + * @param {DOMRectInit} rectInit - Data of the element's content rectangle. + */ + function ResizeObserverEntry(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 }); + } + return ResizeObserverEntry; + }()); + + var ResizeObserverSPI = /** @class */ (function () { + /** + * Creates a new instance of ResizeObserver. + * + * @param {ResizeObserverCallback} callback - Callback function that is invoked + * when one of the observed elements changes it's content dimensions. + * @param {ResizeObserverController} controller - Controller instance which + * is responsible for the updates of observer. + * @param {ResizeObserver} callbackCtx - Reference to the public + * ResizeObserver instance which will be passed to callback function. + */ + function ResizeObserverSPI(callback, controller, callbackCtx) { + /** + * Collection of resize observations that have detected changes in dimensions + * of elements. + * + * @private {Array} + */ + this.activeObservations_ = []; + /** + * Registry of the ResizeObservation instances. + * + * @private {Map} + */ + 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} + */ + 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 = this; + this.clearActive(); + this.observations_.forEach(function (observation) { + if (observation.isActive()) { + _this.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; + }; + return ResizeObserverSPI; + }()); + + // 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 = /** @class */ (function () { + /** + * Creates a new instance of ResizeObserver. + * + * @param {ResizeObserverCallback} callback - Callback that is invoked when + * dimensions of the observed elements change. + */ + function ResizeObserver(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); + } + return ResizeObserver; + }()); + // Expose public methods of ResizeObserver. + [ + 'observe', + 'unobserve', + 'disconnect' + ].forEach(function (method) { + ResizeObserver.prototype[method] = function () { + var _a; + return (_a = observers.get(this))[method].apply(_a, arguments); + }; + }); + + 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; + _this.axis.x.scrollbar.rect = _this.axis.x.scrollbar.el.getBoundingClientRect(); + _this.axis.y.scrollbar.rect = _this.axis.y.scrollbar.el.getBoundingClientRect(); + + 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; // We stop here on server-side + + if (canUseDom) { + this.initDOM(); + 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 || mutation.removedNodes.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() { + var _this4 = this; + + // Event listeners + if (this.options.autoHide) { + this.el.removeEventListener('mouseenter', this.onMouseEnter); + } + + ['mousedown', 'click', 'dblclick', 'touchstart', 'touchend', 'touchmove'].forEach(function (e) { + _this4.el.removeEventListener(e, _this4.onPointerEvent); + }); + this.el.removeEventListener('mousemove', this.onMouseMove); + this.el.removeEventListener('mouseleave', this.onMouseLeave); + this.contentEl.removeEventListener('scroll', this.onScroll); + window.removeEventListener('resize', this.onWindowResize); + this.mutationObserver && this.mutationObserver.disconnect(); + this.resizeObserver.disconnect(); // Cancel all debounced functions + + this.recalculate.cancel(); + this.onMouseMove.cancel(); + this.hideScrollbars.cancel(); + this.onWindowResize.cancel(); + } + /** + * 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', + 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_utils/static/src/img/module.png b/muk_web_utils/static/src/img/module.png new file mode 100644 index 0000000..74ca4c7 Binary files /dev/null and b/muk_web_utils/static/src/img/module.png differ diff --git a/muk_web_utils/static/src/js/core/async.js b/muk_web_utils/static/src/js/core/async.js new file mode 100644 index 0000000..9a026ab --- /dev/null +++ b/muk_web_utils/static/src/js/core/async.js @@ -0,0 +1,85 @@ +/********************************************************************************** +* +* Copyright (c) 2017-2019 MuK IT GmbH. +* +* This file is part of MuK Web Utils +* (see https://mukit.at). +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +* +**********************************************************************************/ + +odoo.define('muk_web_utils.async', function (require) { +"use strict"; + +var core = require('web.core'); + +var _t = core._t; +var QWeb = core.qweb; + +var syncLoop = function(items, func, callback) { + items.reduce(function(promise, item) { + return promise.then(func.bind(this, item)); + }, $.Deferred().resolve()).then(callback); +}; + +var syncProgress = function(items, func, callback, update) { + var progress = 0; + items.reduce(function(promise, item) { + return promise.then(function() { + update(++progress / items.length); + return func(item); + }); + }, $.Deferred().resolve()).then(callback); +}; + +var createNotification = function(widget, title) { + return widget.call('notification', 'notify', { + title: title || _t('Upload'), + message: _t('Uploading...'), + icon: 'fa-upload', + sticky: true, + progress: { + text: "0%", + state: 0.0, + }, + }); +}; + +var updateNotification = function(widget, notification, progress) { + widget.call('notification', 'progress', notification, { + text: (progress * 100).toFixed(2) + "%", + state: (progress * 100).toFixed(2), + }); +}; + +var closeNotification = function(widget, notification) { + widget.call('notification', 'close', notification); +}; + +var syncNotification = function(widget, title, items, func, callback) { + var notification = createNotification(widget, title); + var update = _.partial(updateNotification, widget, notification); + syncProgress(items, func, function() { + Promise.resolve(closeNotification(widget, notification)).then(callback); + }, update); +}; + +return { + syncLoop: syncLoop, + syncProgress: syncProgress, + syncNotification: syncNotification, +}; + +}); \ No newline at end of file diff --git a/muk_web_utils/static/src/js/core/dialog.js b/muk_web_utils/static/src/js/core/dialog.js new file mode 100644 index 0000000..efbb02a --- /dev/null +++ b/muk_web_utils/static/src/js/core/dialog.js @@ -0,0 +1,68 @@ +/********************************************************************************** +* +* Copyright (c) 2017-2019 MuK IT GmbH. +* +* This file is part of MuK Web Utils +* (see https://mukit.at). +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +* +**********************************************************************************/ + +odoo.define('muk_web_utils.Dialog', function (require) { +"use strict"; + +var core = require('web.core'); + +var Dialog = require('web.Dialog'); + +var QWeb = core.qweb; +var _t = core._t; + +Dialog.input = function (owner, title, options) { + var $content = $('
'); + var $input = $('', { + type: 'text', + class: options && options.input && options.input.class, + value: options && options.input && options.input.value, + }); + $content.append($input); + var confirm = function(event) { + if(options && options.confirm_callback) { + options.confirm_callback.call(self, event, $input.val()); + } + } + var buttons = [ + { + text: _t("Save"), + classes: 'btn-primary', + close: true, + click: confirm, + }, + { + text: _t("Cancel"), + close: true, + click: options && options.cancel_callback + } + ]; + return new Dialog(owner, _.extend({ + size: 'medium', + buttons: buttons, + $content: $content, + title: title, + }, options)).open({shouldFocusButtons:true}); +}; + + +}); diff --git a/muk_web_utils/static/src/js/core/dropzone.js b/muk_web_utils/static/src/js/core/dropzone.js new file mode 100644 index 0000000..8c12fce --- /dev/null +++ b/muk_web_utils/static/src/js/core/dropzone.js @@ -0,0 +1,132 @@ +/********************************************************************************** +* +* Copyright (c) 2017-2019 MuK IT GmbH. +* +* This file is part of MuK Web Utils +* (see https://mukit.at). +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +* +**********************************************************************************/ + +odoo.define('muk_web_utils.dropzone', function (require) { +"use strict"; + +var core = require('web.core'); + +var _t = core._t; +var QWeb = core.qweb; + +var DropzoneMixin = { + dropzoneData: {}, + dropzoneClasses: ['mk_dropzone'], + _checkDropzoneEvent: function(event) { + return true; + }, + _startDropzone: function($dropzone) { + if(this.$dropzone) { + this._destroyDropzone(); + } + this.$dropzone = $dropzone; + this.$dropzone.dndHover().on({ + 'dndHoverStart.dropzone': this._hoverDropzoneEnter.bind(this), + 'dndHoverEnd.dropzone': this._hoverDropzoneLeave.bind(this), + }); + this.$dropzone.on('dragenter.dropzone', this._dragenterDropzone.bind(this)); + this.$dropzone.on('dragover.dropzone', this._dragoverDropzone.bind(this)); + this.$dropzone.on('dragleave.dropzone', this._dragleaveDropzone.bind(this)); + this.$dropzone.on('drop.dropzone', this._dropDropzone.bind(this)); + _.each(this.dropzoneData, function(value, key) { + this.$dropzone.attr(key, value) + }, this); + }, + _destroyDropzone: function() { + if(this.$dropzone) { + this.$dropzone.off('.dropzone'); + this.$dropzone.dndHover('destroy'); + _.each(this.dropzoneData, function(value, key) { + this.$dropzone.removeAttr(key) + }, this); + this.$dropzone = false; + } + }, + _toggleDropzone: function(state) { + this.$dropzone.toggleClass(this.dropzoneClasses.join(" "), state); + }, + _hoverDropzoneEnter: function(event, originalEvent) { + if(this._checkDropzoneEvent(originalEvent)) { + this._toggleDropzone(true); + event.preventDefault(); + return false; + } + }, + _hoverDropzoneLeave: function(event, originalEvent) { + this._toggleDropzone(false); + event.stopPropagation(); + event.preventDefault(); + return false; + }, + _handleDrag: function(event) { + }, + _handleDrop: function(event) { + }, + _dragenterDropzone: function(event) { + if(this._checkDropzoneEvent(event)) { + event.preventDefault(); + } + }, + _dragoverDropzone: function(event) { + if(this._checkDropzoneEvent(event)) { + event.preventDefault(); + this._handleDrag(event); + } + }, + _dragleaveDropzone: function(event) { + if(this._checkDropzoneEvent(event)) { + event.preventDefault(); + } + }, + _dropDropzone: function(event) { + if(this._checkDropzoneEvent(event)) { + event.preventDefault(); +// event.stopPropagation(); + this._handleDrop(event); + } + } +}; + +var FileDropzoneMixin = _.extend({}, DropzoneMixin, { + dropzoneData: { + 'data-dropzone-text': _t("Drop files here to upload!"), + }, + dropzoneClasses: DropzoneMixin.dropzoneClasses.concat(['mk_dropzone_file']), + dropzoneCheck: window.File && window.FileReader && window.FileList && window.Blob, + _checkDropzoneEvent: function(event) { + var dataTransfer = event.originalEvent && event.originalEvent.dataTransfer; + var fileCheck = dataTransfer && _.some(dataTransfer.types, function(type) { + return type == "Files"; + }); + return this.dropzoneCheck && fileCheck; + }, + _handleDrag: function(event) { + event.originalEvent.dataTransfer.dropEffect = 'copy'; + }, +}); + +return { + DropzoneMixin: DropzoneMixin, + FileDropzoneMixin: FileDropzoneMixin, +}; + +}); \ No newline at end of file diff --git a/muk_web_utils/static/src/js/core/files.js b/muk_web_utils/static/src/js/core/files.js new file mode 100644 index 0000000..8839ab0 --- /dev/null +++ b/muk_web_utils/static/src/js/core/files.js @@ -0,0 +1,176 @@ +/********************************************************************************** +* +* Copyright (c) 2017-2019 MuK IT GmbH. +* +* This file is part of MuK Web Utils +* (see https://mukit.at). +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +* +**********************************************************************************/ + +odoo.define('muk_web_utils.files', function (require) { +"use strict"; + +var core = require('web.core'); + +var QWeb = core.qweb; +var _t = core._t; + +var traverseItems = function(items, tree) { + var def = $.Deferred(); + var files = []; + var defs = []; + _.each(items, function(item, index) { + var entry = item.webkitGetAsEntry(); + if(entry) { + var traverse = $.Deferred(); + traverseEntry(entry, tree).then(function(result) { + if(tree) { + files = files.concat(result); + } else { + files = _.union(files, result); + } + traverse.resolve(); + }); + defs.push(traverse); + } else { + var file = item.getAsFile(); + if(file) { + if(file.size) { + files.push(_.extend({}, file, {isFileItem: true})); + } + } else { + console.warn("Your browser doesn't support Drag and Drop!"); + } + } + }); + Promise.resolve().apply($, defs).then(function () { + def.resolve(files); + }); + return def; +}; + +var traverseEntries = function(entries, tree) { + var def = $.Deferred(); + var files = []; + var defs = []; + _.each(entries, function(entry, index) { + var traverse = $.Deferred(); + traverseEntry(entry, tree).then(function(result) { + if(tree) { + files = files.concat(result); + } else { + files = _.union(files, result); + } + traverse.resolve(); + }); + defs.push(traverse); + }); + Promise.resolve().apply($, defs).then(function () { + def.resolve(files); + }); + return def; +} + +var traverseEntry = function(entry, tree) { + var def = $.Deferred(); + if(entry.isFile) { + def.resolve([entry]); + } else if(entry.isDirectory) { + entry.createReader().readEntries(function (entries) { + traverseEntries(entries, tree).then(function (files) { + if(tree) { + def.resolve([{ + name: entry.name, + files: files, + isFile: false, + isDirectory: true, + childCount: files.length, + fullPath: entry.fullPath, + fileCount: _.reduce(files, function(sum, item) { + return item.isFile ? sum + 1 : sum + item.fileCount || 0; + }, 0), + }]); + } else { + def.resolve(files); + } + }); + }); + } else { + def.resolve([]); + } + return def; +}; + +var getFileTree = function(items, count) { + var traverse = traverseItems(items, true); + if(count) { + var def = $.Deferred(); + traverse.then(function(files) { + def.resolve({ + files: files, + count: _.reduce(files, function(sum, item) { + return item.isFile ? sum + 1 : sum + item.fileCount || 0; + }, 0), + }); + }); + return def; + } + return traverse; +}; + +var getFileList = function(items, count) { + var traverse = traverseItems(items, false); + if(count) { + var def = $.Deferred(); + traverse.then(function(files) { + def.resolve({ + files: files, + count: files.length, + }); + + }); + return def; + } + return traverse; +}; + +var loadFile = function(file, callback) { + var fileReader = new FileReader(); + fileReader.readAsDataURL(file); + fileReader.onloadend = callback; +}; + +var readFile = function(file, callback) { + if(file.isFile) { + file.file(function(file) { + loadFile(file, callback); + }); + } else { + loadFile(file, callback); + } +}; + +return { + traverseItems: traverseItems, + traverseEntries: traverseEntries, + traverseEntry: traverseEntry, + getFileTree: getFileTree, + getFileList: getFileList, + loadFile: loadFile, + readFile: readFile, +}; + +}); diff --git a/muk_web_utils/static/src/js/core/mimetype.js b/muk_web_utils/static/src/js/core/mimetype.js new file mode 100644 index 0000000..e874d30 --- /dev/null +++ b/muk_web_utils/static/src/js/core/mimetype.js @@ -0,0 +1,110 @@ +/********************************************************************************** +* +* Copyright (c) 2017-2019 MuK IT GmbH. +* +* This file is part of MuK Web Utils +* (see https://mukit.at). +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +* +**********************************************************************************/ + +odoo.define('muk_web_utils.mimetype', function (require) { +"use strict"; + +var core = require('web.core'); +var utils = require('web.utils'); + +var QWeb = core.qweb; +var _t = core._t; + +var mapping = [ + ['file-image-o', /^image\//], + ['file-audio-o', /^audio\//], + ['file-video-o', /^video\//], + ['file-pdf-o', 'application/pdf'], + ['file-text-o', 'text/plain'], + ['file-code-o', [ + 'text/html', + 'text/javascript', + 'application/javascript' + ]], + ['file-archive-o', [ + /^application\/x-(g?tar|xz|compress|bzip2|g?zip)$/, + /^application\/x-(7z|rar|zip)-compressed$/, + /^application\/(zip|gzip|tar)$/ + ]], + ['file-word-o', [ + /ms-?word/, 'application/vnd.oasis.opendocument.text', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' + ]], + ['file-powerpoint-o', [ + /ms-?powerpoint/, + 'application/vnd.openxmlformats-officedocument.presentationml.presentation' + ]], + ['file-excel-o', [ + /ms-?excel/, + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' + ]], + ['file-o' ] +]; + +function match(mimetype, cond) { + if (Array.isArray(cond)) { + return cond.reduce(function(v, c) { + return v || match(mimetype, c); + }, false); + } else if (cond instanceof RegExp) { + return cond.test(mimetype); + } else if (cond === undefined) { + return true; + } else { + return mimetype === cond; + } +} + +var cache = {}; +function resolve(mimetype) { + if (cache[mimetype]) { + return cache[mimetype]; + } + for (var i = 0; i < mapping.length; i++) { + if (match(mimetype, mapping[i][1])) { + cache[mimetype] = mapping[i][0]; + return mapping[i][0]; + } + } +} + +function mimetype2fa(mimetype, options) { + if (typeof mimetype === 'object') { + options = mimetype; + return function(mimetype) { + return mimetype2fa(mimetype, options); + }; + } else { + var icon = resolve(mimetype); + if (icon && options && options.prefix) { + return options.prefix + icon; + } else { + return icon; + } + } +} + +return { + mimetype2fa: mimetype2fa, +}; + +}); \ No newline at end of file diff --git a/muk_web_utils/static/src/js/core/utils.js b/muk_web_utils/static/src/js/core/utils.js new file mode 100644 index 0000000..b0c9734 --- /dev/null +++ b/muk_web_utils/static/src/js/core/utils.js @@ -0,0 +1,85 @@ +/********************************************************************************** +* +* Copyright (c) 2017-2019 MuK IT GmbH. +* +* This file is part of MuK Web Utils +* (see https://mukit.at). +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +* +**********************************************************************************/ + +odoo.define('muk_web_utils.utils', function (require) { +"use strict"; + +var core = require('web.core'); + +var _t = core._t; +var QWeb = core.qweb; + +var isUrl = function(string) { + var protocol = string.match(/^(?:\w+:)?\/\/(\S+)$/); + if (protocol && protocol[1]) { + var localHost = (/^localhost[\:?\d]*(?:[^\:?\d]\S*)?$/).test(protocol[1]); + var nonLocalHost = (/^localhost[\:?\d]*(?:[^\:?\d]\S*)?$/).test(protocol[1]); + return !!(localHost || nonLocalHost); + } + return false; +} + +var parseText2Html= function(text) { + return text + .replace(/((?:https?|ftp):\/\/[\S]+)/g,'$1 ') + .replace(/[\n\r]/g,'
'); +} + +var closedRange = function(start, end) { + return _.range(start, end + 1); +} + +var partitionPageList = function(pages, page, size) { + if (!size || size < 5) { + throw "The size must be at least 5 to partition the list."; + } + var sideSize = size < 9 ? 1 : 2; + var leftSize = (size - sideSize * 2 - 3) >> 1; + var rightSize = (size - sideSize * 2 - 2) >> 1; + if (pages <= size) { + return closedRange(1, pages); + } + if (page <= size - sideSize - 1 - rightSize) { + return closedRange(1, size - sideSize - 1) + .concat([false]) + .concat(closedRange(pages - sideSize + 1, pages)); + } + if (page >= pages - sideSize - 1 - rightSize) { + return closedRange(1, sideSize) + .concat([false]) + .concat(closedRange(pages - sideSize - 1 - rightSize - leftSize, pages)); + } + return closedRange(1, sideSize) + .concat([false]) + .concat(closedRange(page - leftSize, page + rightSize)) + .concat([false]) + .concat(closedRange(pages - sideSize + 1, pages)); +} + +return { + isUrl: isUrl, + closedRange: closedRange, + parseText2Html: parseText2Html, + partitionPageList: partitionPageList, +}; + +}); \ No newline at end of file diff --git a/muk_web_utils/static/src/js/fields/abstract.js b/muk_web_utils/static/src/js/fields/abstract.js new file mode 100644 index 0000000..39ac1bd --- /dev/null +++ b/muk_web_utils/static/src/js/fields/abstract.js @@ -0,0 +1,46 @@ +/********************************************************************************** +* +* Copyright (c) 2017-2019 MuK IT GmbH. +* +* This file is part of MuK Web Utils +* (see https://mukit.at). +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +* +**********************************************************************************/ + +odoo.define('muk_web_utils.abstract', function(require) { +"use strict"; + +var core = require('web.core'); +var session = require('web.session'); +var utils = require('web.field_utils'); +var fields = require('web.basic_fields'); +var registry = require('web.field_registry'); + +var AbstractField = require('web.AbstractField'); + +var _t = core._t; +var QWeb = core.qweb; + +AbstractField.include({ + isFocusable: function () { + if(!!this.attrs.skip_focus){ + return false; + } + return this._super.apply(this, arguments); + }, +}); + +}); diff --git a/muk_web_utils/static/src/js/fields/binary.js b/muk_web_utils/static/src/js/fields/binary.js new file mode 100644 index 0000000..188587d --- /dev/null +++ b/muk_web_utils/static/src/js/fields/binary.js @@ -0,0 +1,86 @@ +/********************************************************************************** +* +* Copyright (c) 2017-2019 MuK IT GmbH. +* +* This file is part of MuK Web Utils +* (see https://mukit.at). +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +* +**********************************************************************************/ + +odoo.define('muk_web_utils.binary', function(require) { +"use strict"; + +var core = require('web.core'); +var session = require('web.session'); +var utils = require('web.field_utils'); +var fields = require('web.basic_fields'); +var registry = require('web.field_registry'); + +var _t = core._t; +var QWeb = core.qweb; + +fields.FieldBinaryFile.extend({ + willStart: function () { + var def = this._rpc({ + route: '/config/muk_web_utils.binary_max_size', + }).then(function(result) { + this.max_upload_size = result.max_upload_size * 1024 * 1024; + }.bind(this)); + return this._super.apply(this, arguments); + }, + _renderReadonly: function () { + this._super.apply(this, arguments); + var $wrapper = $('
', { + class: "mk_field_binary_wrapper" + }); + $wrapper.addClass(this.$el.attr('class')); + this.$el.removeClass("o_field_widget"); + this.$el.removeClass("o_hidden"); + $wrapper.append(this.$el); + this.setElement($wrapper); + }, + _renderEdit: function () { + this._super.apply(this, arguments); + if (this.nodeOptions && this.nodeOptions.accept) { + this.$('input[name="ufile"]').prop("accept", this.nodeOptions.accept); + } + }, +}); + +var FieldBinarySize = fields.FieldFloat.extend({ + init: function(parent, name, record) { + this._super.apply(this, arguments); + this.nodeOptions = _.defaults(this.nodeOptions, { + si: true, + }); + }, + _formatValue: function (value) { + var options = _.extend({}, + this.nodeOptions, + { data: this.recordData }, + this.formatOptions + ); + return utils.format['binary_size'](value, this.field, options) + }, +}); + +registry.add('binary_size', FieldBinarySize); + +return { + FieldBinarySize: FieldBinarySize, +}; + +}); diff --git a/muk_web_utils/static/src/js/fields/color.js b/muk_web_utils/static/src/js/fields/color.js new file mode 100644 index 0000000..fad9160 --- /dev/null +++ b/muk_web_utils/static/src/js/fields/color.js @@ -0,0 +1,130 @@ +/********************************************************************************** +* +* Copyright (c) 2017-2019 MuK IT GmbH. +* +* This file is part of MuK Web Utils +* (see https://mukit.at). +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +* +**********************************************************************************/ + +odoo.define('muk_web_utils.color', function (require) { +"use strict"; + +var core = require('web.core'); +var fields = require('web.basic_fields'); +var registry = require('web.field_registry'); +var colorpicker = require('web.colorpicker'); + +var AbstractField = require('web.AbstractField'); + +var _t = core._t; +var QWeb = core.qweb; + +var FieldColor = fields.InputField.extend({ + events: _.extend({}, fields.InputField.prototype.events, { + "click .mk_field_color_button": "_onCustomColorButtonClick", + }), + template: "muk_web_utils.FieldColor", + supportedFieldTypes: ['char'], + start: function() { + this.$input = this.$('.mk_field_color_input'); + return this._super.apply(this, arguments); + }, + _renderEdit: function () { + this.$('.mk_field_color_input').val( + this._formatValue(this.value) + ); + this.$('.mk_field_color_input').css({ + 'background-color': this._formatValue(this.value), + }); + }, + _renderReadonly: function () { + this.$el.text(this._formatValue(this.value)); + this.$el.css({'color': this._formatValue(this.value)}); + }, + _doAction: function() { + this._super.apply(this, arguments); + this.$('.mk_field_color_input').css({ + 'background-color': this._getValue(), + }); + }, + _formatValue: function (value) { + return value; + }, + _parseValue: function (value) { + if((/(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i).test(value)) { + return value; + } else { + throw new Error(_.str.sprintf(_t("'%s' is not a correct color value"), value)); + } + }, + _onCustomColorButtonClick: function () { + var ColorpickerDialog = new colorpicker(this, { + dialogClass: 'mk_field_color_picker', + defaultColor: this._getValue(), + }); + ColorpickerDialog.on('colorpicker:saved', this, function (event) { + this.$input.val(event.data.hex); + this._doAction(); + }); + ColorpickerDialog.open(); + }, +}); + +var FieldColorIndex = AbstractField.extend({ + events: _.extend({}, AbstractField.prototype.events, { + 'change': '_onChange', + }), + template: 'muk_web_utils.FieldColorIndex', + supportedFieldTypes: ['integer'], + isSet: function () { + return this.value === 0 || this._super.apply(this, arguments); + }, + getFocusableElement: function () { + return this.$el.is('select') ? this.$el : $(); + }, + _renderEdit: function () { + this.$el.addClass('mk_color_index_' + this.value); + this.$('option[value="' + this.value + '"]').prop('selected', true); + }, + _renderReadonly: function () { + this.$el.addClass('mk_color_index_' + this.value); + this.$el.empty().text('Color ' + this._formatValue(this.value)); + }, + _onChange: function (event) { + this.$el.removeClass(function (index, className) { + return (className.match (/(^|\s)mk_color_index_\S+/g) || []).join(' '); + }); + this.$el.addClass('mk_color_index_' + this.$el.val()); + this._setValue(this.$el.val()); + }, + _parseValue: function (value) { + if(0 > value || value > 12) { + throw new Error(_.str.sprintf(_t("'%s' is not a correct color index (0-12)"), value)); + } + return value; + }, +}); + +registry.add('color_char', FieldColor); +registry.add('color_index', FieldColorIndex); + +return { + FieldColor: FieldColor, + FieldColorIndex: FieldColorIndex, +}; + +}); \ No newline at end of file diff --git a/muk_web_utils/static/src/js/fields/copy.js b/muk_web_utils/static/src/js/fields/copy.js new file mode 100644 index 0000000..07c5eef --- /dev/null +++ b/muk_web_utils/static/src/js/fields/copy.js @@ -0,0 +1,135 @@ +/********************************************************************************** +* +* Copyright (c) 2017-2019 MuK IT GmbH. +* +* This file is part of MuK Web Utils +* (see https://mukit.at). +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +* +**********************************************************************************/ + +odoo.define('muk_web_utils.copy', function (require) { +"use strict"; + +var core = require('web.core'); +var session = require('web.session'); +var fields = require('web.basic_fields'); +var registry = require('web.field_registry'); + +var _t = core._t; +var QWeb = core.qweb; + +var BinaryFileCopy = fields.FieldBinaryFile.extend({ + init: function () { + this._super.apply(this, arguments); + if (!this.field.attachment) { + throw _.str.sprintf(_t( + "The field '%s' must be a binary field with an set " + + "attachment flag for the share widget to work." + ), this.field.string); + } + this.accessToken = !!this.nodeOptions.token; + }, + willStart: function() { + var def = this.value && this.res_id ? this._fetchShareUrl() : Promise.resolve(); + return Promise.resolve(this._super.apply(this, arguments), def); + }, + _fetchShareUrl: function() { + var self = this; + var def = $.Deferred(); + if (this.accessToken) { + this._rpc({ + model: 'ir.attachment', + method: 'search', + args: [[ + ['res_id', '=', this.res_id], + ['res_field', '=', this.name], + ['res_model', '=', this.model], + ]], + kwargs: { + context: session.user_context, + }, + }).then(function(attchments) { + self._rpc({ + model: 'ir.attachment', + method: 'generate_access_token', + args: attchments + }).then(function(access_token) { + self.shareUrl = session.url('/web/content', { + model: self.model, + field: self.name, + id: self.res_id, + access_token: access_token.shift(), + }); + def.resolve(); + }); + }); + } else { + this.shareUrl = session.url('/web/content', { + model: self.model, + field: self.name, + id: self.res_id, + }); + def.resolve(); + } + return def; + }, + _setUpClipboad: function() { + var self = this; + var $clipboardBtn = this.$('.mk_copy_binary'); + this.clipboard = new ClipboardJS($clipboardBtn[0], { + text: function (trigger) { + return self.shareUrl; + }, + container: self.$el[0] + }); + this.clipboard.on('success', function (event) { + _.defer(function () { + $clipboardBtn.tooltip('show'); + _.delay(function () { + $clipboardBtn.tooltip('hide'); + }, 800); + }); + }); + $clipboardBtn.click(function(event) { + event.stopPropagation(); + }); + $clipboardBtn.tooltip({ + title: _t('Link Copied!'), + trigger: 'manual', + placement: 'bottom' + }); + }, + _renderReadonly: function () { + this._super.apply(this, arguments); + this.$el.addClass('mk_field_copy'); + this.$el.append($(QWeb.render('muk_web_utils.BinaryFieldCopy'))); + this._setUpClipboad(); + }, + destroy: function () { + this._super.apply(this, arguments); + if (this.clipboard) { + this.clipboard.destroy(); + } + }, +}); + +registry.add('copy_binary', BinaryFileCopy); + +return { + BinaryFileCopy: BinaryFileCopy, +}; + +}); \ No newline at end of file diff --git a/muk_web_utils/static/src/js/fields/domain.js b/muk_web_utils/static/src/js/fields/domain.js new file mode 100644 index 0000000..4204f42 --- /dev/null +++ b/muk_web_utils/static/src/js/fields/domain.js @@ -0,0 +1,54 @@ +/********************************************************************************** +* +* Copyright (c) 2017-2019 MuK IT GmbH. +* +* This file is part of MuK Web Utils +* (see https://mukit.at). +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +* +**********************************************************************************/ + +odoo.define('muk_web_utils.domain', function (require) { +"use strict"; + +var core = require('web.core'); +var session = require('web.session'); +var fields = require('web.basic_fields'); +var view_dialogs = require('web.view_dialogs'); + +var _t = core._t; +var QWeb = core.qweb; + +fields.FieldDomain.include({ + _onShowSelectionButtonClick: function (e) { + e.preventDefault(); + new view_dialogs.SelectCreateDialog(this, { + context: this.attrs.context || {}, + title: _t("Selected records"), + res_model: this._domainModel, + domain: this.value || "[]", + no_create: true, + readonly: true, + disable_multiple_selection: true, + }).open(); + }, + isValid: function () { + return ( + this._isValid && (!this.domainSelector || this.domainSelector.isValid()) + ); + }, +}); + +}); diff --git a/muk_web_utils/static/src/js/fields/image.js b/muk_web_utils/static/src/js/fields/image.js new file mode 100644 index 0000000..c8907e3 --- /dev/null +++ b/muk_web_utils/static/src/js/fields/image.js @@ -0,0 +1,58 @@ +/********************************************************************************** +* +* Copyright (c) 2017-2019 MuK IT GmbH. +* +* This file is part of MuK Web Utils +* (see https://mukit.at). +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +* +**********************************************************************************/ + +odoo.define('muk_web_utils.image', function (require) { +"use strict"; + +var core = require('web.core'); +var session = require('web.session'); +var fields = require('web.basic_fields'); + +var _t = core._t; +var QWeb = core.qweb; + +fields.FieldBinaryImage.extend({ + willStart: function () { + var def = this._rpc({ + route: '/config/muk_web_utils.binary_max_size', + }).then(function(result) { + this.max_upload_size = result.max_upload_size * 1024 * 1024; + }.bind(this)); + return this._super.apply(this, arguments); + }, + _render: function () { + this._super.apply(this, arguments); + this.$('.mk_field_image_wrapper').remove(); + this.$('img').wrap($('
', { + class: "mk_field_image_wrapper" + })); + var $wrapper = $('.mk_field_image_wrapper'); + var width = this.nodeOptions.size ? + this.nodeOptions.size[0] : this.attrs.width; + var height = this.nodeOptions.size ? + this.nodeOptions.size[1] : this.attrs.height; + $wrapper.css('min-width', (width || 50) + 'px'); + $wrapper.css('min-height', (height || 50) + 'px'); + }, +}); + +}); diff --git a/muk_web_utils/static/src/js/fields/module.js b/muk_web_utils/static/src/js/fields/module.js new file mode 100644 index 0000000..59eaed9 --- /dev/null +++ b/muk_web_utils/static/src/js/fields/module.js @@ -0,0 +1,96 @@ +/********************************************************************************** +* +* Copyright (c) 2017-2019 MuK IT GmbH. +* +* This file is part of MuK Web Utils +* (see https://mukit.at). +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +* +**********************************************************************************/ + +odoo.define('muk_web_utils.ModuleBoolean', function (require) { +"use strict"; + +var core = require('web.core'); +var fields = require('web.basic_fields'); +var registry = require('web.field_registry'); +var framework = require('web.framework'); + +var Dialog = require('web.Dialog'); +var AbstractField = require('web.AbstractField'); + +var _t = core._t; +var QWeb = core.qweb; + +var ModuleBoolean = fields.FieldBoolean.extend({ + supportedFieldTypes: [], + events: _.extend({}, AbstractField.prototype.events, { + 'click input': '_onInputClicked', + }), + renderWithLabel: function ($label) { + this.$label = $label; + this._render(); + }, + _openDialog: function () { + var buttons = [{ + text: _t("Download"), + classes: 'btn-primary', + close: true, + click: this._confirmRedirect.bind(this), + }, { + text: _t("Cancel"), + close: true, + }]; + return new Dialog(this, { + size: 'medium', + buttons: buttons, + $content: $('
', { + html: $(QWeb.render('muk_web_utils.MissingModuleDialog')), + }), + title: _t("Missing Module"), + }).open(); + }, + _confirmRedirect: function () { + if(this.nodeOptions.url) { + framework.redirect(this.nodeOptions.url); + } else { + var module = this.name.replace("module_", ""); + framework.redirect("https://apps.odoo.com/apps/modules/browse?search=" + module); + } + }, + _render: function () { + this._super.apply(this, arguments); + var $element = this.$label || this.$el; + $element.append(' ').append($("", { + 'text': _t("Store"), + 'class': "badge badge-primary oe_inline mk_module_label" + })); + }, + _onInputClicked: function (event) { + if ($(event.currentTarget).prop("checked")) { + var dialog = this._openDialog(); + dialog.on('closed', this, this._resetValue.bind(this)); + } + }, + _resetValue: function () { + this.$input.prop("checked", false).change(); + }, +}); + +registry.add('module_boolean', ModuleBoolean); + +return ModuleBoolean; + +}); \ No newline at end of file diff --git a/muk_web_utils/static/src/js/fields/path.js b/muk_web_utils/static/src/js/fields/path.js new file mode 100644 index 0000000..ffc2bc1 --- /dev/null +++ b/muk_web_utils/static/src/js/fields/path.js @@ -0,0 +1,119 @@ +/********************************************************************************** +* +* Copyright (c) 2017-2019 MuK IT GmbH. +* +* This file is part of MuK Web Utils +* (see https://mukit.at). +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +* +**********************************************************************************/ + +odoo.define('muk_web_utils.path', function (require) { +"use strict"; + +var core = require('web.core'); +var fields = require('web.basic_fields'); +var registry = require('web.field_registry'); +var colorpicker = require('web.colorpicker'); + +var AbstractField = require('web.AbstractField'); + +var _t = core._t; +var QWeb = core.qweb; + +var FieldPathNames = fields.FieldChar.extend({ + init: function(parent, name, record) { + this._super.apply(this, arguments); + this.max_width = this.nodeOptions.width || 500; + }, + _renderReadonly: function() { + var show_value = this._formatValue(this.value); + var text_witdh = $.fn.textWidth(show_value); + if(text_witdh >= this.max_width) { + var ratio_start = (1 - (this.max_width / text_witdh)) * show_value.length; + show_value = ".." + show_value.substring(ratio_start, show_value.length); + } + this.$el.text(show_value); + }, +}); + +var FieldPathJson = fields.FieldText.extend({ + events: _.extend({}, fields.FieldText.prototype.events, { + 'click a' : '_onNodeClicked', + }), + init: function(parent, name, record) { + this._super.apply(this, arguments); + this.max_width = this.nodeOptions.width || 500; + this.seperator = this.nodeOptions.seperator || "/"; + this.prefix = this.nodeOptions.prefix || false; + this.suffix = this.nodeOptions.suffix || false; + }, + _renderReadonly: function() { + this.$el.empty(); + this._renderPath(); + }, + _renderPath: function() { + var text_width_measure = ""; + var path = JSON.parse(this.value || "[]"); + $.each(_.clone(path).reverse(), function(index, element) { + text_width_measure += element.name + "/"; + if($.fn.textWidth(text_width_measure) >= this.max_width) { + this.$el.prepend($('').text("..")); + } else { + if (index == 0) { + if(this.suffix) { + this.$el.prepend($('').text(this.seperator)); + } + this.$el.prepend($('').text(element.name)); + this.$el.prepend($('').text(this.seperator)); + } else { + this.$el.prepend($('', { + 'class': 'oe_form_uri', + 'data-model': element.model, + 'data-id': element.id, + 'href': "javascript:void(0);", + 'text': element.name, + })); + if (index != path.length - 1) { + this.$el.prepend($('').text(this.seperator)); + } else if (this.prefix) { + this.$el.prepend($('').text(this.seperator)); + } + } + } + return ($.fn.textWidth(text_width_measure) < this.max_width); + }.bind(this)); + }, + _onNodeClicked : function(event) { + this.do_action({ + type: 'ir.actions.act_window', + res_model: $(event.currentTarget).data('model'), + res_id: $(event.currentTarget).data('id'), + views: [[false, 'form']], + target: 'current', + context: {}, + }); + } +}); + +registry.add('path_names', FieldPathNames); +registry.add('path_json', FieldPathJson); + +return { + FieldPathNames: FieldPathNames, + FieldPathJson: FieldPathJson, +}; + +}); \ No newline at end of file diff --git a/muk_web_utils/static/src/js/fields/share.js b/muk_web_utils/static/src/js/fields/share.js new file mode 100644 index 0000000..c25564b --- /dev/null +++ b/muk_web_utils/static/src/js/fields/share.js @@ -0,0 +1,226 @@ +/********************************************************************************** +* +* Copyright (c) 2017-2019 MuK IT GmbH. +* +* This file is part of MuK Web Utils +* (see https://mukit.at). +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +* +**********************************************************************************/ + +odoo.define('muk_web_utils.share', function (require) { +"use strict"; + +var core = require('web.core'); +var session = require('web.session'); +var fields = require('web.basic_fields'); +var registry = require('web.field_registry'); + +var utils = require('muk_web_utils.utils'); +var copy = require('muk_web_utils.copy'); + +var _t = core._t; +var QWeb = core.qweb; + +var ShareMixin = { + shareEvents: { + 'click .mk_share_dropdown_message': '_onShareMessageClick', + 'click .mk_share_dropdown_note': '_onShareNoteClick', + 'click .mk_share_dropdown_mail': '_onShareMailClick', + 'click .mk_share_dropdown_send': '_onShareSendClick', + }, + getShareMessageValues: function(message) { + var values = { + name: session.partner_display_name, + record: this.recordData.display_name, + url: utils.isUrl(this.value) && this.value, + value: this.value, + }; + return { + subject: _.template(this.shareOptions.subjectTemplate)(values), + body: QWeb.render(this.shareOptions.bodyTemplate, values), + text: _.template(this.shareOptions.textTemplate)(values), + url: utils.isUrl(this.value) && this.value, + } + }, + openShareChat: function(note) { + var values = this.getShareMessageValues(); + var context = { + default_is_log: note, + default_body: values.body, + default_subject: values.subject, + default_model: this.shareOptions.res_model, + default_res_id: this.shareOptions.res_id, + mail_post_autofollow: false, + }; + this.do_action({ + type: 'ir.actions.act_window', + res_model: 'mail.compose.message', + view_mode: 'form', + views: [[false, 'form']], + target: 'new', + context: context, + }); + }, + _onShareMessageClick: function(event) { + event.preventDefault(); + event.stopPropagation(); + this.openShareChat(false); + }, + _onShareNoteClick: function(event) { + event.preventDefault(); + event.stopPropagation(); + this.openShareChat(true); + }, + _onShareMailClick: function(event) { + event.preventDefault(); + event.stopPropagation(); + var values = this.getShareMessageValues(); + var subject = "subject=" + values.subject; + var body = "&body=" + encodeURIComponent(values.text); + window.location.href = "mailto:?" + subject + body; + }, + _onShareSendClick: function(event) { + event.preventDefault(); + event.stopPropagation(); + var values = this.getShareMessageValues(); + navigator.share({ + title: values.subject, + text: values.text, + url: values.url, + }); + }, +}; + +var CharShare = fields.CharCopyClipboard.extend(ShareMixin, { + fieldDependencies: _.extend({}, fields.CharCopyClipboard.prototype.fieldDependencies, { + display_name: {type: 'char'}, + }), + events: _.extend({}, fields.CharCopyClipboard.prototype.events, ShareMixin.shareEvents), + init: function(parent, name, record) { + this._super.apply(this, arguments); + this.navigator = window.navigator.share; + this.chatter = _.contains(odoo._modules, "mail"); + this.shareOptions = _.defaults(this.nodeOptions, { + subjectTemplate: _t("<%= name %> shared a message!"), + textTemplate: _t("<%= value %>"), + bodyTemplate: 'muk_web_utils.ShareMessage', + }); + this.shareOptions = _.extend({}, this.shareOptions, { + res_model: this.recordData[this.nodeOptions.res_model] || this.model, + res_id: this.recordData[this.nodeOptions.res_id] || this.res_id, + }); + }, + _render: function() { + this._super.apply(this, arguments); + this.$el.addClass('mk_field_share'); + this.$el.prepend($(QWeb.render('muk_web_utils.CharShare', { + navigator: !!this.navigator, + chatter: !!this.chatter, + }))); + }, +}); + +var TextShare = fields.TextCopyClipboard.extend(ShareMixin, { + fieldDependencies: _.extend({}, fields.TextCopyClipboard.prototype.fieldDependencies, { + display_name: {type: 'char'}, + }), + events: _.extend({}, fields.TextCopyClipboard.prototype.events, ShareMixin.shareEvents), + init: function(parent, name, record) { + this._super.apply(this, arguments); + this.navigator = window.navigator.share; + this.chatter = _.contains(odoo._modules, "mail"); + this.shareOptions = _.defaults(this.nodeOptions, { + subjectTemplate: _t("<%= name %> shared a message!"), + textTemplate: _t("<%= value %>"), + bodyTemplate: 'muk_web_utils.ShareMessage', + }); + this.shareOptions = _.extend({}, this.shareOptions, { + res_model: this.recordData[this.nodeOptions.res_model] || this.model, + res_id: this.recordData[this.nodeOptions.res_id] || this.res_id, + }); + }, + _render: function() { + this._super.apply(this, arguments); + this.$el.addClass('mk_field_share'); + this.$el.prepend($(QWeb.render('muk_web_utils.TextShare', { + navigator: !!this.navigator, + chatter: !!this.chatter, + }))); + } +}); + +var BinaryFileShare = copy.BinaryFileCopy.extend(ShareMixin, { + fieldDependencies: _.extend({}, fields.FieldBinaryFile.prototype.fieldDependencies, { + display_name: {type: 'char'}, + }), + events: _.extend({}, copy.BinaryFileCopy.prototype.events, ShareMixin.shareEvents, { + 'click .mk_share_button': '_onShareDropdownClick', + }), + init: function () { + this._super.apply(this, arguments); + this.navigator = window.navigator.share; + this.chatter = _.contains(odoo._modules, "mail"); + this.shareOptions = _.defaults(this.nodeOptions, { + subjectTemplate: _t("<%= name %> shared a file!"), + textTemplate: _t("<%= value %>"), + bodyTemplate: 'muk_web_utils.ShareBinaryMessage', + }); + this.shareOptions = _.extend({}, this.shareOptions, { + res_model: this.recordData[this.nodeOptions.res_model] || this.model, + res_id: this.recordData[this.nodeOptions.res_id] || this.res_id, + }); + }, + getShareMessageValues: function() { + var values = { + name: session.partner_display_name, + record: this.recordData.display_name, + url: this.shareUrl, + value: this.shareUrl, + }; + return { + subject: _.template(this.shareOptions.subjectTemplate)(values), + body: QWeb.render(this.shareOptions.bodyTemplate, values), + text: _.template(this.shareOptions.textTemplate)(values), + url: this.shareUrl, + } + }, + _renderReadonly: function () { + this._super.apply(this, arguments); + this.$el.addClass('mk_field_share'); + this.$el.append($(QWeb.render('muk_web_utils.BinaryShare', { + navigator: !!this.navigator, + chatter: !!this.chatter, + share: !!this.shareUrl, + }))); + }, + _onShareDropdownClick: function(event) { + $(event.currentTarget).dropdown("toggle"); + event.stopPropagation(); + }, +}); + +registry.add('share_char', CharShare); +registry.add('share_text', TextShare); +registry.add('share_binary', BinaryFileShare); + +return { + ShareMixin: ShareMixin, + CharShare: CharShare, + TextShare: TextShare, + BinaryFileShare: BinaryFileShare, +}; + +}); \ No newline at end of file diff --git a/muk_web_utils/static/src/js/fields/utils.js b/muk_web_utils/static/src/js/fields/utils.js new file mode 100644 index 0000000..001acdd --- /dev/null +++ b/muk_web_utils/static/src/js/fields/utils.js @@ -0,0 +1,54 @@ +/********************************************************************************** +* +* Copyright (c) 2017-2019 MuK IT GmbH. +* +* This file is part of MuK Web Utils +* (see https://mukit.at). +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +* +**********************************************************************************/ + +odoo.define('muk_web_utils.field_utils', function(require) { +"use strict"; + +var core = require('web.core'); +var session = require('web.session'); +var utils = require('web.field_utils'); + +var _t = core._t; +var QWeb = core.qweb; + +function formatBinarySize(value, field, options) { + options = _.defaults(options || {}, { + si: true, + }); + var thresh = options.si ? 1000 : 1024; + if(Math.abs(value) < thresh) { + return utils.format['float'](value, field, options) + ' B'; + } + var units = options.si + ? ['KB','MB','GB','TB','PB','EB','ZB','YB'] + : ['KiB','MiB','GiB','TiB','PiB','EiB','ZiB','YiB']; + var unit = -1; + do { + value /= thresh; + ++unit; + } while(Math.abs(value) >= thresh && unit < units.length - 1); + return utils.format['float'](value, field, options) + ' ' + units[unit]; +} + +utils.format.binary_size = formatBinarySize; + +}); diff --git a/muk_web_utils/static/src/js/libs/jquery.js b/muk_web_utils/static/src/js/libs/jquery.js new file mode 100644 index 0000000..84b3314 --- /dev/null +++ b/muk_web_utils/static/src/js/libs/jquery.js @@ -0,0 +1,104 @@ +/********************************************************************************** +* +* Copyright (c) 2017-2019 MuK IT GmbH. +* +* This file is part of MuK Web Utils +* (see https://mukit.at). +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +* +**********************************************************************************/ + +$.fn.textWidth = function(text, font) { + if (!$.fn.textWidth.fakeEl) $.fn.textWidth.fakeEl = $('').hide().appendTo(document.body); + $.fn.textWidth.fakeEl.text(text || this.val() || this.text()).css('font', font || this.css('font')); + return $.fn.textWidth.fakeEl.width(); +}; + +$.fn.dndHover = function(options) { + return this.each(function() { + var self = $(this); + var collection = $(); + var dragenter = function(event) { + if (collection.size() === 0) { + self.trigger('dndHoverStart', [event]); + } + collection = collection.add(event.target); + }; + var dragleave = function(event) { + setTimeout(function() { + collection = collection.not(event.target); + if (collection.size() === 0) { + self.trigger('dndHoverEnd', [event]); + } + }, 1); + }; + var drop = function(event) { + setTimeout(function() { + collection = $(); + self.trigger('dndHoverEnd', [event]); + }, 1); + }; + if(options && options === 'destroy') { + self.off('dragenter.dnd_hover'); + self.off('dragleave.dnd_hover'); + self.off('drop.dnd_hover'); + } else { + self.on('dragenter.dnd_hover', dragenter); + self.on('dragleave.dnd_hover', dragleave); + self.on('drop.dnd_hover', drop); + } + }); +}; + +$.ajaxTransport("+binary", function(options, originalOptions, jqXHR) { + if (window.FormData && ((options.dataType && (options.dataType == 'binary')) || + (options.data && ((window.ArrayBuffer && options.data instanceof ArrayBuffer) || + (window.Blob && options.data instanceof Blob))))) { + return { + send: function(headers, callback){ + var xhr = new XMLHttpRequest(); + var url = options.url, + type = options.type, + async = options.async || true, + dataType = options.responseType || 'blob', + data = options.data || null, + username = options.username, + password = options.password; + xhr.addEventListener('load', function(){ + var data = {}; + data[options.dataType] = xhr.response; + callback(xhr.status, xhr.statusText, data, xhr.getAllResponseHeaders()); + }); + xhr.open(type, url, async, username, password); + for (var i in headers ) { + xhr.setRequestHeader(i, headers[i] ); + } + if (options.xhrFields) { + for (var key in options.xhrFields) { + if (key in xhr) { + xhr[key] = options.xhrFields[key]; + } + } + } + xhr.responseType = dataType; + xhr.send(data); + }, + abort: function(){ + jqXHR.abort(); + } + }; + } +}); + diff --git a/muk_web_utils/static/src/js/libs/scrollbar.js b/muk_web_utils/static/src/js/libs/scrollbar.js new file mode 100644 index 0000000..2c889fd --- /dev/null +++ b/muk_web_utils/static/src/js/libs/scrollbar.js @@ -0,0 +1,27 @@ +/********************************************************************************** +* +* Copyright (c) 2017-2019 MuK IT GmbH. +* +* This file is part of MuK Web Utils +* (see https://mukit.at). +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +* +**********************************************************************************/ + +$.fn.renderScrollBar = function() { + this.each(function() { + new SimpleBar(this); + }); +}; diff --git a/muk_web_utils/static/src/js/libs/underscore.js b/muk_web_utils/static/src/js/libs/underscore.js new file mode 100644 index 0000000..a8a4aa8 --- /dev/null +++ b/muk_web_utils/static/src/js/libs/underscore.js @@ -0,0 +1,47 @@ +/********************************************************************************** +* +* Copyright (c) 2017-2019 MuK IT GmbH. +* +* This file is part of MuK Web Utils +* (see https://mukit.at). +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +* +**********************************************************************************/ + +_.mixin({ + memoizeDebounce: function(func, wait, options) { + wait = (typeof wait !== 'undefined') ? wait : 0; + options = (typeof options !== 'undefined') ? options : {}; + var mem = _.memoize(function() { + return _.debounce(func, wait, options) + }, options.resolver); + return function() { + mem.apply(this, arguments).apply(this, arguments) + } + } +}); + +_.mixin({ + memoizeThrottle: function(func, wait, options) { + wait = (typeof wait !== 'undefined') ? wait : 0; + options = (typeof options !== 'undefined') ? options : {}; + var mem = _.memoize(function() { + return _.throttle(func, wait, options) + }, options.resolver); + return function() { + mem.apply(this, arguments).apply(this, arguments) + } + } +}); \ No newline at end of file diff --git a/muk_web_utils/static/src/js/services/notification_service.js b/muk_web_utils/static/src/js/services/notification_service.js new file mode 100644 index 0000000..8f5787e --- /dev/null +++ b/muk_web_utils/static/src/js/services/notification_service.js @@ -0,0 +1,37 @@ +/********************************************************************************** +* +* Copyright (c) 2017-2019 MuK IT GmbH. +* +* This file is part of MuK Web Utils +* (see https://mukit.at). +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +* +**********************************************************************************/ + +odoo.define('muk_web_utils.NotificationService', function (require) { +"use strict"; + +var NotificationService = require('web.NotificationService'); + +NotificationService.include({ + progress: function (notificationId, progress) { + if(notificationId in this.notifications) { + var notification = this.notifications[notificationId]; + notification.updateProgress(progress.state, progress.text); + } + }, +}); + +}); diff --git a/muk_web_utils/static/src/js/views/form/renderer.js b/muk_web_utils/static/src/js/views/form/renderer.js new file mode 100644 index 0000000..496211a --- /dev/null +++ b/muk_web_utils/static/src/js/views/form/renderer.js @@ -0,0 +1,47 @@ +/********************************************************************************** +* +* Copyright (c) 2017-2019 MuK IT GmbH. +* +* This file is part of MuK Web Utils +* (see https://mukit.at). +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +* +**********************************************************************************/ + +odoo.define('muk_web_utils.FormRenderer', function (require) { +"use strict"; + +var core = require('web.core'); + +var FormRenderer = require('web.FormRenderer'); + +var _t = core._t; +var QWeb = core.qweb; + +FormRenderer.include({ + _updateView: function ($newContent) { + this._super.apply(this, arguments); + _.each(this.allFieldWidgets[this.state.id], function (widget) { + if (widget.attrs.widget === 'module_boolean') { + var inputID = this.idsForLabels[widget.name]; + var $widgets = this.$('.o_field_widget[name=' + widget.name + ']'); + var $label = inputID ? this.$('.o_form_label[for=' + inputID + ']') : $(); + widget.renderWithLabel($label.eq($widgets.index(widget.$el))); + } + }, this); + } +}); + +}); \ No newline at end of file diff --git a/muk_web_utils/static/src/js/widgets/notification.js b/muk_web_utils/static/src/js/widgets/notification.js new file mode 100644 index 0000000..b51b636 --- /dev/null +++ b/muk_web_utils/static/src/js/widgets/notification.js @@ -0,0 +1,46 @@ +/********************************************************************************** +* +* Copyright (c) 2017-2019 MuK IT GmbH. +* +* This file is part of MuK Web Utils +* (see https://mukit.at). +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +* +**********************************************************************************/ + +odoo.define('muk_web_utils.Notification', function (require) { +"use strict"; + +var core = require('web.core'); + +var Notification = require('web.Notification'); + +var _t = core._t; +var QWeb = core.qweb; + +Notification.include({ + init: function (parent, params) { + this._super.apply(this, arguments); + this.icon = params.icon || this.icon; + this.progress = params.progress; + }, + updateProgress: function(state, text) { + this.progress = {state: state, text: text}; + this.$(".progress-bar").text(text); + this.$(".progress-bar").width(state + "%"); + }, +}); + +}); \ No newline at end of file diff --git a/muk_web_utils/static/src/scss/binary.scss b/muk_web_utils/static/src/scss/binary.scss new file mode 100644 index 0000000..d4541e2 --- /dev/null +++ b/muk_web_utils/static/src/scss/binary.scss @@ -0,0 +1,28 @@ +/********************************************************************************** +* +* Copyright (c) 2017-2019 MuK IT GmbH. +* +* This file is part of MuK Web Utils +* (see https://mukit.at). +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +* +**********************************************************************************/ + +.o_list_view .o_field_widget.o_field_binary_file { + height: 100%; + button.btn { + padding-top: 0.25rem; + } +} diff --git a/muk_web_utils/static/src/scss/color.scss b/muk_web_utils/static/src/scss/color.scss new file mode 100644 index 0000000..297f8f9 --- /dev/null +++ b/muk_web_utils/static/src/scss/color.scss @@ -0,0 +1,66 @@ +/********************************************************************************** +* +* Copyright (c) 2017-2019 MuK IT GmbH. +* +* This file is part of MuK Web Utils +* (see https://mukit.at). +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +* +**********************************************************************************/ + +.mk_field_color_picker { + .o_opacity_slider { + display: none ! important; + } + input#opacity { + display: none ! important; + } + label[for="opacity"] { + display: none ! important; + } +} + +.o_field_widget.mk_field_color { + .mk_field_color_input { + width: auto; + display: inline-block; + } + .mk_field_color_button { + display: inline-block; + margin-bottom: 0.25rem; + line-height: 1.42rem; + } +} + +span.mk_field_color_index { + @for $size from 1 through length($o-colors) { + &.mk_color_index_#{$size - 1} { + color: nth($o-colors, $size); + } + } +} + +select.mk_field_color_index { + &.mk_color_index_0, .mk_color_index_0 { + background-color: $white ! important; + color: nth($o-colors, 1) ! important; + } + @for $size from 2 through length($o-colors) { + &.mk_color_index_#{$size - 1}, .mk_color_index_#{$size - 1} { + background-color: nth($o-colors, $size) ! important; + color: $white ! important; + } + } +} \ No newline at end of file diff --git a/muk_web_utils/static/src/scss/copy.scss b/muk_web_utils/static/src/scss/copy.scss new file mode 100644 index 0000000..ff67504 --- /dev/null +++ b/muk_web_utils/static/src/scss/copy.scss @@ -0,0 +1,34 @@ +/********************************************************************************** +* +* Copyright (c) 2017-2019 MuK IT GmbH. +* +* This file is part of MuK Web Utils +* (see https://mukit.at). +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +* +**********************************************************************************/ + +.o_field_widget.mk_field_copy { + .mk_copy_binary { + margin-left: 0.5rem; + .mk_copy_button { + font-size: 0.8rem; + line-height: 0.5; + border-radius: 0.2rem; + margin-bottom: 0.12rem; + padding: 0.25rem 0.4rem; + } + } +} \ No newline at end of file diff --git a/muk_web_utils/static/src/scss/dropzone.scss b/muk_web_utils/static/src/scss/dropzone.scss new file mode 100644 index 0000000..337ffbf --- /dev/null +++ b/muk_web_utils/static/src/scss/dropzone.scss @@ -0,0 +1,64 @@ +/********************************************************************************** +* +* Copyright (c) 2017-2019 MuK IT GmbH. +* +* This file is part of MuK Web Utils +* (see https://mukit.at). +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +* +**********************************************************************************/ + +.mk_dropzone { + position: relative; + &:before { + display:flex; + z-index: 1052; + color: gray('700'); + flex-direction:row; + align-items: center; + justify-content: center; + width: #{"calc(100% - 20px)"}; + height: #{"calc(100% - 20px)"}; + border: 2px dashed gray('700'); + @include gradient-y($white, gray('100')); + @include o-position-absolute(10px, 0, 0, 10px); + } + &:after { + display:flex; + z-index: 1052; + color: gray('700'); + flex-direction:row; + align-items: center; + justify-content: center; + width: #{"calc(100% - 20px)"}; + height: #{"calc(100% - 10px)"}; + @include o-position-absolute(0, 0, 0, 0); + } + &.mk_dropzone_file { + &:before { + font-family: FontAwesome; + text-decoration: inherit; + font-style: normal; + font-weight: normal; + font-size: 15rem; + content: "\f0ee"; + } + &:after { + padding-top: 18rem; + font-size: 3rem; + content: attr(data-dropzone-text); + } + } +} \ No newline at end of file diff --git a/muk_web_utils/static/src/scss/image.scss b/muk_web_utils/static/src/scss/image.scss new file mode 100644 index 0000000..e78e9a3 --- /dev/null +++ b/muk_web_utils/static/src/scss/image.scss @@ -0,0 +1,84 @@ +/********************************************************************************** +* +* Copyright (c) 2017-2019 MuK IT GmbH. +* +* This file is part of MuK Web Utils +* (see https://mukit.at). +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +* +**********************************************************************************/ + +.o_field_widget.o_field_image { + @include media-breakpoint-up(sm, $o-extra-grid-breakpoints) { + .mk_field_image_wrapper { + min-height: 60px; + min-width: 80px; + } + } + .mk_form_image_controls { + @include o-position-absolute($left: 0, $bottom: 0); + width: 100%; + color: white; + background-color: $o-brand-primary; + opacity: 0; + transition: opacity ease 400ms; + > button.fa { + border: none; + background-color: transparent; + } + > .fa { + margin: 5px; + cursor: pointer; + } + } + &:hover .mk_form_image_controls { + opacity: 0.8; + } + @include media-breakpoint-down(xs, $o-extra-grid-breakpoints) { + .mk_form_image_controls{ + position: initial; + opacity: 1; + > .fa{ + width: 50%; + padding: 6px; + margin: 0px; + text-align: center; + background: $o-brand-secondary; + } + } + } +} + +.o_field_widget.o_field_image.oe_avatar { + .mk_form_image_controls { + @include o-position-absolute($left: 0, $bottom: 10px); + } + img { + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4); + max-width: $o-avatar-size; + max-height: $o-avatar-size; + vertical-align: top; + margin-bottom: 10px; + border: none; + } + @include media-breakpoint-down(xs, $o-extra-grid-breakpoints) { + .mk_form_image_controls{ + position: initial; + > .fa{ + background: $o-brand-secondary; + } + } + } +} diff --git a/muk_web_utils/static/src/scss/mixins.scss b/muk_web_utils/static/src/scss/mixins.scss new file mode 100644 index 0000000..4c6e540 --- /dev/null +++ b/muk_web_utils/static/src/scss/mixins.scss @@ -0,0 +1,40 @@ +/********************************************************************************** +* +* Copyright (c) 2017-2019 MuK IT GmbH. +* +* This file is part of MuK Web Utils +* (see https://mukit.at). +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +* +**********************************************************************************/ + +@mixin mk-flex-container ($direction: row, $wrap: nowrap, $justify: flex-start, $items: stretch, $content: stretch) { + display: flex; + flex-wrap: $wrap; + flex-direction: $direction; + justify-content: $justify; + align-content: $content; + align-items: $items; +} + +@mixin mk-flex-child ($grow: 0, $shrink: 1, $basis: auto, $order: 0) { + display: flex; + flex-grow: $grow; + flex-shrink: $shrink; + flex-basis: $basis; + order: $order; +} + + diff --git a/muk_web_utils/static/src/scss/module.scss b/muk_web_utils/static/src/scss/module.scss new file mode 100644 index 0000000..0a30d94 --- /dev/null +++ b/muk_web_utils/static/src/scss/module.scss @@ -0,0 +1,33 @@ +/********************************************************************************** +* +* Copyright (c) 2017-2019 MuK IT GmbH. +* +* This file is part of MuK Web Utils +* (see https://mukit.at). +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +* +**********************************************************************************/ + +.o_settings_container { + .o_setting_box { + .o_setting_left_pane { + .mk_module_label { + position: absolute; + top: 0px; + right: 40px; + } + } + } +} \ No newline at end of file diff --git a/muk_web_utils/static/src/scss/notification.scss b/muk_web_utils/static/src/scss/notification.scss new file mode 100644 index 0000000..4caf339 --- /dev/null +++ b/muk_web_utils/static/src/scss/notification.scss @@ -0,0 +1,30 @@ +/********************************************************************************** +* +* Copyright (c) 2017-2019 MuK IT GmbH. +* +* This file is part of MuK Web Utils +* (see https://mukit.at). +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +* +**********************************************************************************/ + +.o_notification { + .mk_notification_progress { + padding: 2px 10px 10px 10px; + .progress { + background-color: rgba(0, 0, 0, 0.3); + } + } +} diff --git a/muk_web_utils/static/src/scss/share.scss b/muk_web_utils/static/src/scss/share.scss new file mode 100644 index 0000000..37fa64e --- /dev/null +++ b/muk_web_utils/static/src/scss/share.scss @@ -0,0 +1,54 @@ +/********************************************************************************** +* +* Copyright (c) 2017-2019 MuK IT GmbH. +* +* This file is part of MuK Web Utils +* (see https://mukit.at). +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +* +**********************************************************************************/ + +.o_field_widget.mk_field_share { + &.o_field_copy { + padding-left: 90px; + .mk_share_dropdown { + @include o-position-absolute($top: 0, $left: 0); + .mk_share_button { + padding: 0 10px; + } + &.mk_share_char { + height: 100%; + + } + &.mk_share_text { + .dropdown-menu { + line-height: 0.1 + } + } + } + } + &.o_form_uri { + .mk_share_dropdown { + margin-left: 0.2rem; + .mk_share_button { + padding: 0.25rem 0.4rem; + font-size: 0.8rem; + line-height: 0.5; + border-radius: 0.2rem; + margin-bottom: 0.12rem; + } + } + } +} \ No newline at end of file diff --git a/muk_web_utils/static/src/scss/switch.scss b/muk_web_utils/static/src/scss/switch.scss new file mode 100644 index 0000000..7fe9d6e --- /dev/null +++ b/muk_web_utils/static/src/scss/switch.scss @@ -0,0 +1,158 @@ +/********************************************************************************** +* +* Copyright (c) 2017-2019 MuK IT GmbH. +* +* This file is part of MuK Web Utils +* (see https://mukit.at). +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +* +**********************************************************************************/ + +.switch { + font-size: $font-size-base; + position: relative; + + input { + position: absolute; + height: 1px; + width: 1px; + background: none; + border: 0; + clip: rect(0 0 0 0); + clip-path: inset(50%); + overflow: hidden; + padding: 0; + + + label { + position: relative; + min-width: calc(#{$switch-height} * 2); + border-radius: $switch-border-radius; + height: $switch-height; + line-height: $switch-height; + display: inline-block; + cursor: pointer; + outline: none; + user-select: none; + vertical-align: middle; + text-indent: calc(calc(#{$switch-height} * 2) + .5rem); + } + + + label::before, + + label::after { + content: ''; + position: absolute; + top: 0; + left: 0; + width: calc(#{$switch-height} * 2); + bottom: 0; + display: block; + } + + + label::before { + right: 0; + background-color: $switch-bg; + border-radius: $switch-border-radius; + transition: $switch-transition; + } + + + label::after { + top: $switch-thumb-padding; + left: $switch-thumb-padding; + width: calc(#{$switch-height} - calc(#{$switch-thumb-padding} * 2)); + height: calc(#{$switch-height} - calc(#{$switch-thumb-padding} * 2)); + border-radius: $switch-thumb-border-radius; + background-color: $switch-thumb-bg; + transition: $switch-transition; + } + + &:checked + label::before { + background-color: $switch-checked-bg; + } + + &:checked + label::after { + margin-left: $switch-height; + } + + &:focus + label::before { + outline: none; + box-shadow: $switch-focus-box-shadow; + } + + &:disabled + label { + color: $switch-disabled-color; + cursor: not-allowed; + } + + &:disabled + label::before { + background-color: $switch-disabled-bg; + } + } + + &.switch-sm { + font-size: $font-size-sm; + + input { + + label { + min-width: calc(#{$switch-height-sm} * 2); + height: $switch-height-sm; + line-height: $switch-height-sm; + text-indent: calc(calc(#{$switch-height-sm} * 2) + .5rem); + } + + + label::before { + width: calc(#{$switch-height-sm} * 2); + } + + + label::after { + width: calc(#{$switch-height-sm} - calc(#{$switch-thumb-padding} * 2)); + height: calc(#{$switch-height-sm} - calc(#{$switch-thumb-padding} * 2)); + } + + &:checked + label::after { + margin-left: $switch-height-sm; + } + } + } + + &.switch-lg { + font-size: $font-size-lg; + + input { + + label { + min-width: calc(#{$switch-height-lg} * 2); + height: $switch-height-lg; + line-height: $switch-height-lg; + text-indent: calc(calc(#{$switch-height-lg} * 2) + .5rem); + } + + + label::before { + width: calc(#{$switch-height-lg} * 2); + } + + + label::after { + width: calc(#{$switch-height-lg} - calc(#{$switch-thumb-padding} * 2)); + height: calc(#{$switch-height-lg} - calc(#{$switch-thumb-padding} * 2)); + } + + &:checked + label::after { + margin-left: $switch-height-lg; + } + } + } + + + .switch { + margin-left: 1rem; + } +} \ No newline at end of file diff --git a/muk_web_utils/static/src/scss/variables.scss b/muk_web_utils/static/src/scss/variables.scss new file mode 100644 index 0000000..74fa8ca --- /dev/null +++ b/muk_web_utils/static/src/scss/variables.scss @@ -0,0 +1,39 @@ +/********************************************************************************** +* +* Copyright (c) 2017-2019 MuK IT GmbH. +* +* This file is part of MuK Web Utils +* (see https://mukit.at). +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +* +**********************************************************************************/ + +//---------------------------------------------------------- +// Switch +//---------------------------------------------------------- + +$switch-bg: #dee2e6; +$switch-disabled-bg: #e9ecef; +$switch-disabled-color: #868e96; +$switch-height: calc(#{$input-height} * .8) !default; +$switch-height-sm: calc(#{$input-height-sm} * .8) !default; +$switch-height-lg: calc(#{$input-height-lg} * .8) !default; +$switch-checked-bg: map-get($theme-colors, 'primary') !default; +$switch-thumb-bg: $white !default; +$switch-thumb-border-radius: 50% !default; +$switch-thumb-padding: 2px !default; +$switch-transition: .2s all !default; +$switch-border-radius: $switch-height; +$switch-focus-box-shadow: 0 0 0 $input-btn-focus-width rgba(map-get($theme-colors, 'primary'), .25); \ No newline at end of file diff --git a/muk_web_utils/static/src/xml/color.xml b/muk_web_utils/static/src/xml/color.xml new file mode 100644 index 0000000..ecbc10d --- /dev/null +++ b/muk_web_utils/static/src/xml/color.xml @@ -0,0 +1,65 @@ + + + + + + + + +
+ + +
+
+ + + + + + +
\ No newline at end of file diff --git a/muk_web_utils/static/src/xml/copy.xml b/muk_web_utils/static/src/xml/copy.xml new file mode 100644 index 0000000..4a21900 --- /dev/null +++ b/muk_web_utils/static/src/xml/copy.xml @@ -0,0 +1,35 @@ + + + + + + + +
+ +
+
+ +
\ No newline at end of file diff --git a/muk_web_utils/static/src/xml/image.xml b/muk_web_utils/static/src/xml/image.xml new file mode 100644 index 0000000..824c3af --- /dev/null +++ b/muk_web_utils/static/src/xml/image.xml @@ -0,0 +1,34 @@ + + + + + + + + +
+
+
+
+ +
\ No newline at end of file diff --git a/muk_web_utils/static/src/xml/module.xml b/muk_web_utils/static/src/xml/module.xml new file mode 100644 index 0000000..0709de4 --- /dev/null +++ b/muk_web_utils/static/src/xml/module.xml @@ -0,0 +1,39 @@ + + + + + + + +
+
+ Missing Module +
+
+

The module could not be found on the server.

+

Click on the download button to be redirected to the store and download the corresponding module.

+
+
+
+ +
\ No newline at end of file diff --git a/muk_web_utils/static/src/xml/notification.xml b/muk_web_utils/static/src/xml/notification.xml new file mode 100644 index 0000000..2ed595a --- /dev/null +++ b/muk_web_utils/static/src/xml/notification.xml @@ -0,0 +1,39 @@ + + + + + + + + +
+
+
+ +
+
+
+
+
+ +
\ No newline at end of file diff --git a/muk_web_utils/static/src/xml/share.xml b/muk_web_utils/static/src/xml/share.xml new file mode 100644 index 0000000..5e149d5 --- /dev/null +++ b/muk_web_utils/static/src/xml/share.xml @@ -0,0 +1,106 @@ + + + + + + + +
+ + +
+
+ + +
+ + + + + + + + + + + + + + + +
+ + +
+
+ + +
+ +

shared a link with you!

+ + Open + +
+ +

shared a message with you!

+

+ +

+
+ + +
+

shared a file with you!

+ + Download + +
+
+ + \ No newline at end of file diff --git a/muk_web_utils/static/src/xml/switch.xml b/muk_web_utils/static/src/xml/switch.xml new file mode 100644 index 0000000..cb9e742 --- /dev/null +++ b/muk_web_utils/static/src/xml/switch.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/muk_web_utils/static/tests/fields.js b/muk_web_utils/static/tests/fields.js new file mode 100644 index 0000000..6af0e6f --- /dev/null +++ b/muk_web_utils/static/tests/fields.js @@ -0,0 +1,205 @@ +/********************************************************************************** +* +* Copyright (c) 2017-2019 MuK IT GmbH. +* +* This file is part of MuK Web Utils +* (see https://mukit.at). +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +* +**********************************************************************************/ +odoo.define('muk_web_utils.tests.fields', function (require) { +"use strict"; + +var basicFields = require('web.basic_fields'); +var concurrency = require('web.concurrency'); +var config = require('web.config'); +var core = require('web.core'); +var FormView = require('web.FormView'); +var KanbanView = require('web.KanbanView'); +var ListView = require('web.ListView'); +var session = require('web.session'); +var testUtils = require('web.test_utils'); +var field_registry = require('web.field_registry'); + +var createView = testUtils.createView; +var createAsyncView = testUtils.createAsyncView; +var DebouncedField = basicFields.DebouncedField; +var JournalDashboardGraph = basicFields.JournalDashboardGraph; +var _t = core._t; + +QUnit.module('muk_web_utils', {}, function () { + +QUnit.module('fields', { + beforeEach: function () { + this.data = { + partner: { + fields: { + display_name: { + string: "Displayed name", + type: "char", + searchable: true + }, + short: { + string: "Short", + type: "char", + searchable: true, + trim: true + }, + long: { + string: "Long", + string: "txt", + type: "text", + }, + document: { + string: "Binary", + type: "binary", + attachment: true, + }, + }, + records: [{ + id: 1, + display_name: "first record", + short: "Short Text", + long: "Super looooooong Text", + document: 'coucou==\n', + }], + }, + }; + } +}, function () { + QUnit.module('BinaryFileCopy'); + + QUnit.test('Fields is correctly rendered', function (assert) { + assert.expect(2); + + var form = createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: ( + '
' + + '' + + '' + + '' + ), + res_id: 1, + }); + + assert.strictEqual( + form.$('a.o_field_widget[name="document"] > .mk_copy_binary > .mk_copy_button').length, + 1, "the copy button should be visible in readonly mode" + ); + + form.$buttons.find('.o_form_button_edit').click(); + + assert.strictEqual( + form.$('a.o_field_widget[name="document"] > .mk_copy_binary').length, + 0, "the copy button shouldn't be visible in edit mode" + ); + + form.destroy(); + }); + + QUnit.module('CharShare'); + + QUnit.test('Fields is correctly rendered', function (assert) { + assert.expect(1); + + var form = createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: ( + '
' + + '
' + + '' + + '
' + + '
' + ), + res_id: 1, + }); + + assert.strictEqual( + form.$('span.o_field_widget[name="short"] > .mk_share_dropdown.mk_share_char').length, + 1, "the copy button should be visible in readonly mode" + ); + + form.destroy(); + }); + + QUnit.module('TextShare'); + + QUnit.test('Fields is correctly rendered', function (assert) { + assert.expect(1); + + var form = createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: ( + '
' + + '
' + + '' + + '
' + + '
' + ), + res_id: 1, + }); + + assert.strictEqual( + form.$('span.o_field_widget[name="long"] > .mk_share_dropdown.mk_share_text').length, + 1, "the copy button should be visible in readonly mode" + ); + + form.destroy(); + }); + + QUnit.module('BinaryFileShare'); + + QUnit.test('Fields is correctly rendered', function (assert) { + assert.expect(2); + + var form = createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: ( + '
' + + '' + + '' + + '' + ), + res_id: 1, + }); + + assert.strictEqual( + form.$('a.o_field_widget[name="document"] > .mk_share_dropdown > .mk_share_button').length, + 1, "the share dropdown should be visible in readonly mode" + ); + + form.$buttons.find('.o_form_button_edit').click(); + + assert.strictEqual( + form.$('a.o_field_widget[name="document"] > .mk_share_dropdown > .mk_share_button').length, + 0, "the share dropdown shouldn't be visible in edit mode" + ); + + form.destroy(); + }); +}); + +}); + +}); \ No newline at end of file diff --git a/muk_web_utils/template/assets.xml b/muk_web_utils/template/assets.xml new file mode 100644 index 0000000..1386b49 --- /dev/null +++ b/muk_web_utils/template/assets.xml @@ -0,0 +1,79 @@ + + + + + + +