diff --git a/Makefile b/Makefile index c6220e4000639e85b10d2dff210b093cd8aea886..c08f543d5f03892ff0f55e5c31624dee76e26e84 100644 --- a/Makefile +++ b/Makefile @@ -48,7 +48,7 @@ include $(MAKEFILE_IMPORT_PATH)jsdoc-json.mk include $(MAKEFILE_IMPORT_PATH)gitignore.mk include $(MAKEFILE_IMPORT_PATH)color.mk include $(MAKEFILE_IMPORT_PATH)version.mk -include $(MAKEFILE_IMPORT_PATH)nodejs.mk +include $(MAKEFILE_IMPORT_PATH)node.mk include $(MAKEFILE_IMPORT_PATH)terminal.mk #include $(MAKEFILE_IMPORT_PATH)target-go-fetch-licenses.mk include $(MAKEFILE_IMPORT_PATH)target-deploy-tool.mk @@ -62,6 +62,7 @@ include $(MAKEFILE_IMPORT_PATH)target-update-makefiles.mk include $(MAKEFILE_IMPORT_PATH)target-help.mk #include $(MAKEFILE_IMPORT_PATH)target-go-build.mk include $(MAKEFILE_IMPORT_PATH)target-node-test.mk +include $(MAKEFILE_IMPORT_PATH)target-node-release.mk include $(MAKEFILE_IMPORT_PATH)target-git.mk include $(MAKEFILE_IMPORT_PATH)target-init-standard.mk include $(MAKEFILE_IMPORT_PATH)target-variable.mk diff --git a/application/CHANGELOG b/application/CHANGELOG new file mode 100644 index 0000000000000000000000000000000000000000..2a9ee2ba799d120994c2b39bb3fede4c962dd300 --- /dev/null +++ b/application/CHANGELOG @@ -0,0 +1,437 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +## [2.0.0] - 2022-08-08 + +- New structure in the repo +- Remove namespace functionality +- Switching the tests to esbuild +- Actualization of the documentation template +- New URL of the documentation +- Bug fix typeOf (constructor name was recognized wrong) +- Removal of the built version of the library + +## [1.31.0] - 2022-02-07 + +## Added + +- [new promise domReady and windowReady](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/116) + +## [1.30.0] - 2022-02-05 + +## Added + +- [new class DeadMansSwitch](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/115) + +## [1.29.3] - 2022-01-23 + +## Fixed + +- [CSSStyleSheet polyfill throws an error](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/113) + +## [1.29.2] - 2022-01-22 + +## Fixed + +- [CSSStyleSheet polyfill throws an error](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/113) + +## [1.29.1] - 2022-01-07 + +## Fixed + +- [TypeError: element.addNode is not a function](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/112) + +## [1.29.0] - 2021-12-29 + +## Added + +- [new class DeadMansSwitch](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/111) +- [new function fireCustomEvent](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/110) +- [extends CustomerElement.getSlottedElements() with parameter name](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/109) + + +## [1.28.1] - 2021-12-12 + +## Added + +- [new function fireCustomEvent](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/108) + +## [1.28.0] - 2021-12-11 + +## Added + +- [new data-monster-select-this attribute](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/105) +- [Node.toString()](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/107) + +## Changed + +- [add rootReference to buildTree ](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/106) + +## [1.27.0] - 2021-12-06 + +## Added + +- [extends Updater for manual update bindings](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/102) +- [new function findClosestByClass](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/104) + +## Changed + +- [refactoring updater with internalSymbol](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/103) + +## [1.26.1] - 2021-12-02 + +## Fixed + +- [themed templates are not found from the shadowroot](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/101) + +## [1.26.0] - 2021-12-02 + +## Added + +- [new class Worker](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/95) +- [new class NodeRecursiveIterator](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/97) +- [new class Node](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/98) +- [new class NodeList](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/99) +- [new function buildTree](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/100) + +## Fixed + +- [only one attribute is replaced with indexed path](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/96) + + +## [1.25.0] - 2021-11-13 + +## Added + +- [new class FocusManager](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/93) +- [new class ResourceManager](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/92) +- [new class Resource, Link, Stylesheet, Data and Script](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/91) +- [new class UUID](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/89) + +## Changed + +- [optimization of random](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/90) + + +## [1.24.0] - 2021-11-13 + +## Added + +- [new class WriteError](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/79) +- [new transformer commands nth, first, last and nth-last](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/78) +- [add Proxyobserver.setObject](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/80) +- [transform: new debug, to-base64, from-base64, path-exists and optimzed default command](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/84) +- [new function trimSpace](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/86) + +## Changed + +- [changeover to internalSymbol and uniform symbols and keys](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/81) +- [optimization of the datasources and the restapi](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/82) +- [optimization attribute monitoring](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/85) +- [Formatter: marker in marker](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/88) + +## [1.23.0] - 2021-10-28 + +## Added + +- [add the processing of slotted elements](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/76) + +## Changed + +- [datasource should work with proxy data](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/77) + +## [1.22.0] - 2021-10-25 + +## Added + +- [new DataSource classes](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/75) + +## [1.22.0] - 2021-10-23 + +## Fixed + +- [package.json](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/74) + +## [1.21.0] - 2021-10-22 + +## Added + +- [new class processing](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/73) + +## [1.20.2] - 2021-10-19 + +## Fixed + +- [the method getClone is not called for objects with this method.](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/72) + +## [1.20.1] - 2021-10-19 + +## Fixed + +- [the integration of class instances into empty arrays throws an exception](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/71) + +## [1.20.0] - 2021-10-18 + +## Fixed + +- [closest cannot be used, because closest is not correct for slotted elements](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/70) + +## [1.19.0] - 2021-10-16 + +## Added + +- [new method hasNode to check if a node is included](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/69) + +## Fixed + +- [value===NaN is always false](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/68) + + +## [1.18.0] - 2021-10-11 + +## Added + +- [option can be specified as DataURI](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/64) +- [MediaType class](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/65) +- [DataURI class](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/66) +- [toBinary & fromBinary functions](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/67) + +## [1.17.2] - 2021-10-09 + +## Fixed + +- [the diff function evaluates arrays incorrectly](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/62) + +## [1.17.1] - 2021-10-09 + +## Fixed + +- [diff throws an error at zero values](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/61) + +## [1.17.0] - 2021-10-07 + +## Changed + +- [In the templates of the buildmap function there should also be the possibility to specify parent values.](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/60) + +## [1.16.1] - 2021-09-29 + +## Fixed + +- [Uncaught TypeError: (0 , logging_namespace_namespaceObject.assignToNamespace) is not a function](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/59) + +## [1.16.0] - 2021-09-27 + +## Changed + +- documentation +- build scripts + +## [1.15.3] - 2021-09-20 + +### Fixed + +- [connectedCallback can be called more often and setTimeout prevents checking](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/57) + +## [1.15.1] - 2021-09-13 + +### Fixed + +- [TypeError: t.getElementById is not a function](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/56) + +## [1.15.0] - 2021-09-13 + +### Added + +- [when changing the option attribute, the options should also change](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/50) +- [new PROPERTY_KEY_INTERNALDATA internals](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/52) + +## Changed + +- [improvement of the prox handling in the proxobserver](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/55) +- [move observedAttributes to customelements](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/54) +- [optimization of the extends handling in distinction to Object.assign](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/53) +- [change from Monster.assignToNamespace to assignToNamespace an import](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/51) + +## [1.14.1] - 2021-09-12 + +### Fixed + +- [control and optimize customelement](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/49) + +## [1.14.0] - 2021-09-12 + +### Added + +- [control and optimize customelement](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/49) + +## [1.13.0] - 2021-09-06 + +### Added + +- [classes for localization and management of translations](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/48) + +## [1.12.0] - 2021-09-05 + +### Added + +- [a class to format texts](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/47) + + +## [1.11.1] - 2021-09-05 + +## Changed + +- [improvement of the entry points of the libraries](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/46) + + +## [1.11.0] - 2021-09-04 + +## Changed + +- [conversion from babelify to webpack](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/44) + +## [1.10.3] - 2021-09-01 + +## Changed + +- [older browsers have a problem with the proxy object](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/43) + + +## [1.10.2] - 2021-08-31 + +## Changed + +- [optimizations for old browsers](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/42) + +## [1.10.0] - 2021-08-25 + +### Added + +- [add extend function](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/35) +- [new method fireEvent](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/38) + +## Changed + +- [shadowRoot should be optional](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/36) + +### Fixed + +- [improve code coverage](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/40) +- [an empty object does not lead to an output](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/29) + +### Removed + +- [the updater class replace the handle class](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/34) + +## [1.9.0] - 2021-08-17 + +### Added + +- [create the possibility to bind form fields to the datset](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/30) +- [new attribute method to change tokens](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/31) +- [new function isSymbol and validateSymbol](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/32) + +## Changed + +- [Pipe should call callbacks in context](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/33) + +## Fixed + +- [an empty object does not lead to an output](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/29) + +## [1.8.0] - 2021-08-07 + +### Added + +- [new Monster.DOM.Updater](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/25) +- [add tojson to transformer](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/27) + +### Changed + +- [the validate functions now return the validated value](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/24) +- [introduce DOM constants](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/26) + +## [1.7.0] - 2021-08-07 + +### Added + +- [new Monster.DOM.CustomElement](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/20) +- [new Monster.DOM.Theme](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/21) +- [new Monster.Types.getType](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/22) +- [new Monster.Data.buildMap](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/23) + +## [1.6.0] - 2021-07-29 + +### Added + +- [new function getGobal()](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/15) +- [new Monster.DOM.Template](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/16) +- [new Monster.Type.RandomID](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/19) +- [new method Monster.Util.Pathfinder::deleteVia()](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/18) + +### Fixed + +- missing imports in monster.js + + +## [1.5.0] - 2021-07-06 + +### Added + +- [new Monster.Data.Transformer](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/9) +- [new Monster.Data.Pathfinder](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/9) +- [new functions Monster.Types.isInstance() and Monster.Types.validateInstance()](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/12) + +### Changed + +- [The class Monster.Types.Object is now called Monster.Types.Base](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/11) + +## [1.4.0] - 2021-07-04 + +### Added + +- [new Monster.Types.Queue](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/6) +- [new Monster.Types.UniqueQueue](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/6) +- [new Monster.Types.Stack](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/7) +- [new Monster.Types.validateInteger](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/8) +- [new Monster.Types.isInteger](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/8) +- [new Monster.Util.Pathfinder](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/9) + +### Changed + +- [ensure that an object is not informed of the same change more than once](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/issues/5) + +## [1.3.0] - 2021-06-30 + +### Added + +- new class Monster.Util.Comparator +- new class Monster.Constraints.AbstractConstraint +- new class Monster.Constraints.AndOperator +- new class Monster.Constraints.OrOperator +- new class Monster.Constraints.Invalid +- new class Monster.Constraints.Valid +- new class Monster.Constraints.IsObject +- new class Monster.Constraints.IsArray + +### Changed + +- the return value of TokenList.replace() is now this +- change observer tags from `Set` to `TokenList` + +## [1.2.0] - 2021-06-30 + +### Added + +- Monster.Types.TokenList + +## [1.1.0] - 2021-06-28 + +### Added + +- Monster.Types +- Monster.Util +- Monster.Math + diff --git a/application/LICENSE b/application/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..dd3f281914bfb481b0e3cb681bf9f7d2031204eb --- /dev/null +++ b/application/LICENSE @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + Monster + Copyright (C) 2021 OSS / Libraries / Javascript + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +<https://www.gnu.org/licenses/>. diff --git a/application/README.md b/application/README.md index 12510765e5e6e2defd72ba1358349b941a56fe88..694f1036eee4e98fef1a01124403f72b67f2c25b 100644 --- a/application/README.md +++ b/application/README.md @@ -1,6 +1,12 @@ # Monster -Monster is a lightweight, robust and easy-to-use library with modest ambitions. +A modern, efficient and flexible JavaScript library for building user interfaces. + +Monster relies on proven concepts mixed with many new Javascript concepts. + +The library relies on modern features such as classes, WeakRef, +WeakMaps, proxies or the MutationObserver interface, to name a few. + Monster integrates easily into your existing websites without taking over everything. @@ -17,43 +23,12 @@ One design target is to reach the shiny sun with as little Javascript as possibl Monster was built with ES6 modules and uses [import](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/import) and [export](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export). -There is a version in the `/dist` folder that was built via [webpack](https://github.com/webpack/webpack) and -thus supports all browsers that are compatible with [ES5](https://caniuse.com/es5). - For some functions you need additional [polyfills](#polyfill). ## Installation -`npm install @schukai/monster` - -## CDN - -``` -<!DOCTYPE html> -<html> - <head> - <title>HTML, CSS and JavaScript demo</title> - </head> - - <body> - <p>Hello Monster <span id="version"></span>!</p> - <script type="module"> - import { - Monster - } from 'https://unpkg.com/@schukai/monster@1.31.0/dist/modules/monster.js'; - let id = document.getElementById('version'); - id.innerHTML = Monster.getVersion(); - </script> - </body> -</html> -``` - -`nomodule` for backwards compatibility +`npm install @schukai/monster`, `yarn install @schukai/monster` oder `pnpm install @schukai/monster` -``` -<script type="module" src="https://unpkg.com/@schukai/monster@1.31.0/dist/modules/monster.js"></script> -<script nomodule src="https://unpkg.com/@schukai/monster@1.31.0/dist/monster.js"></script> -``` ### Polyfill We do try to work around some browser bugs, but on the whole we don't use polyfills and feature detection. @@ -61,24 +36,14 @@ We do try to work around some browser bugs, but on the whole we don't use polyfi However, many functions can be mapped via [polyfill.io](https://polyfill.io/) and thus the compatibility can be increased. ``` - <script id="polyfill" src="" - crossorigin="anonymous" - referrerpolicy="no-referrer"></script> -``` - -### Compatibility - -if the global name Monster, is already used by another component, the following variable can be used to adapt the name of the component. - -``` -__MonsterRootName__="MyMonster" + <script id="polyfill" src="https://polyfill.io/v3/polyfill.min.js?features=Array.from,Array.isArray,Array.prototype.entries,Array.prototype.fill,Array.prototype.forEach,Array.prototype.indexOf,Array.prototype.keys,Array.prototype.lastIndexOf,Array.prototype.map,Array.prototype.reduce,Array.prototype.sort,ArrayBuffer,atob,DataView,document,DocumentFragment,Element,Event,globalThis,HTMLDocument,HTMLTemplateElement,JSON,Map,Math.log2,Number.isInteger,Object.assign,Object.defineProperty,Object.entries,Object.getOwnPropertyDescriptor,Object.getPrototypeOf,Object.keys,Promise,Reflect,Reflect.defineProperty,Reflect.get,Reflect.getOwnPropertyDescriptor,Reflect.setPrototypeOf,Set,String.prototype.endsWith,String.prototype.matchAll,String.prototype.padStart,String.prototype.startsWith,String.prototype.trim,Symbol,Symbol.iterator,WeakMap,WeakSet" + crossorigin="anonymous" + referrerpolicy="no-referrer"></script> ``` -After that, all classes are accessible via `MyMonster`. For example `MyMonster.getVersion()`. - ## Documentation -To check out docs and examples, visit [monsterjs.org/en/doc/latest/](https://monsterjs.org/en/doc/latest/). +To check out docs and examples, visit [monsterjs.org/en/doc/monster/](https://monsterjs.org/en/doc/monster/). ## Questions @@ -92,14 +57,14 @@ issue. Issues not conforming to the guidelines may be closed immediately. ## License -Copyright © 2021 schukai GmbH +Copyright © 2022 schukai GmbH [AGPL](https://www.gnu.org/licenses/agpl-3.0.de.html) -you can also purchase a commercial licence +You can also purchase a commercial licence. ## Changelog Detailed changes for each release are documented in -the [CHANGELOG](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/blob/master/packages/monster/CHANGELOG). +the [CHANGELOG](https://gitlab.schukai.com/oss/libraries/javascript/monster/-/blob/master/application/CHANGELOG). diff --git a/deployment/makefiles/color.mk b/development/makefiles/color.mk similarity index 100% rename from deployment/makefiles/color.mk rename to development/makefiles/color.mk diff --git a/deployment/makefiles/directories-standard.mk b/development/makefiles/directories-standard.mk similarity index 100% rename from deployment/makefiles/directories-standard.mk rename to development/makefiles/directories-standard.mk diff --git a/deployment/makefiles/gitignore.mk b/development/makefiles/gitignore.mk similarity index 100% rename from deployment/makefiles/gitignore.mk rename to development/makefiles/gitignore.mk diff --git a/deployment/makefiles/go.mk b/development/makefiles/go.mk similarity index 100% rename from deployment/makefiles/go.mk rename to development/makefiles/go.mk diff --git a/deployment/makefiles/jsdoc-json.mk b/development/makefiles/jsdoc-json.mk similarity index 100% rename from deployment/makefiles/jsdoc-json.mk rename to development/makefiles/jsdoc-json.mk diff --git a/deployment/makefiles/jsdoc.mk b/development/makefiles/jsdoc.mk similarity index 100% rename from deployment/makefiles/jsdoc.mk rename to development/makefiles/jsdoc.mk diff --git a/deployment/makefiles/license-agpl3.mk b/development/makefiles/license-agpl3.mk similarity index 100% rename from deployment/makefiles/license-agpl3.mk rename to development/makefiles/license-agpl3.mk diff --git a/deployment/makefiles/nodejs.mk b/development/makefiles/node.mk similarity index 100% rename from deployment/makefiles/nodejs.mk rename to development/makefiles/node.mk diff --git a/deployment/makefiles/output.mk b/development/makefiles/output.mk similarity index 100% rename from deployment/makefiles/output.mk rename to development/makefiles/output.mk diff --git a/deployment/makefiles/placeholder.mk b/development/makefiles/placeholder.mk similarity index 100% rename from deployment/makefiles/placeholder.mk rename to development/makefiles/placeholder.mk diff --git a/deployment/makefiles/project.mk b/development/makefiles/project.mk similarity index 100% rename from deployment/makefiles/project.mk rename to development/makefiles/project.mk diff --git a/deployment/makefiles/s3.mk b/development/makefiles/s3.mk similarity index 100% rename from deployment/makefiles/s3.mk rename to development/makefiles/s3.mk diff --git a/deployment/makefiles/target-caddy.mk b/development/makefiles/target-caddy.mk similarity index 100% rename from deployment/makefiles/target-caddy.mk rename to development/makefiles/target-caddy.mk diff --git a/deployment/makefiles/target-deploy-tool.mk b/development/makefiles/target-deploy-tool.mk similarity index 100% rename from deployment/makefiles/target-deploy-tool.mk rename to development/makefiles/target-deploy-tool.mk diff --git a/deployment/makefiles/target-docman.mk b/development/makefiles/target-docman.mk similarity index 100% rename from deployment/makefiles/target-docman.mk rename to development/makefiles/target-docman.mk diff --git a/deployment/makefiles/target-git.mk b/development/makefiles/target-git.mk similarity index 100% rename from deployment/makefiles/target-git.mk rename to development/makefiles/target-git.mk diff --git a/deployment/makefiles/target-go-build.mk b/development/makefiles/target-go-build.mk similarity index 100% rename from deployment/makefiles/target-go-build.mk rename to development/makefiles/target-go-build.mk diff --git a/deployment/makefiles/target-go-fetch-licenses.mk b/development/makefiles/target-go-fetch-licenses.mk similarity index 100% rename from deployment/makefiles/target-go-fetch-licenses.mk rename to development/makefiles/target-go-fetch-licenses.mk diff --git a/deployment/makefiles/target-help.mk b/development/makefiles/target-help.mk similarity index 100% rename from deployment/makefiles/target-help.mk rename to development/makefiles/target-help.mk diff --git a/deployment/makefiles/target-init-standard.mk b/development/makefiles/target-init-standard.mk similarity index 100% rename from deployment/makefiles/target-init-standard.mk rename to development/makefiles/target-init-standard.mk diff --git a/deployment/makefiles/target-jekyll.mk b/development/makefiles/target-jekyll.mk similarity index 100% rename from deployment/makefiles/target-jekyll.mk rename to development/makefiles/target-jekyll.mk diff --git a/deployment/makefiles/target-jsdoc-build.mk b/development/makefiles/target-jsdoc-build.mk similarity index 100% rename from deployment/makefiles/target-jsdoc-build.mk rename to development/makefiles/target-jsdoc-build.mk diff --git a/deployment/makefiles/target-minerva.mk b/development/makefiles/target-minerva.mk similarity index 100% rename from deployment/makefiles/target-minerva.mk rename to development/makefiles/target-minerva.mk diff --git a/deployment/makefiles/target-node-build.mk b/development/makefiles/target-node-build.mk similarity index 100% rename from deployment/makefiles/target-node-build.mk rename to development/makefiles/target-node-build.mk diff --git a/development/makefiles/target-node-release.mk b/development/makefiles/target-node-release.mk new file mode 100644 index 0000000000000000000000000000000000000000..f6b6bde835bc3c892821b355165f6dcfc5b33188 --- /dev/null +++ b/development/makefiles/target-node-release.mk @@ -0,0 +1,49 @@ +############################################################################################# +############################################################################################# +## +## BUILD NODE +## +############################################################################################# +############################################################################################# + +.PHONY: node-release-major +## release major version of node +node-release-major: + $(ECHOMARKER) "release package" + $(QUIET) $(MAKE) next-major-version + $(QUIET) $(MAKE) node-release + +.PHONY: node-release-minor +## release minor version of node +node-release-minor: + $(ECHOMARKER) "release package" + $(QUIET) $(MAKE) next-minor-version + $(QUIET) $(MAKE) node-release + +.PHONY: node-release-patch +## release patch version of node +node-release-patch: + $(ECHOMARKER) "release package" + $(QUIET) $(MAKE) next-patch-version + $(QUIET) $(MAKE) node-release + +.PHONY: node-release +node-release: + $(ECHOMARKER) "release package" + #$(QUIET) $(MAKE) node-test + $(QUIET) $(NPM) --prefix $(NODE_ROOT_DIR) run build + +# $(QUIET) $(MAKE) clean +# $(QUIET) $(SCRIPT_PATH)increase-version.sh "$(MONSTER_CORE_DIR)package.json" $(MONSTER_CORE_VERSION) $(NEXTVERSION) +# $(QUIET) $(MAKE) clean +# $(QUIET) $(MAKE) build-monster +# $(QUIET) $(MAKE) uglifyjs-monster +# $(QUIET) $(MAKE) test-monster +# $(QUIET) $(MAKE) coverage-monster DONTOPENBROWSER=true +# $(QUIET) $(MAKE) test-browser-monster DONTOPENBROWSER=true +# $(QUIET) $(MAKE) doc-2-aws +# $(QUIET) $(MAKE) doc-build-versions-js +# $(QUIET) $(MAKE) git-tag +# $(QUIET) $(MAKE) transfer-monster +# $(QUIET) $(MAKE) npm-publish-monster +# $(QUIET) touch $(MONSTER_CORE_DIR)package.json ; $(MAKE) doc-build-versions-js \ No newline at end of file diff --git a/deployment/makefiles/target-node-test.mk b/development/makefiles/target-node-test.mk similarity index 68% rename from deployment/makefiles/target-node-test.mk rename to development/makefiles/target-node-test.mk index 05535db44ae4eadd87732438ea197f88ab8b8b07..0a03f40ac2fb4618807a45e02f5392bbd6256cba 100644 --- a/deployment/makefiles/target-node-test.mk +++ b/development/makefiles/target-node-test.mk @@ -10,5 +10,11 @@ ## Test JS Components node-test: $(NODE_MODULES_MODIFIED) $(ECHOMARKER) "Test Node Components" - $(QUIET) cd $(TEST_PATH); $(MOCHA) --recursive $(TEST_PATH)cases/ ; cd - + $(QUIET) $(NPM) --prefix $(NODE_ROOT_DIR) run test + +.PHONY: node-web-test +## Test JS Components +node-web-test: $(NODE_MODULES_MODIFIED) + $(ECHOMARKER) "Test Node Components" + $(QUIET) $(NPM) --prefix $(NODE_ROOT_DIR) run web-test diff --git a/deployment/makefiles/target-update-makefiles.mk b/development/makefiles/target-update-makefiles.mk similarity index 100% rename from deployment/makefiles/target-update-makefiles.mk rename to development/makefiles/target-update-makefiles.mk diff --git a/deployment/makefiles/target-variable.mk b/development/makefiles/target-variable.mk similarity index 100% rename from deployment/makefiles/target-variable.mk rename to development/makefiles/target-variable.mk diff --git a/deployment/makefiles/terminal-check.mk b/development/makefiles/terminal-check.mk similarity index 100% rename from deployment/makefiles/terminal-check.mk rename to development/makefiles/terminal-check.mk diff --git a/deployment/makefiles/terminal.mk b/development/makefiles/terminal.mk similarity index 100% rename from deployment/makefiles/terminal.mk rename to development/makefiles/terminal.mk diff --git a/deployment/makefiles/version.mk b/development/makefiles/version.mk similarity index 62% rename from deployment/makefiles/version.mk rename to development/makefiles/version.mk index b4d7eb0540a618d50ab307aafabfb52f34774b26..da81bea9a379dc08674bb3589a6d2fa02163212c 100644 --- a/deployment/makefiles/version.mk +++ b/development/makefiles/version.mk @@ -18,10 +18,23 @@ endif PROJECT_VERSION ?= $(shell cat $(RELEASE_FILE) | jq -r .version) PROJECT_BUILD_DATE ?= $(shell $(VERSION_BIN) date) - .PHONY: next-patch-version -## create next-patch-version +## create next patch version next-patch-version: $(ECHOMARKER) "Creating next version" $(QUIET) $(VERSION_BIN) patch --path $(RELEASE_FILE) --selector "version" $(QUIET) $(eval PROJECT_VERSION := $(shell cat $(RELEASE_FILE) | jq -r .version)) + +.PHONY: next-minor-version +## create next minor version +next-minor-version: + $(ECHOMARKER) "Creating next minor version" + $(QUIET) $(VERSION_BIN) minor --path $(RELEASE_FILE) --selector "version" + $(QUIET) $(eval PROJECT_VERSION := $(shell cat $(RELEASE_FILE) | jq -r .version)) + +.PHONY: next-major-version +## create next major version +next-major-version: + $(ECHOMARKER) "Creating next minor version" + $(QUIET) $(VERSION_BIN) major --path $(RELEASE_FILE) --selector "version" + $(QUIET) $(eval PROJECT_VERSION := $(shell cat $(RELEASE_FILE) | jq -r .version)) diff --git a/development/monster.temp b/development/monster.temp new file mode 100644 index 0000000000000000000000000000000000000000..1f57bde84836eaa56f32a9904b1b33261a1ba0b2 --- /dev/null +++ b/development/monster.temp @@ -0,0 +1,44068 @@ +'use strict'; + +/** + * @author schukai GmbH + */ + +import {isObject} from "../types/is.mjs"; +import {AbstractConstraint} from "./abstract.mjs"; + +export {IsObject} + +/** + * Constraints are used to define conditions that must be met by the value of a variable. + * + * The uniform API of the constraints allows chains to be formed. + * + * ``` + * <script type="module"> + * import {IsObject} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/isobject.mjs'; + * console.log(new IsObject()) + * </script> + * ``` + * + * @example + * + * import {IsObject} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/isobject.mjs'; + * + * new IsObject() + * .isValid({}) + * .then(()=>console.log(true)); + * // ↦ true + * + * + * new IsObject() + * .isValid(99) + * .catch(e=>console.log(e)); + * // ↦ 99 + * + * @since 1.3.0 + * @copyright schukai GmbH + * @memberOf Monster.Constraints + * @summary A constraint to check if a value is an object + */ +class IsObject extends AbstractConstraint { + + /** + * this method return a promise containing the result of the check. + * + * @param {*} value + * @returns {Promise} + */ + isValid(value) { + if (isObject(value)) { + return Promise.resolve(value); + } + + return Promise.reject(value); + } + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {AbstractConstraint} from "./abstract.mjs"; + +export {Invalid} + +/** + * Constraints are used to define conditions that must be met by the value of a variable. + * + * The uniform API of the constraints allows chains to be formed. + * + * The invalid constraint allows an always invalid query to be performed. this constraint is mainly intended for testing. + * + * ``` + * <script type="module"> + * import {Invalid} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/invalid.mjs'; + * new Invalid(); + * </script> + * ``` + * + * @example + * + * import {Invalid} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/invalid.mjs'; + * + * new Invalid().isValid() + * .then(()=>console.log(true)) + * .catch(()=>console.log(false)); + * // ↦ false + * + * @since 1.3.0 + * @copyright schukai GmbH + * @memberOf Monster.Constraints + * @summary A constraint that always invalid + */ +class Invalid extends AbstractConstraint { + + /** + * this method return a rejected promise + * + * @param {*} value + * @returns {Promise} + */ + isValid(value) { + return Promise.reject(value); + } + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {AbstractOperator} from "./abstractoperator.mjs"; + +export {AndOperator} + +/** + * Constraints are used to define conditions that must be met by the value of a variable. + * + * The uniform API of the constraints allows chains to be formed. + * + * The AndOperator is used to link several contraints. The constraint is fulfilled if all constraints of the operators are fulfilled. + * + * ``` + * <script type="module"> + * import {AndOperator} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/andoperator.mjs'; + * new AndOperator(); + * </script> + * ``` + * + * @example + * + * import {Valid} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/valid.mjs'; + * import {Invalid} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/invalid.mjs'; + * import {AndOperator} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/andoperator.mjs'; + * + * new AndOperator( + * new Valid(), new Valid()).isValid() + * .then(()=>console.log(true)) + * .catch(()=>console.log(false)); + * // ↦ true + * + * new AndOperator( + * new Invalid(), new Valid()).isValid() + * .then(()=>console.log(true)) + * .catch(()=>console.log(false)); + * // ↦ false + * + * @since 1.3.0 + * @copyright schukai GmbH + * @memberOf Monster.Constraints + * @summary A and operator constraint + */ +class AndOperator extends AbstractOperator { + + /** + * this method return a promise containing the result of the check. + * + * @param {*} value + * @returns {Promise} + */ + isValid(value) { + return Promise.all([this.operantA.isValid(value), this.operantB.isValid(value)]); + } + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {AbstractOperator} from "./abstractoperator.mjs"; + +export {OrOperator} + +/** + * Constraints are used to define conditions that must be met by the value of a variable. + * + * The uniform API of the constraints allows chains to be formed. + * + * The OrOperator is used to link several constraints. The constraint is fulfilled if one of the constraints is fulfilled. + * + * ``` + * <script type="module"> + * import {OrOperator} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraint/oroperator.mjs'; + * new OrOperator(); + * </script> + * ``` + * + * @example + * + * import {Valid} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/valid.mjs'; + * import {Invalid} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/invalid.mjs'; + * import {OrOperator} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/oroperator.mjs'; + * + * new OrOperator( + * new Valid(), new Invalid()).isValid() + * .then(()=>console.log(true)) + * .catch(()=>console.log(false)); + * // ↦ true + * + * new OrOperator( + * new Invalid(), new Invalid()).isValid() + * .then(()=>console.log(true)) + * .catch(()=>console.log(false)); + * // ↦ false + * + * @since 1.3.0 + * @copyright schukai GmbH + * @memberOf Monster.Constraints + * @summary A or operator + */ +class OrOperator extends AbstractOperator { + + /** + * this method return a promise containing the result of the check. + * + * @param {*} value + * @returns {Promise} + */ + isValid(value) { + var self = this; + + return new Promise(function (resolve, reject) { + let a, b; + + self.operantA.isValid(value) + .then(function () { + resolve(); + }).catch(function () { + a = false; + /** b has already been evaluated and was not true */ + if (b === false) { + reject(); + } + }); + + self.operantB.isValid(value) + .then(function () { + resolve(); + }).catch(function () { + b = false; + /** b has already been evaluated and was not true */ + if (a === false) { + reject(); + } + }); + }); + } + + +} +'use strict'; + +/** + * @author schukai GmbH + */ + + +import {Base} from '../types/base.mjs'; + +export {AbstractConstraint} + +/** + * Constraints are used to define conditions that must be met by the value of a variable. + * + * The uniform API of the constraints allows chains to be formed. + * + * The abstract constraint defines the api for all constraints. mainly the method isValid() is defined. + * + * derived classes must implement the method isValid(). + * + * @since 1.3.0 + * @copyright schukai GmbH + * @memberOf Monster.Constraints + * @summary The abstract constraint + */ +class AbstractConstraint extends Base { + + /** + * + */ + constructor() { + super(); + } + + /** + * this method must return a promise containing the result of the check. + * + * @param {*} value + * @returns {Promise} + */ + isValid(value) { + return Promise.reject(value); + } +} +'use strict'; + +/** + * Constraints are used to define conditions that must be met by the value of a variable so that the value can be transferred to the system. + * + * @namespace Monster.Constraints + * @memberOf Monster + * @author schukai GmbH + */ +const ns = {};'use strict'; + +/** + * @author schukai GmbH + */ + +import {isArray} from "../types/is.mjs"; +import {AbstractConstraint} from "./abstract.mjs"; + +export {IsArray} + +/** + * Constraints are used to define conditions that must be met by the value of a variable. + * + * The uniform API of the constraints allows chains to be formed. + * + * ``` + * <script type="module"> + * import {IsArray} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/isarray.mjs'; + * console.log(new IsArray()) + * </script> + * ``` + * + * @example + * + * import {IsArray} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/isarray.mjs'; + * + * new IsArray() + * .isValid([]) + * .then(()=>console.log(true)); + * // ↦ true + * + * new IsArray() + * .isValid(99) + * .catch(e=>console.log(e)); + * // ↦ 99 + * + * @since 1.3.0 + * @copyright schukai GmbH + * @memberOf Monster.Constraints + * @summary A constraint to check if a value is an array + */ +class IsArray extends AbstractConstraint { + + /** + * this method return a promise containing the result of the check. + * + * @param {*} value + * @returns {Promise} + */ + isValid(value) { + if (isArray(value)) { + return Promise.resolve(value); + } + + return Promise.reject(value); + } + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {AbstractConstraint} from "./abstract.mjs"; + +export {AbstractOperator} + +/** + * Constraints are used to define conditions that must be met by the value of a variable. + * + * The uniform API of the constraints allows chains to be formed. + * + * Operators allow you to link constraints together. for example, you can check whether a value is an object or an array. each operator has two operands that are linked together. + * + * @since 1.3.0 + * @copyright schukai GmbH + * @memberOf Monster.Constraints + * @summary The abstract operator constraint + */ +class AbstractOperator extends AbstractConstraint { + + /** + * + * @param {AbstractConstraint} operantA + * @param {AbstractConstraint} operantB + * @throws {TypeError} "parameters must be from type AbstractConstraint" + */ + constructor(operantA, operantB) { + super(); + + if (!(operantA instanceof AbstractConstraint) || !(operantB instanceof AbstractConstraint)) { + throw new TypeError("parameters must be from type AbstractConstraint") + } + + this.operantA = operantA; + this.operantB = operantB; + + } + + +} + +'use strict'; + +/** + * @author schukai GmbH + */ + +import {AbstractConstraint} from "./abstract.mjs"; + +export {Valid} + +/** + * Constraints are used to define conditions that must be met by the value of a variable. + * + * The uniform API of the constraints allows chains to be formed. + * + * The valid constraint allows an always valid query to be performed. this constraint is mainly intended for testing. + * + * ``` + * <script type="module"> + * import {Valid} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/valid.mjs'; + * new Valid(); + * </script> + * ``` + * + * @example + * + * import {Valid} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/valid.mjs'; + * + * new Valid().isValid() + * .then(()=>console.log(true)) + * .catch(()=>console.log(false)); + * // ↦ true + * + * @since 1.3.0 + * @copyright schukai GmbH + * @memberOf Monster.Constraints + * @summary A constraint that always valid + */ +class Valid extends AbstractConstraint { + + /** + * this method return a promise containing the result of the check. + * + * @param {*} value + * @returns {Promise} + */ + isValid(value) { + return Promise.resolve(value); + } + +} +/** + * @license + * Copyright 2021 schukai GmbH + * SPDX-License-Identifier: AGPL-3.0-only or COMMERCIAL + * @author schukai GmbH + */ + + +/** + * Main namespace for Monster. + * + * @namespace Monster + * @author schukai GmbH + */ +'use strict'; + +import './constants.mjs'; +// find packages/monster/source/ -type f -name "*.mjs" -not -name "*namespace*" -not -iname "monster.mjs" +import './constraints/isobject.mjs'; +import './constraints/valid.mjs'; +import './constraints/invalid.mjs'; +import './constraints/abstractoperator.mjs'; +import './constraints/oroperator.mjs'; +import './constraints/andoperator.mjs'; +import './constraints/abstract.mjs'; +import './constraints/isarray.mjs'; +import './logging/logger.mjs'; +import './logging/handler.mjs'; +import './logging/logentry.mjs'; +import './logging/handler/console.mjs'; +import './text/formatter.mjs'; +import './dom/resource/script.mjs'; +import './dom/resource/data.mjs'; +import './dom/resource/link/stylesheet.mjs'; +import './dom/resource/link.mjs'; +import './dom/resource.mjs'; +import './dom/updater.mjs'; +import './dom/attributes.mjs'; +import './dom/template.mjs'; +import './dom/util.mjs'; +import './dom/ready.mjs'; +import './dom/resourcemanager.mjs'; +import './dom/locale.mjs'; +import './dom/customcontrol.mjs'; +import './dom/constants.mjs'; +import './dom/assembler.mjs'; +import './dom/theme.mjs'; +import './dom/worker/factory.mjs'; +import './dom/focusmanager.mjs'; +import './dom/events.mjs'; +import './dom/customelement.mjs'; +import './i18n/formatter.mjs'; +import './i18n/providers/fetch.mjs'; +import './i18n/translations.mjs'; +import './i18n/locale.mjs'; +import './i18n/provider.mjs'; +import './types/queue.mjs'; +import './types/binary.mjs'; +import './types/regex.mjs'; +import './types/observer.mjs'; +import './types/observerlist.mjs'; +import './types/basewithoptions.mjs'; +import './types/is.mjs'; +import './types/proxyobserver.mjs'; +import './types/uniquequeue.mjs'; +import './types/node.mjs'; +import './types/tokenlist.mjs'; +import './types/typeof.mjs'; +import './types/uuid.mjs'; +import './types/mediatype.mjs'; +import './types/dataurl.mjs'; +import './types/base.mjs'; +import './types/version.mjs'; +import './types/nodelist.mjs'; +import './types/id.mjs'; +import './types/randomid.mjs'; +import './types/noderecursiveiterator.mjs'; +import './types/validate.mjs'; +import './types/stack.mjs'; +import './util/deadmansswitch.mjs'; +import './util/comparator.mjs'; +import './util/trimspaces.mjs'; +import './util/clone.mjs'; +import './util/freeze.mjs'; +import './util/processing.mjs'; +import './constants.mjs'; +import './data/pathfinder.mjs'; +import './data/pipe.mjs'; +import './data/extend.mjs'; +import './data/diff.mjs'; +import './data/buildmap.mjs'; +import './data/datasource.mjs'; +import './data/buildtree.mjs'; +import './data/transformer.mjs'; +import './data/datasource/storage.mjs'; +import './data/datasource/restapi.mjs'; +import './data/datasource/storage/sessionstorage.mjs'; +import './data/datasource/storage/localstorage.mjs'; +import './data/datasource/restapi/writeerror.mjs'; +import './math/random.mjs'; + +export {Monster} + +/** + * This class has no other purpose than to exist. + * + * @since 2.0.0 + * @copyright schukai GmbH + * @memberOf Monster + */ +class Monster { + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Handler} from '../logging/handler.mjs'; +import {LogEntry} from '../logging/logentry.mjs'; + +import {Base} from '../types/base.mjs'; +import {validateInteger, validateObject, validateString} from '../types/validate.mjs'; + +export {Logger, ALL, TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF}; + +/** + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF + * @type {number} + * @memberOf Monster.Logging + */ +const ALL = 255; +/** + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF + * @type {number} + * @memberOf Monster.Logging + */ +const TRACE = 64; +/** + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF + * @type {number} + * @memberOf Monster.Logging + */ +const DEBUG = 32; +/** + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF + * @type {number} + * @memberOf Monster.Logging + */ +const INFO = 16; +/** + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF + * @type {number} + * @memberOf Monster.Logging + */ +const WARN = 8; +/** + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF + * @type {number} + * @memberOf Monster.Logging + */ +const ERROR = 4; +/** + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF + * @type {number} + * @memberOf Monster.Logging + */ +const FATAL = 2; +/** + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF + * @type {number} + * @memberOf Monster.Logging + */ +const OFF = 0; + +/** + * The logger is a class that takes care of logging. + * + * ``` + * <script type="module"> + * import {ID} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/logging/logger.mjs'; + * new Logger() + * </script> + * ``` + * + * @since 1.5.0 + * @copyright schukai GmbH + * @memberOf Monster.Logging + */ +class Logger extends Base { + + /** + * + */ + constructor() { + super(); + this.handler = new Set; + } + + /** + * + * @param {Handler} handler + * @returns {Logger} + * @throws {Error} the handler must be an instance of Handler + */ + addHandler(handler) { + validateObject(handler) + if (!(handler instanceof Handler)) { + throw new Error("the handler must be an instance of Handler") + } + + this.handler.add(handler) + return this; + } + + /** + * + * @param {Handler} handler + * @returns {Logger} + * @throws {Error} the handler must be an instance of Handler + */ + removeHandler(handler) { + validateObject(handler) + if (!(handler instanceof Handler)) { + throw new Error("the handler must be an instance of Handler") + } + + this.handler.delete(handler); + return this; + } + + /** + * log Trace message + * + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF (ALL = 0xff;OFF = 0x00; + * + * @param {*} arguments + * @returns {Logger} + * @since 1.5.0 + */ + logTrace() { + triggerLog.apply(this, [TRACE, ...arguments]); + return this; + }; + + /** + * log Debug message + * + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF (ALL = 0xff;OFF = 0x00; + * + * @param {*} arguments + * @returns {Logger} + * @since 1.5.0 + */ + logDebug() { + triggerLog.apply(this, [DEBUG, ...arguments]); + return this; + }; + + /** + * log Info message + * + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF (ALL = 0xff;OFF = 0x00; + * + * + * @param {*} arguments + * @returns {Logger} + * @since 1.5.0 + */ + logInfo() { + triggerLog.apply(this, [INFO, ...arguments]); + return this; + }; + + /** + * log Warn message + * + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF (ALL = 0xff;OFF = 0x00; + * + * @param {*} arguments + * @returns {Logger} + * @since 1.5.0 + */ + logWarn() { + triggerLog.apply(this, [WARN, ...arguments]); + return this; + }; + + /** + * log Error message + * + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF (ALL = 0xff;OFF = 0x00; + * + * @param {*} arguments + * @returns {Logger} + * @since 1.5.0 + */ + logError() { + triggerLog.apply(this, [ERROR, ...arguments]); + return this; + }; + + /** + * log Fatal message + * + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF (ALL = 0xff;OFF = 0x00; + * + * @param {*} arguments + * @returns {Logger} + * @since 1.5.0 + */ + logFatal() { + triggerLog.apply(this, [FATAL, ...arguments]); + return this; + }; + + + /** + * Labels + * + * @param {integer} level + * @returns {string} + */ + getLabel(level) { + validateInteger(level); + + if (level === ALL) return 'ALL'; + if (level === TRACE) return 'TRACE'; + if (level === DEBUG) return 'DEBUG'; + if (level === INFO) return 'INFO'; + if (level === WARN) return 'WARN'; + if (level === ERROR) return 'ERROR'; + if (level === FATAL) return 'FATAL'; + if (level === OFF) return 'OFF'; + + return 'unknown'; + }; + + /** + * Level + * + * @param {string} label + * @returns {integer} + */ + getLevel(label) { + validateString(label); + + if (label === 'ALL') return ALL; + if (label === 'TRACE') return TRACE; + if (label === 'DEBUG') return DEBUG; + if (label === 'INFO') return INFO; + if (label === 'WARN') return WARN; + if (label === 'ERROR') return ERROR; + if (label === 'FATAL') return FATAL; + if (label === 'OFF') return OFF; + + return 0; + }; + + +} + + +/** + * Log triggern + * + * @param {integer} loglevel + * @param {*} args + * @returns {Logger} + * @private + */ +function triggerLog(loglevel, ...args) { + var logger = this; + + for (let handler of logger.handler) { + handler.log(new LogEntry(loglevel, args)) + } + + return logger; + +} +'use strict'; + +/** + * @author schukai GmbH + */ + + +import {Base} from '../types/base.mjs'; +import {validateInteger} from '../types/validate.mjs'; + +export {LogEntry} + +/** + * A log entry for the logger + * + * ``` + * <script type="module"> + * import {ID} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/logging/logentry.mjs'; + * console.log(new LogEntry()) + * </script> + * ``` + * + * @since 1.5.0 + * @copyright schukai GmbH + * @memberOf Monster.Logging + */ + class LogEntry extends Base { + /** + * + * @param {Integer} loglevel + * @param {...*} args + */ + constructor(loglevel, ...args) { + super(); + validateInteger(loglevel); + + this.loglevel = loglevel + this.arguments = args + } + + /** + * + * @returns {integerr} + */ + getLogLevel() { + return this.loglevel + } + + /** + * + * @returns {array} + */ + getArguments() { + return this.arguments + } + +} +'use strict'; + +/** + * Namespace for logging. + * + * @namespace Monster.Logging + * @memberOf Monster + * @author schukai GmbH + */ +const ns = {};'use strict'; + +/** + * @namespace Monster.Logging.Handler + * @memberOf Monster.Logging + * @author schukai GmbH + */ +const ns = {}; +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from '../../types/base.mjs'; +import {getGlobalObject} from "../../types/global.mjs"; +import {Handler} from '../handler.mjs'; +import {LogEntry} from "../logentry.mjs"; + +export {ConsoleHandler} + +/** + * You can create an object of the class simply by using the namespace `new Monster.Logging.Handler.ConsoleHandler()`. + * + * ``` + * <script type="module"> + * import {ConsoleHandler} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/logging/handler/console.mjs'; + * console.log(new ConsoleHandler()) + * </script> + * ``` + * + * @since 1.5.0 + * @copyright schukai GmbH + * @memberOf Monster.Logging.Handler + */ + class ConsoleHandler extends Handler { + constructor() { + super(); + } + + + /** + * This is the central log function. this method must be + * overwritten by derived handlers with their own logic. + * + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF (ALL = 0xff;OFF = 0x00; + * + * @param {LogEntry} entry + * @returns {boolean} + */ + log(entry) { + if (super.log(entry)) { + let console = getGlobalObject('console'); + if (!console) return false; + console.log(entry.toString()); + return true; + } + + return false; + } + +} + + +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from '../types/base.mjs'; +import {validateInstance, validateInteger} from "../types/validate.mjs"; +import {LogEntry} from "./logentry.mjs"; +import {ALL, DEBUG, ERROR, FATAL, INFO, OFF, TRACE, WARN} from "./logger.mjs"; + +export {Handler} + +/** + * The log handler is the interface between the log entries and the log listeners. + * + * ``` + * <script type="module"> + * import {ID} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/logging/handler.mjs'; + * console.log(new Handler()) + * </script> + * ``` + * + * @since 1.5.0 + * @copyright schukai GmbH + * @memberOf Monster.Logging + */ + class Handler extends Base { + constructor() { + super(); + + /** + * Loglevel + * + * @type {integer} + */ + this.loglevel = OFF; + } + + /** + * This is the central log function. this method must be + * overwritten by derived handlers with their own logic. + * + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF (ALL = 0xff;OFF = 0x00; + * + * @param {LogEntry} entry + * @returns {boolean} + */ + log(entry) { + validateInstance(entry, LogEntry); + + if (this.loglevel < entry.getLogLevel()) { + return false; + } + + return true; + } + + /** + * set loglevel + * + * @param {integer} loglevel + * @returns {Handler} + * @since 1.5.0 + */ + setLogLevel(loglevel) { + validateInteger(loglevel) + this.loglevel = loglevel; + return this; + } + + /** + * get loglevel + * + * @returns {integer} + * @since 1.5.0 + */ + getLogLevel() { + return this.loglevel; + } + + /** + * Set log level to All + * + * @returns {Handler} + * @since 1.5.0 + */ + setAll() { + this.setLogLevel(ALL); + return this; + }; + + /** + * Set log level to Trace + * + * @returns {Handler} + * @since 1.5.0 + */ + setTrace() { + this.setLogLevel(TRACE); + return this; + }; + + /** + * Set log level to Debug + * + * @returns {Handler} + * @since 1.5.0 + */ + setDebug() { + this.setLogLevel(DEBUG); + return this; + }; + + /** + * Set log level to Info + * + * @returns {Handler} + * @since 1.5.0 + */ + setInfo() { + this.setLogLevel(INFO); + return this; + }; + + /** + * Set log level to Warn + * + * @returns {undefined} + * @since 1.5.0 + */ + setWarn() { + this.setLogLevel(WARN); + return this; + }; + + /** + * Set log level to Error + * + * @returns {Handler} + * @since 1.5.0 + */ + setError() { + this.setLogLevel(ERROR); + return this; + }; + + /** + * Set log level to Fatal + * + * @returns {Handler} + * @since 1.5.0 + */ + setFatal() { + this.setLogLevel(FATAL); + return this; + }; + + + /** + * Set log level to Off + * + * @returns {Handler} + * @since 1.5.0 + */ + setOff() { + this.setLogLevel(OFF); + return this; + }; + + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../constants.mjs"; +import {extend} from "../data/extend.mjs"; +import {Pipe} from "../data/pipe.mjs"; + +import {BaseWithOptions} from "../types/basewithoptions.mjs"; +import {isObject, isString} from "../types/is.mjs"; +import {validateArray, validateString} from "../types/validate.mjs"; +import {getMonsterVersion} from "../types/version.mjs"; + +export {Formatter} + +/** + * @private + * @type {symbol} + */ +const internalObjectSymbol = Symbol('internalObject'); + +/** + * @private + * @type {symbol} + */ +const watchdogSymbol = Symbol('watchdog'); + +/** + * @private + * @type {symbol} + */ +const markerOpenIndexSymbol = Symbol('markerOpenIndex'); + +/** + * @private + * @type {symbol} + */ +const markerCloseIndexSymbol = Symbol('markercloseIndex'); + +/** + * @private + * @type {symbol} + */ +const workingDataSymbol = Symbol('workingData'); + + +/** + * Messages can be formatted with the formatter. To do this, an object with the values must be passed to the formatter. The message can then contain placeholders. + * + * Look at the example below. The placeholders use the logic of Pipe. + * + * ``` + * <script type="module"> + * import {Formatter} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/text/formatter.mjs'; + * new Formatter() + * </script> + * ``` + * + * ## Marker in marker + * + * Markers can be nested. Here, the inner marker is resolved first `${subkey} ↦ 1 = ${mykey2}` and then the outer marker `${mykey2}`. + * + * ``` + * const text = '${mykey${subkey}}'; + * let obj = { + * mykey2: "1", + * subkey: "2" + * }; + * + * new Formatter(obj).format(text); + * // ↦ 1 + * ``` + * + * ## Callbacks + * + * The values in a formatter can be adjusted via the commands of the `Transformer` or the`Pipe`. + * There is also the possibility to use callbacks. + * + * const formatter = new Formatter({x: '1'}, { + * callbacks: { + * quote: (value) => { + * return '"' + value + '"' + * } + * } + * }); + * + * formatter.format('${x | call:quote}')) + * // ↦ "1" + * + * ## Marker with parameter + * + * A string can also bring its own values. These must then be separated from the key by a separator `::`. + * The values themselves must be specified in key/value pairs. The key must be separated from the value by a separator `=`. + * + * When using a pipe, you must pay attention to the separators. + * + * @example + * + * import {Formatter} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/text/formatter.mjs'; + * + * new Formatter({ + * a: { + * b: { + * c: "Hello" + * }, + * d: "world", + * } + * }).format("${a.b.c} ${a.d | ucfirst}!"); // with pipe + * + * // ↦ Hello World! + * + * @since 1.12.0 + * @copyright schukai GmbH + * @memberOf Monster.Text + */ + class Formatter extends BaseWithOptions { + + /** + * Default values for the markers are `${` and `}` + * + * @param {object} object + * @throws {TypeError} value is not a object + */ + constructor(object, options) { + super(options); + this[internalObjectSymbol] = object || {} + this[markerOpenIndexSymbol] = 0; + this[markerCloseIndexSymbol] = 0; + } + + /** + * @property {object} marker + * @property {array} marker.open=["${"] + * @property {array} marker.close=["${"] + * @property {object} parameter + * @property {string} parameter.delimiter="::" + * @property {string} parameter.assignment="=" + * @property {object} callbacks={} + */ + get defaults() { + return extend({}, super.defaults, { + marker: { + open: ['${'], + close: ['}'] + }, + parameter: { + delimiter: '::', + assignment: '=' + }, + callbacks: {}, + }) + } + + + /** + * Set new Parameter Character + * + * Default values for the chars are `::` and `=` + * + * ``` + * formatter.setParameterChars('#'); + * formatter.setParameterChars('[',']'); + * formatter.setParameterChars('i18n{','}'); + * ``` + * + * @param {string} delimiter + * @param {string} assignment + * @return {Formatter} + * @since 1.24.0 + * @throws {TypeError} value is not a string + */ + setParameterChars(delimiter, assignment) { + + if (delimiter !== undefined) { + this[internalSymbol]['parameter']['delimiter'] = validateString(delimiter); + } + + if (assignment !== undefined) { + this[internalSymbol]['parameter']['assignment'] = validateString(assignment); + } + + return this; + } + + /** + * Set new Marker + * + * Default values for the markers are `${` and `}` + * + * ``` + * formatter.setMarker('#'); // open and close are both # + * formatter.setMarker('[',']'); + * formatter.setMarker('i18n{','}'); + * ``` + * + * @param {array|string} open + * @param {array|string|undefined} close + * @return {Formatter} + * @since 1.12.0 + * @throws {TypeError} value is not a string + */ + setMarker(open, close) { + + if (close === undefined) { + close = open; + } + + if (isString(open)) open = [open]; + if (isString(close)) close = [close]; + + this[internalSymbol]['marker']['open'] = validateArray(open); + this[internalSymbol]['marker']['close'] = validateArray(close); + return this; + } + + /** + * + * @param {string} text + * @return {string} + * @throws {TypeError} value is not a string + * @throws {Error} too deep nesting + */ + format(text) { + this[watchdogSymbol] = 0; + this[markerOpenIndexSymbol] = 0; + this[markerCloseIndexSymbol] = 0; + this[workingDataSymbol] = {}; + return format.call(this, text); + } + +} + +/** + * @private + * @return {string} + */ +function format(text) { + const self = this; + + self[watchdogSymbol]++; + if (this[watchdogSymbol] > 20) { + throw new Error('too deep nesting') + } + + let openMarker = self[internalSymbol]['marker']['open']?.[this[markerOpenIndexSymbol]]; + let closeMarker = self[internalSymbol]['marker']['close']?.[this[markerCloseIndexSymbol]]; + + // contains no placeholders + if (text.indexOf(openMarker) === -1 || text.indexOf(closeMarker) === -1) { + return text; + } + + let result = tokenize.call(this, validateString(text), openMarker, closeMarker) + + if (self[internalSymbol]['marker']['open']?.[this[markerOpenIndexSymbol] + 1]) { + this[markerOpenIndexSymbol]++; + } + + if (self[internalSymbol]['marker']['close']?.[this[markerCloseIndexSymbol] + 1]) { + this[markerCloseIndexSymbol]++; + } + + result = format.call(self, result) + + return result; +} + +/** + * @private + * @since 1.12.0 + * @param text + * @return {string} + */ +function tokenize(text, openMarker, closeMarker) { + const self = this; + + let formatted = []; + + const parameterAssignment = self[internalSymbol]['parameter']['assignment'] + const parameterDelimiter = self[internalSymbol]['parameter']['delimiter'] + const callbacks = self[internalSymbol]['callbacks']; + + while (true) { + + let startIndex = text.indexOf(openMarker); + // no marker + if (startIndex === -1) { + formatted.push(text); + break; + } else if (startIndex > 0) { + formatted.push(text.substring(0, startIndex)) + text = text.substring(startIndex) + } + + let endIndex = text.substring(openMarker.length).indexOf(closeMarker); + if (endIndex !== -1) endIndex += openMarker.length; + let insideStartIndex = text.substring(openMarker.length).indexOf(openMarker); + if (insideStartIndex !== -1) { + insideStartIndex += openMarker.length; + if (insideStartIndex < endIndex) { + let result = tokenize.call(self, text.substring(insideStartIndex), openMarker, closeMarker); + text = text.substring(0, insideStartIndex) + result + endIndex = text.substring(openMarker.length).indexOf(closeMarker); + if (endIndex !== -1) endIndex += openMarker.length; + } + } + + if (endIndex === -1) { + throw new Error("syntax error in formatter template") + return; + } + + let key = text.substring(openMarker.length, endIndex); + let parts = key.split(parameterDelimiter); + let currentPipe = parts.shift(); + + self[workingDataSymbol] = extend({}, self[internalObjectSymbol], self[workingDataSymbol]); + + for (const kv of parts) { + const [k, v] = kv.split(parameterAssignment); + self[workingDataSymbol][k] = v; + } + + const t1 = key.split('|').shift().trim(); // pipe symbol + const t2 = t1.split('::').shift().trim(); // key value delimiter + const t3 = t2.split('.').shift().trim(); // path delimiter + let prefix = self[workingDataSymbol]?.[t3] ? 'path:' : 'static:'; + + let command = ""; + if (prefix && key.indexOf(prefix) !== 0 + && key.indexOf('path:') !== 0 + && key.indexOf('static:') !== 0) { + command = prefix; + } + + command += currentPipe; + + const pipe = new Pipe(command); + + if (isObject(callbacks)) { + for (const [name, callback] of Object.entries(callbacks)) { + pipe.setCallback(name, callback); + } + } + + formatted.push(validateString(pipe.run(self[workingDataSymbol]))); + + text = text.substring(endIndex + closeMarker.length); + + } + + return formatted.join(''); +} +'use strict'; + +/** + * Namespace for texts. + * + * @namespace Monster.Text + * @memberOf Monster + * @author schukai GmbH + */ +const ns = {}; +'use strict'; + +/** + * @author schukai GmbH + */ + +import {extend} from "../../../data/extend.mjs"; +import {Link} from "../link.mjs"; + +export {Stylesheet} + +/** + * This class is used by the resource manager to embed external resources. + * + * ``` + * <script type="module"> + * import {Style} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/resource/link/stylesheet.mjs'; + * new Stylesheet() + * </script> + * ``` + * + * @since 1.25.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM.Resource + * @summary A Resource class + * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link + */ +class Stylesheet extends Link { + + /** + * @property {string} rel {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-rel} + */ + get defaults() { + return extend({}, super.defaults, { + rel: 'stylesheet' + }) + } + +} + +'use strict'; + +/** + * In this namespace you will find classes and methods for links + * + * @namespace Monster.DOM.Resource.Link + * @memberOf Monster + * @author schukai GmbH + */ +const ns = {};'use strict'; + +/** + * In this namespace you will find classes and methods for handling resources. + * + * @namespace Monster.DOM.Resource + * @memberOf Monster + * @author schukai GmbH + */ +const ns = {}; +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalStateSymbol} from "../../constants.mjs"; +import {extend} from "../../data/extend.mjs"; +import {getGlobalFunction} from "../../types/global.mjs"; +import { + ATTRIBUTE_CLASS, + ATTRIBUTE_ERRORMESSAGE, + ATTRIBUTE_ID, + ATTRIBUTE_SRC, + ATTRIBUTE_TITLE, + ATTRIBUTE_TYPE, + TAG_SCRIPT +} from "../constants.mjs"; +import {KEY_DOCUMENT, KEY_QUERY, referenceSymbol, Resource} from "../resource.mjs"; + +export {Data} + +/** + * This class is used by the resource manager to embed data. + * + * ``` + * <script type="module"> + * import {Data} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/resource/data.mjs'; + * new Data() + * </script> + * ``` + * + * @since 1.25.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM.Resource + * @summary A Data Resource class + */ +class Data extends Resource { + + /** + * @property {string} mode=cors https://developer.mozilla.org/en-US/docs/Web/API/fetch + * @property {string} credentials=same-origin https://developer.mozilla.org/en-US/docs/Web/API/fetch + * @property {string} type=application/json {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-type} + */ + get defaults() { + return extend({}, super.defaults, { + mode: 'cors', + credentials: 'same-origin', + type: 'application/json', + }) + } + + /** + * + * @return {Monster.DOM.Resource.Data} + */ + create() { + createElement.call(this); + return this; + } + + /** + * This method appends the HTMLElement to the specified document + * + * throws {Error} target not found + * @return {Monster.DOM.Resource} + */ + connect() { + + if (!(this[referenceSymbol] instanceof HTMLElement)) { + this.create(); + } + + appendToDocument.call(this); + return this; + } + + /** + * @return {string} + */ + static getURLAttribute() { + return ATTRIBUTE_SRC + } + +} + +/** + * @private + * @return {Monster.DOM.Resource.Data} + */ +function createElement() { + const self = this; + + const document = self.getOption(KEY_DOCUMENT); + self[referenceSymbol] = document.createElement(TAG_SCRIPT); + + for (let key of [ATTRIBUTE_TYPE, ATTRIBUTE_ID, ATTRIBUTE_CLASS, ATTRIBUTE_TITLE]) { + if (self.getOption(key) !== undefined) { + self[referenceSymbol][key] = self.getOption(key); + } + } + + return self; +} + + +/** + * @private + * @return {Promise} + * throws {Error} target not found + */ +function appendToDocument() { + const self = this; + + const targetNode = document.querySelector(self.getOption(KEY_QUERY, 'head')) + if (!(targetNode instanceof HTMLElement)) { + throw new Error('target not found') + } + + targetNode.appendChild(self[referenceSymbol]); + + getGlobalFunction('fetch')(self.getOption(ATTRIBUTE_SRC), { + method: 'GET', // *GET, POST, PUT, DELETE, etc. + mode: self.getOption('mode', 'cors'), // no-cors, *cors, same-origin + cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached + credentials: self.getOption('credentials', 'same-origin'), // include, *same-origin, omit + headers: { + 'Accept': self.getOption('type', 'application/json') + }, + redirect: 'follow', // manual, *follow, error + referrerPolicy: 'no-referrer', // no-referrer, + }).then(response => { + + return response.text() + + + }).then(text => { + + const textNode = document.createTextNode(text); + self[referenceSymbol].appendChild(textNode); + + self[internalStateSymbol].getSubject()['loaded'] = true; + + + }).catch(e => { + self[internalStateSymbol].setSubject({ + loaded: true, + error: e.toString(), + }) + + targetNode.setAttribute(ATTRIBUTE_ERRORMESSAGE, e.toString()); + }) + + return self; +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {extend} from "../../data/extend.mjs"; +import { + ATTRIBUTE_CLASS, + ATTRIBUTE_HREF, + ATTRIBUTE_ID, + ATTRIBUTE_NONCE, ATTRIBUTE_SRC, + ATTRIBUTE_TITLE, ATTRIBUTE_TYPE, + TAG_LINK +} from "../constants.mjs"; +import {KEY_DOCUMENT, referenceSymbol, Resource} from "../resource.mjs"; + +export {Link} + +/** + * This class is used by the resource manager to embed external resources. + * + * ``` + * <script type="module"> + * import {Link} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/resource/link.mjs'; + * new Link() + * </script> + * ``` + * + * @since 1.25.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM.Resource + * @summary A Resource class + * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link + */ +class Link extends Resource { + + /** + * @property {string} as {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-as} + * @property {string} crossOrigin=anonymous {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-crossorigin} + * @property {boolean} disabled + * @property {string} href {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-href} + * @property {string} hreflang {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-hreflang} + * @property {string} imagesizes {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-imagesizes} + * @property {string} imagesrcset {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-imagesrcset} + * @property {string} integrity {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-integrity} + * @property {string} media {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-media} + * @property {string} prefetch {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-prefetch} + * @property {string} referrerpolicy {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-referrerpolicy} + * @property {string} rel {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-rel} + * @property {string} type {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-type} + * @property {string} sizes {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-sizes} + * @property {string} nonce {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-nonce} + */ + get defaults() { + return extend({}, super.defaults, { + as: undefined, + crossOrigin: 'anonymous', + disabled: undefined, + href: undefined, + hreflang: undefined, + imagesizes: undefined, + imagesrcset: undefined, + integrity: undefined, + media: undefined, + prefetch: undefined, + referrerpolicy: undefined, + rel: undefined, + sizes: undefined, + type: undefined, + nonce: undefined + }) + } + + /** + * + * @return {Monster.DOM.Resource.Link} + */ + create() { + createElement.call(this); + return this; + } + + /** + * @return {string} + */ + static getURLAttribute() { + return ATTRIBUTE_HREF + } + +} + +/** + * @private + * @return {Monster.DOM.Resource.Link} + */ +function createElement() { + const self = this; + + const document = self.getOption(KEY_DOCUMENT); + self[referenceSymbol] = document.createElement(TAG_LINK); + + for (let key of ['as','crossOrigin','disabled','href','hreflang','imagesizes','imagesrcset','integrity','media','prefetch','referrerpolicy','sizes','rel','type',ATTRIBUTE_HREF,ATTRIBUTE_ID,ATTRIBUTE_CLASS,ATTRIBUTE_TITLE,ATTRIBUTE_NONCE]) { + if (self.getOption(key) !== undefined) { + self[referenceSymbol][key] = self.getOption(key); + } + } + + return self; +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {extend} from "../../data/extend.mjs"; +import { + ATTRIBUTE_CLASS, + ATTRIBUTE_ID, + ATTRIBUTE_NONCE, + ATTRIBUTE_SRC, + ATTRIBUTE_TITLE, + ATTRIBUTE_TYPE, + TAG_SCRIPT +} from "../constants.mjs"; +import {KEY_DOCUMENT, referenceSymbol, Resource} from "../resource.mjs"; + +export {Script} + +/** + * This class is used by the resource manager to embed scripts. + * + * ``` + * <script type="module"> + * import {Script} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/resource/script.mjs'; + * new Script() + * </script> + * ``` + * + * @since 1.25.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM.Resource + * @summary A Resource class + */ +class Script extends Resource { + + /** + * @property {boolean} async=true {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-async} + * @property {string} crossOrigin=anonymous {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-crossorigin} + * @property {boolean} defer=false {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-defer} + * @property {string} integrity {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-integrity} + * @property {boolean} nomodule {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-nomodule} + * @property {string} nonce {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-nonce} + * @property {string} referrerpolicy {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-referrerpolicy} + * @property {string} type {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-type} + */ + get defaults() { + return extend({}, super.defaults, { + async: true, + crossOrigin: 'anonymous', + defer: false, + integrity: undefined, + nomodule: false, + nonce: undefined, + referrerpolicy: undefined, + type: 'text/javascript', + }) + } + + /** + * + * @return {Monster.DOM.Resource.Script} + */ + create() { + createElement.call(this); + return this; + } + + /** + * @return {string} + */ + static getURLAttribute() { + return ATTRIBUTE_SRC + } + +} + +/** + * @private + * @return {Monster.DOM.Resource.Script} + */ +function createElement() { + const self = this; + + const document = self.getOption(KEY_DOCUMENT); + self[referenceSymbol] = document.createElement(TAG_SCRIPT); + + for (let key of ['crossOrigin', 'defer', 'async', 'integrity', 'nomodule', ATTRIBUTE_NONCE, 'referrerpolicy', ATTRIBUTE_TYPE, ATTRIBUTE_SRC, ATTRIBUTE_ID, ATTRIBUTE_CLASS, ATTRIBUTE_TITLE]) { + if (self.getOption(key) !== undefined) { + self[referenceSymbol][key] = self.getOption(key); + } + } + + + return self; +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../constants.mjs"; +import {diff} from "../data/diff.mjs"; +import {Pathfinder} from "../data/pathfinder.mjs"; +import {Pipe} from "../data/pipe.mjs"; +import { + ATTRIBUTE_ERRORMESSAGE, + ATTRIBUTE_UPDATER_ATTRIBUTES, + ATTRIBUTE_UPDATER_BIND, + ATTRIBUTE_UPDATER_INSERT, + ATTRIBUTE_UPDATER_INSERT_REFERENCE, + ATTRIBUTE_UPDATER_REMOVE, + ATTRIBUTE_UPDATER_REPLACE, + ATTRIBUTE_UPDATER_SELECT_THIS +} from "../dom/constants.mjs"; + +import {Base} from "../types/base.mjs"; +import {isArray, isInstance, isIterable} from "../types/is.mjs"; +import {Observer} from "../types/observer.mjs"; +import {ProxyObserver} from "../types/proxyobserver.mjs"; +import {validateArray, validateInstance} from "../types/validate.mjs"; +import {clone} from "../util/clone.mjs"; +import {trimSpaces} from "../util/trimspaces.mjs"; +import {findTargetElementFromEvent} from "./events.mjs"; +import {findDocumentTemplate} from "./template.mjs"; +import {getDocument} from "./util.mjs"; + +export {Updater} + +/** + * The updater class connects an object with the dom. In this way, structures and contents in the DOM can be programmatically adapted via attributes. + * + * For example, to include a string from an object, the attribute `data-monster-replace` can be used. + * a further explanation can be found under {@tutorial dom-based-templating-implementation}. + * + * Changes to attributes are made only when the direct values are changed. If you want to assign changes to other values + * as well, you have to insert the attribute `data-monster-select-this`. This should be done with care, as it can reduce performance. + * + * ``` + * <script type="module"> + * import {Updater} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/updater.mjs'; + * new Updater() + * </script> + * ``` + * + * @example + * + * import {Updater} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/updater.mjs'; + * + * // First we prepare the html document. + * // This is done here via script, but can also be inserted into the document as pure html. + * // To do this, simply insert the tag <h1 data-monster-replace="path:headline"></h1>. + * const body = document.querySelector('body'); + * const headline = document.createElement('h1'); + * headline.setAttribute('data-monster-replace','path:headline') + * body.appendChild(headline); + * + * // the data structure + * let obj = { + * headline: "Hello World", + * }; + * + * // Now comes the real magic. we pass the updater the parent HTMLElement + * // and the desired data structure. + * const updater = new Updater(body, obj); + * updater.run(); + * + * // Now you can change the data structure and the HTML will follow these changes. + * const subject = updater.getSubject(); + * subject['headline'] = "Hello World!" + * + * @since 1.8.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @throws {Error} the value is not iterable + * @throws {Error} pipes are not allowed when cloning a node. + * @throws {Error} no template was found with the specified key. + * @throws {Error} the maximum depth for the recursion is reached. + * @throws {TypeError} value is not a object + * @throws {TypeError} value is not an instance of HTMLElement + * @summary The updater class connects an object with the dom + */ +class Updater extends Base { + + /** + * @since 1.8.0 + * @param {HTMLElement} element + * @param {object|ProxyObserver|undefined} subject + * @throws {TypeError} value is not a object + * @throws {TypeError} value is not an instance of HTMLElement + * @see {@link Monster.DOM.findDocumentTemplate} + */ + constructor(element, subject) { + super(); + + /** + * @type {HTMLElement} + */ + if (subject === undefined) subject = {} + if (!isInstance(subject, ProxyObserver)) { + subject = new ProxyObserver(subject); + } + + this[internalSymbol] = { + element: validateInstance(element, HTMLElement), + last: {}, + callbacks: new Map(), + eventTypes: ['keyup', 'click', 'change', 'drop', 'touchend', 'input'], + subject: subject + } + + this[internalSymbol].callbacks.set('checkstate', getCheckStateCallback.call(this)); + + this[internalSymbol].subject.attachObserver(new Observer(() => { + + const s = this[internalSymbol].subject.getRealSubject(); + + const diffResult = diff(this[internalSymbol].last, s) + this[internalSymbol].last = clone(s); + + for (const [, change] of Object.entries(diffResult)) { + removeElement.call(this, change); + insertElement.call(this, change); + updateContent.call(this, change); + updateAttributes.call(this, change); + } + })); + + } + + /** + * Defaults: 'keyup', 'click', 'change', 'drop', 'touchend' + * + * @see {@link https://developer.mozilla.org/de/docs/Web/Events} + * @since 1.9.0 + * @param {Array} types + * @return {Updater} + */ + setEventTypes(types) { + this[internalSymbol].eventTypes = validateArray(types); + return this; + } + + /** + * With this method, the eventlisteners are hooked in and the magic begins. + * + * ``` + * updater.run().then(() => { + * updater.enableEventProcessing(); + * }); + * ``` + * + * @since 1.9.0 + * @return {Updater} + * @throws {Error} the bind argument must start as a value with a path + */ + enableEventProcessing() { + this.disableEventProcessing(); + + for (const type of this[internalSymbol].eventTypes) { + // @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener + this[internalSymbol].element.addEventListener(type, getControlEventHandler.call(this), { + capture: true, + passive: true + }); + } + + return this; + + } + + /** + * This method turns off the magic or who loves it more profane it removes the eventListener. + * + * @since 1.9.0 + * @return {Updater} + */ + disableEventProcessing() { + + for (const type of this[internalSymbol].eventTypes) { + this[internalSymbol].element.removeEventListener(type, getControlEventHandler.call(this)); + } + + return this; + + } + + /** + * The run method must be called for the update to start working. + * The method ensures that changes are detected. + * + * ``` + * updater.run().then(() => { + * updater.enableEventProcessing(); + * }); + * ``` + * + * @summary Let the magic begin + * @return {Promise} + */ + run() { + // the key __init__has no further meaning and is only + // used to create the diff for empty objects. + this[internalSymbol].last = {'__init__': true}; + return this[internalSymbol].subject.notifyObservers(); + } + + /** + * Gets the values of bound elements and changes them in subject + * + * @since 1.27.0 + * @return {Monster.DOM.Updater} + */ + retrieve() { + retrieveFromBindings.call(this); + return this; + } + + /** + * If you have passed a ProxyObserver in the constructor, you will get the object that the ProxyObserver manages here. + * However, if you passed a simple object, here you will get a proxy for that object. + * + * For changes the ProxyObserver must be used. + * + * @since 1.8.0 + * @return {Proxy} + */ + getSubject() { + return this[internalSymbol].subject.getSubject(); + } + + /** + * This method can be used to register commands that can be called via call: instruction. + * This can be used to provide a pipe with its own functionality. + * + * @param {string} name + * @param {function} callback + * @returns {Transformer} + * @throws {TypeError} value is not a string + * @throws {TypeError} value is not a function + */ + setCallback(name, callback) { + this[internalSymbol].callbacks.set(name, callback); + return this; + } + +} + +/** + * @private + * @since 1.9.0 + * @return {function + * @this Updater + */ +function getCheckStateCallback() { + const self = this; + + return function (current) { + + // this is a reference to the current object (therefore no array function here) + if (this instanceof HTMLInputElement) { + if (['radio', 'checkbox'].indexOf(this.type) !== -1) { + return (this.value + "" === current + "") ? 'true' : undefined + } + } else if (this instanceof HTMLOptionElement) { + + if (isArray(current) && current.indexOf(this.value) !== -1) { + return 'true' + } + + return undefined; + } + } +} + +/** + * @private + */ +const symbol = Symbol('EventHandler'); + +/** + * @private + * @return {function} + * @this Updater + * @throws {Error} the bind argument must start as a value with a path + */ +function getControlEventHandler() { + + const self = this; + + if (self[symbol]) { + return self[symbol]; + } + + /** + * @throws {Error} the bind argument must start as a value with a path. + * @throws {Error} unsupported object + * @param {Event} event + */ + self[symbol] = (event) => { + const element = findTargetElementFromEvent(event, ATTRIBUTE_UPDATER_BIND); + + if (element === undefined) { + return; + } + + retrieveAndSetValue.call(self, element); + + } + + return self[symbol]; + + +} + +/** + * @throws {Error} the bind argument must start as a value with a path + * @param {HTMLElement} element + * @return void + * @memberOf Monster.DOM + * @private + */ +function retrieveAndSetValue(element) { + + const self = this; + + const pathfinder = new Pathfinder(self[internalSymbol].subject.getSubject()); + + let path = element.getAttribute(ATTRIBUTE_UPDATER_BIND); + + if (path.indexOf('path:') !== 0) { + throw new Error('the bind argument must start as a value with a path'); + } + + path = path.substr(5); + + let value; + + if (element instanceof HTMLInputElement) { + switch (element.type) { + + case 'checkbox': + value = element.checked ? element.value : undefined; + break; + default: + value = element.value; + break; + + + } + } else if (element instanceof HTMLTextAreaElement) { + value = element.value; + + } else if (element instanceof HTMLSelectElement) { + + switch (element.type) { + case 'select-one': + value = element.value; + break; + case 'select-multiple': + value = element.value; + + let options = element?.selectedOptions; + if (options === undefined) options = element.querySelectorAll(":scope option:checked"); + value = Array.from(options).map(({value}) => value); + + break; + } + + + // values from customelements + } else if ((element?.constructor?.prototype && !!Object.getOwnPropertyDescriptor(element.constructor.prototype, 'value')?.['get']) || element.hasOwnProperty('value')) { + value = element?.['value']; + } else { + throw new Error("unsupported object"); + } + + const copy = clone(self[internalSymbol].subject.getRealSubject()); + const pf = new Pathfinder(copy); + pf.setVia(path, value); + + const diffResult = diff(copy, self[internalSymbol].subject.getRealSubject()); + + if (diffResult.length > 0) { + pathfinder.setVia(path, value); + } +} + +/** + * @since 1.27.0 + * @return void + * @private + */ +function retrieveFromBindings() { + const self = this; + + if (self[internalSymbol].element.matches('[' + ATTRIBUTE_UPDATER_BIND + ']')) { + retrieveAndSetValue.call(self, element) + } + + for (const [, element] of self[internalSymbol].element.querySelectorAll('[' + ATTRIBUTE_UPDATER_BIND + ']').entries()) { + retrieveAndSetValue.call(self, element) + } + +} + +/** + * @private + * @since 1.8.0 + * @param {object} change + * @return {void} + */ +function removeElement(change) { + const self = this; + + for (const [, element] of self[internalSymbol].element.querySelectorAll(':scope [' + ATTRIBUTE_UPDATER_REMOVE + ']').entries()) { + element.parentNode.removeChild(element); + } +} + +/** + * @private + * @since 1.8.0 + * @param {object} change + * @return {void} + * @throws {Error} the value is not iterable + * @throws {Error} pipes are not allowed when cloning a node. + * @throws {Error} no template was found with the specified key. + * @throws {Error} the maximum depth for the recursion is reached. + * @this Updater + */ +function insertElement(change) { + const self = this; + const subject = self[internalSymbol].subject.getRealSubject(); + const document = getDocument(); + + let mem = new WeakSet; + let wd = 0; + + const container = self[internalSymbol].element; + + while (true) { + let found = false; + wd++; + + let p = clone(change?.['path']); + if (!isArray(p)) return self; + + while (p.length > 0) { + const current = p.join('.'); + + let iterator = new Set; + const query = '[' + ATTRIBUTE_UPDATER_INSERT + '*="path:' + current + '"]'; + + const e = container.querySelectorAll(query); + + if (e.length > 0) { + iterator = new Set( + [...e] + ) + } + + if (container.matches(query)) { + iterator.add(container); + } + + for (const [, containerElement] of iterator.entries()) { + + if (mem.has(containerElement)) continue; + mem.add(containerElement) + + found = true; + + const attributes = containerElement.getAttribute(ATTRIBUTE_UPDATER_INSERT); + let def = trimSpaces(attributes); + let i = def.indexOf(' '); + let key = trimSpaces(def.substr(0, i)); + let refPrefix = key + '-'; + let cmd = trimSpaces(def.substr(i)); + + // this case is actually excluded by the query but is nevertheless checked again here + if (cmd.indexOf('|') > 0) { + throw new Error("pipes are not allowed when cloning a node."); + } + + let pipe = new Pipe(cmd); + self[internalSymbol].callbacks.forEach((f, n) => { + pipe.setCallback(n, f); + }) + + let value + try { + containerElement.removeAttribute(ATTRIBUTE_ERRORMESSAGE); + value = pipe.run(subject) + } catch (e) { + containerElement.setAttribute(ATTRIBUTE_ERRORMESSAGE, e.message); + } + + let dataPath = cmd.split(':').pop(); + + let insertPoint; + if (containerElement.hasChildNodes()) { + insertPoint = containerElement.lastChild; + } + + if (!isIterable(value)) { + throw new Error('the value is not iterable'); + } + + let available = new Set; + + for (const [i, obj] of Object.entries(value)) { + let ref = refPrefix + i; + let currentPath = dataPath + "." + i; + + available.add(ref); + let refElement = containerElement.querySelector('[' + ATTRIBUTE_UPDATER_INSERT_REFERENCE + '="' + ref + '"]'); + + if (refElement instanceof HTMLElement) { + insertPoint = refElement; + continue; + } + + appendNewDocumentFragment(containerElement, key, ref, currentPath); + } + + let nodes = containerElement.querySelectorAll('[' + ATTRIBUTE_UPDATER_INSERT_REFERENCE + '*="' + refPrefix + '"]'); + for (const [, node] of Object.entries(nodes)) { + if (!available.has(node.getAttribute(ATTRIBUTE_UPDATER_INSERT_REFERENCE))) { + try { + containerElement.removeChild(node); + } catch (e) { + containerElement.setAttribute(ATTRIBUTE_ERRORMESSAGE, (containerElement.getAttribute(ATTRIBUTE_ERRORMESSAGE) + ", " + e.message).trim()); + } + + } + } + } + + p.pop(); + } + + if (found === false) break; + if (wd++ > 200) { + throw new Error('the maximum depth for the recursion is reached.'); + } + + } + + +} + +/** + * + * @private + * @since 1.8.0 + * @param {HTMLElement} container + * @param {string} key + * @param {string} ref + * @param {string} path + * @throws {Error} no template was found with the specified key. + */ +function appendNewDocumentFragment(container, key, ref, path) { + + let template = findDocumentTemplate(key, container); + + let nodes = template.createDocumentFragment(); + for (const [, node] of Object.entries(nodes.childNodes)) { + if (node instanceof HTMLElement) { + + applyRecursive(node, key, path); + node.setAttribute(ATTRIBUTE_UPDATER_INSERT_REFERENCE, ref); + } + + container.appendChild(node); + } +} + +/** + * @private + * @since 1.10.0 + * @param {HTMLElement} node + * @param {string} key + * @param {string} path + * @return {void} + */ +function applyRecursive(node, key, path) { + + if (node instanceof HTMLElement) { + + if (node.hasAttribute(ATTRIBUTE_UPDATER_REPLACE)) { + let value = node.getAttribute(ATTRIBUTE_UPDATER_REPLACE); + node.setAttribute(ATTRIBUTE_UPDATER_REPLACE, value.replaceAll("path:" + key, "path:" + path)); + } + + if (node.hasAttribute(ATTRIBUTE_UPDATER_ATTRIBUTES)) { + let value = node.getAttribute(ATTRIBUTE_UPDATER_ATTRIBUTES); + node.setAttribute(ATTRIBUTE_UPDATER_ATTRIBUTES, value.replaceAll("path:" + key, "path:" + path)); + } + + for (const [, child] of Object.entries(node.childNodes)) { + applyRecursive(child, key, path); + } + } +} + +/** + * @private + * @since 1.8.0 + * @param {object} change + * @return {void} + * @this Updater + */ +function updateContent(change) { + const self = this; + const subject = self[internalSymbol].subject.getRealSubject(); + + let p = clone(change?.['path']); + runUpdateContent.call(this, this[internalSymbol].element, p, subject); + + const slots = this[internalSymbol].element.querySelectorAll('slot'); + if (slots.length > 0) { + for (const [, slot] of Object.entries(slots)) { + for (const [, element] of Object.entries(slot.assignedNodes())) { + runUpdateContent.call(this, element, p, subject); + } + } + } + + +} + +/** + * @private + * @since 1.8.0 + * @param {HTMLElement} container + * @param {array} parts + * @param {object} subject + * @return {void} + */ +function runUpdateContent(container, parts, subject) { + if (!isArray(parts)) return; + if (!(container instanceof HTMLElement)) return; + parts = clone(parts); + + let mem = new WeakSet; + + while (parts.length > 0) { + const current = parts.join('.'); + parts.pop(); + + // Unfortunately, static data is always changed as well, since it is not possible to react to changes here. + const query = '[' + ATTRIBUTE_UPDATER_REPLACE + '^="path:' + current + '"], [' + ATTRIBUTE_UPDATER_REPLACE + '^="static:"]'; + const e = container.querySelectorAll('' + query); + + const iterator = new Set([ + ...e + ]) + + if (container.matches(query)) { + iterator.add(container); + } + + /** + * @type {HTMLElement} + */ + for (const [element] of iterator.entries()) { + + if (mem.has(element)) return; + mem.add(element) + + const attributes = element.getAttribute(ATTRIBUTE_UPDATER_REPLACE) + let cmd = trimSpaces(attributes); + + let pipe = new Pipe(cmd); + this[internalSymbol].callbacks.forEach((f, n) => { + pipe.setCallback(n, f); + }) + + let value + try { + element.removeAttribute(ATTRIBUTE_ERRORMESSAGE); + value = pipe.run(subject) + } catch (e) { + element.setAttribute(ATTRIBUTE_ERRORMESSAGE, e.message); + } + + if (value instanceof HTMLElement) { + while (element.firstChild) { + element.removeChild(element.firstChild); + } + + try { + element.appendChild(value); + } catch (e) { + element.setAttribute(ATTRIBUTE_ERRORMESSAGE, (element.getAttribute(ATTRIBUTE_ERRORMESSAGE) + ", " + e.message).trim()); + } + + } else { + element.innerHTML = value; + } + + } + + + } + +} + +/** + * @private + * @since 1.8.0 + * @param {string} path + * @param {object} change + * @return {void} + */ +function updateAttributes(change) { + const subject = this[internalSymbol].subject.getRealSubject(); + let p = clone(change?.['path']); + runUpdateAttributes.call(this, this[internalSymbol].element, p, subject); +} + +/** + * @private + * @param {HTMLElement} container + * @param {array} parts + * @param {object} subject + * @return {void} + * @this Updater + */ +function runUpdateAttributes(container, parts, subject) { + + const self = this; + + if (!isArray(parts)) return; + parts = clone(parts); + + let mem = new WeakSet; + + while (parts.length > 0) { + const current = parts.join('.'); + parts.pop(); + + let iterator = new Set; + + const query = '[' + ATTRIBUTE_UPDATER_SELECT_THIS + '], [' + ATTRIBUTE_UPDATER_ATTRIBUTES + '*="path:' + current + '"], [' + ATTRIBUTE_UPDATER_ATTRIBUTES + '^="static:"]'; + + const e = container.querySelectorAll(query); + + if (e.length > 0) { + iterator = new Set( + [...e] + ) + } + + if (container.matches(query)) { + iterator.add(container); + } + + for (const [element] of iterator.entries()) { + + if (mem.has(element)) return; + mem.add(element) + + const attributes = element.getAttribute(ATTRIBUTE_UPDATER_ATTRIBUTES) + + for (let [, def] of Object.entries(attributes.split(','))) { + def = trimSpaces(def); + let i = def.indexOf(' '); + let name = trimSpaces(def.substr(0, i)); + let cmd = trimSpaces(def.substr(i)); + + let pipe = new Pipe(cmd); + + self[internalSymbol].callbacks.forEach((f, n) => { + pipe.setCallback(n, f, element); + }) + + let value + try { + element.removeAttribute(ATTRIBUTE_ERRORMESSAGE); + value = pipe.run(subject) + } catch (e) { + element.setAttribute(ATTRIBUTE_ERRORMESSAGE, e.message); + } + + + if (value === undefined) { + element.removeAttribute(name) + + } else if (element.getAttribute(name) !== value) { + element.setAttribute(name, value); + } + + handleInputControlAttributeUpdate.call(this, element, name, value); + + } + } + } + +} + +/** + * @private + * @param {HTMLElement|*} element + * @param {string} name + * @param {string|number|undefined} value + * @return {void} + * @this Updater + */ + +function handleInputControlAttributeUpdate(element, name, value) { + const self = this; + + if (element instanceof HTMLSelectElement) { + + + switch (element.type) { + case 'select-multiple': + + for (const [index, opt] of Object.entries(element.options)) { + if (value.indexOf(opt.value) !== -1) { + opt.selected = true; + } else { + opt.selected = false; + } + } + + break; + case 'select-one': + // Only one value may be selected + + for (const [index, opt] of Object.entries(element.options)) { + if (opt.value === value) { + element.selectedIndex = index; + break; + } + } + + break; + } + + + } else if (element instanceof HTMLInputElement) { + switch (element.type) { + + case 'radio': + if (name === 'checked') { + + if (value !== undefined) { + element.checked = true; + } else { + element.checked = false; + } + } + + break; + + case 'checkbox': + + if (name === 'checked') { + + if (value !== undefined) { + element.checked = true; + } else { + element.checked = false; + } + } + + break; + case 'text': + default: + if (name === 'value') { + element.value = (value === undefined ? "" : value); + } + + break; + + + } + } else if (element instanceof HTMLTextAreaElement) { + if (name === 'value') { + element.value = (value === undefined ? "" : value); + } + } + +} +'use strict'; + +import {extend} from "../data/extend.mjs"; +/** + * @author schukai GmbH + */ + +import {ATTRIBUTE_VALUE} from "./constants.mjs"; +import {CustomElement, attributeObserverSymbol} from "./customelement.mjs"; + +export {CustomControl} + +/** + * @private + * @type {symbol} + */ +const attachedInternalSymbol = Symbol('attachedInternal'); + +/** + * To define a new HTML control we need the power of CustomElement + * + * IMPORTANT: after defining a `CustomElement`, the `registerCustomElement` method must be called + * with the new class name. only then will the tag defined via the `getTag` method be made known to the DOM. + * + * <img src="./images/customcontrol-class.png"> + * + * This control uses `attachInternals()` to integrate the control into a form. + * If the target environment does not support this method, the [polyfill](https://www.npmjs.com/package/element-internals-polyfill ) can be used. + * + * You can create the object via the function `document.createElement()`. + * + * ``` + * <script type="module"> + * import {CustomControl} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source//monster.mjs'; + * document.createElement('monster-') + * </script> + * ``` + * + * @startuml customcontrol-class.png + * skinparam monochrome true + * skinparam shadowing false + * HTMLElement <|-- CustomElement + * CustomElement <|-- CustomControl + * @enduml + * + * @summary A base class for customcontrols based on CustomElement + * @see {@link https://www.npmjs.com/package/element-internals-polyfill} + * @see {@link https://github.com/WICG/webcomponents} + * @see {@link https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements} + * @since 1.14.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + */ +class CustomControl extends CustomElement { + + /** + * IMPORTANT: CustomControls instances are not created via the constructor, but either via a tag in the HTML or via <code>document.createElement()</code>. + * + * @throws {Error} the ElementInternals is not supported and a polyfill is necessary + * @summary create new Instance + */ + constructor() { + super(); + + if (typeof this['attachInternals'] === 'function') { + /** + * currently only supported by chrome + * @property {Object} + * @private + */ + this[attachedInternalSymbol] = this.attachInternals(); + } + + initObserver.call(this); + + } + + /** + * This method determines which attributes are to be monitored by `attributeChangedCallback()`. + * + * @return {string[]} + * @since 1.15.0 + */ + static get observedAttributes() { + const list = super.observedAttributes; + list.push(ATTRIBUTE_VALUE); + return list; + } + + /** + * + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/attachInternals} + * @since 1.14.0 + * @return {boolean} + */ + static get formAssociated() { + return true; + } + + /** + * Derived classes can override and extend this method as follows. + * + * ``` + * get defaults() { + * return extends{}, super.defaults, { + * myValue:true + * }); + * } + * ``` + * + * @see {@link https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements-face-example} + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/attachInternals} + * @return {object} + * @since 1.14.0 + */ + get defaults() { + return extend({}, super.defaults); + } + + /** + * Must be overridden by a derived class and return the value of the control. + * + * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals) + * + * @since 1.14.0 + * @throws {Error} the value getter must be overwritten by the derived class + */ + get value() { + throw Error('the value getter must be overwritten by the derived class'); + } + + /** + * Must be overridden by a derived class and return the value of the control. + * + * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals) + * + * @param {*} value + * @since 1.14.0 + * @throws {Error} the value setter must be overwritten by the derived class + */ + set value(value) { + throw Error('the value setter must be overwritten by the derived class'); + } + + /** + * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals) + * + * @return {NodeList} + * @since 1.14.0 + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/labels} + * @throws {Error} the ElementInternals is not supported and a polyfill is necessary + */ + get labels() { + return getInternal.call(this)?.labels; + } + + /** + * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals) + * + * @return {string|null} + */ + get name() { + return this.getAttribute('name'); + } + + /** + * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals) + * + * @return {string} + */ + get type() { + return this.constructor.getTag(); + } + + /** + * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals) + * + * @return {ValidityState} + * @throws {Error} the ElementInternals is not supported and a polyfill is necessary + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/ValidityState} + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/validity} + */ + get validity() { + return getInternal.call(this)?.validity; + } + + /** + * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals) + * + * @return {string} + * @since 1.14.0 + * @see https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/validationMessage + * @throws {Error} the ElementInternals is not supported and a polyfill is necessary + */ + get validationMessage() { + return getInternal.call(this)?.validationMessage; + } + + /** + * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals) + * + * @return {boolean} + * @since 1.14.0 + * @see https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/willValidate + * @throws {Error} the ElementInternals is not supported and a polyfill is necessary + */ + get willValidate() { + return getInternal.call(this)?.willValidate; + } + + /** + * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals) + * + * @return {CustomStateSet} + * @since 1.14.0 + * @see https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/states + * @throws {Error} the ElementInternals is not supported and a polyfill is necessary + */ + get states() { + return getInternal.call(this)?.states; + } + + /** + * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals) + * + * @return {HTMLFontElement|null} + * @since 1.14.0 + * @see https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/form + * @throws {Error} the ElementInternals is not supported and a polyfill is necessary + */ + get form() { + return getInternal.call(this)?.form; + } + + /** + * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals) + * + * ``` + * // Use the control's name as the base name for submitted data + * const n = this.getAttribute('name'); + * const entries = new FormData(); + * entries.append(n + '-first-name', this.firstName_); + * entries.append(n + '-last-name', this.lastName_); + * this.setFormValue(entries); + * ``` + * + * @param {File|string|FormData} value + * @param {File|string|FormData} state + * @since 1.14.0 + * @return {undefined} + * @throws {DOMException} NotSupportedError + * @throws {Error} the ElementInternals is not supported and a polyfill is necessary + * @see https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/setFormValue + */ + setFormValue(value, state) { + getInternal.call(this).setFormValue(value, state); + } + + /** + * + * @param {object} flags + * @param {string|undefined} message + * @param {HTMLElement} anchor + * @see https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/setValidity + * @since 1.14.0 + * @return {undefined} + * @throws {DOMException} NotSupportedError + * @throws {Error} the ElementInternals is not supported and a polyfill is necessary + */ + setValidity(flags, message, anchor) { + getInternal.call(this).setValidity(flags, message, anchor); + } + + /** + * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals) + * + * @see https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/checkValidity + * @since 1.14.0 + * @return {boolean} + * @throws {DOMException} NotSupportedError + * @throws {Error} the ElementInternals is not supported and a polyfill is necessary + */ + checkValidity() { + return getInternal.call(this)?.checkValidity(); + } + + /** + * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals) + * + * @return {boolean} + * @since 1.14.0 + * @see https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/reportValidity + * @throws {Error} the ElementInternals is not supported and a polyfill is necessary + * @throws {DOMException} NotSupportedError + */ + reportValidity() { + return getInternal.call(this)?.reportValidity(); + } + +} + +/** + * @private + * @return {object} + * @throws {Error} the ElementInternals is not supported and a polyfill is necessary + * @this CustomControl + */ +function getInternal() { + const self = this; + + if (!(attachedInternalSymbol in this)) { + throw new Error('ElementInternals is not supported and a polyfill is necessary'); + } + + return this[attachedInternalSymbol]; +} + +/** + * @private + * @return {object} + * @this CustomControl + */ +function initObserver() { + const self = this; + + // value + self[attributeObserverSymbol]['value'] = () => { + self.setOption('value', self.getAttribute('value')); + } + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {parseLocale} from "../i18n/locale.mjs"; + +import {getDocument} from "./util.mjs"; + +export {getLocaleOfDocument} + +/** + * @private + * @type {string} + */ +const DEFAULT_LANGUAGE = 'en'; + +/** + * With this function you can read the language version set by the document. + * For this the attribute `lang` in the html tag is read. If no attribute is set, `en` is used as default. + * + * ```html + * <html lang="en"> + * ``` + * + * You can call the function via the monster namespace `new Monster.DOM.getLocaleOfDocument()`. + * + * ``` + * <script type="module"> + * import {getLocaleOfDocument} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/locale.mjs'; + * new getLocaleOfDocument() + * </script> + * ``` + * + * @since 1.13.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @throws {TypeError} value is not a string + * @throws {Error} unsupported locale + * @summary Tries to determine the locale used + */ +function getLocaleOfDocument() { + + const document = getDocument(); + + let html = document.querySelector('html') + if (html instanceof HTMLElement && html.hasAttribute('lang')) { + let locale = html.getAttribute('lang'); + if (locale) { + return new parseLocale(locale) + } + } + + return parseLocale(DEFAULT_LANGUAGE); +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from '../types/base.mjs'; +import {getGlobalObject} from '../types/global.mjs'; +import {validateString} from "../types/validate.mjs"; +import {ATTRIBUTE_THEME_NAME, DEFAULT_THEME} from "./constants.mjs"; + +export {Theme, getDocumentTheme} + +/** + * The Theme class provides the functionality for the theme. + * + * ``` + * <script type="module"> + * import {Theme} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/theme.mjs'; + * console.log(new Theme()) + * </script> + * ``` + * + * @example + * + * import {getDocumentTheme} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/theme.mjs'; + * + * const theme = getDocumentTheme(); + * console.log(theme.getName()); + * // ↦ monster + * + * @since 1.7.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @summary A theme class + */ +class Theme extends Base { + + /** + * + * @param name + * @throws {TypeError} value is not a string + */ + constructor(name) { + super(); + validateString(name); + this.name = name; + } + + /** + * + * @returns {string} + */ + getName() { + return this.name; + } + +} + +/** + * The theming used in the document can be defined via the html-tag. + * The theming is specified via the attribute `data-monster-theme-name`. + * + * As name for a theme all characters are valid, which are also allowed for a HTMLElement-ID. + * + * ``` + * <html data-monster-theme-name="my-theme"> + * ``` + * + * the default theme name is `monster`. + * + * @return {Theme} + * @memberOf Monster.DOM + * @since 1.7.0 + */ +function getDocumentTheme() { + let document = getGlobalObject('document'); + let name = DEFAULT_THEME; + + let element = document.querySelector('html'); + if (element instanceof HTMLElement) { + let theme = element.getAttribute(ATTRIBUTE_THEME_NAME); + if (theme) { + name = theme; + } + } + + return new Theme(name); + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalStateSymbol, internalSymbol,} from "../constants.mjs"; +import {extend} from "../data/extend.mjs"; +import {BaseWithOptions} from "../types/basewithoptions.mjs"; +import {getGlobalObject} from "../types/global.mjs"; +import {ID} from "../types/id.mjs"; +import {isString} from "../types/is.mjs"; +import {Observer} from "../types/observer.mjs"; +import {ProxyObserver} from "../types/proxyobserver.mjs"; +import {ATTRIBUTE_CLASS, ATTRIBUTE_ID, ATTRIBUTE_TITLE} from "./constants.mjs"; + +export {Resource, KEY_DOCUMENT, KEY_QUERY, referenceSymbol} + +/** + * @private + * @type {string} + */ +const KEY_DOCUMENT = 'document'; + +/** + * @private + * @type {string} + */ +const KEY_QUERY = 'query'; + +/** + * @private + * @type {string} + */ +const KEY_TIMEOUT = 'timeout'; + +/** + * @private + * @type {symbol} + */ +const referenceSymbol = Symbol('reference'); + +/** + * This class is the base class for all resources to be loaded. + * + * ``` + * <script type="module"> + * import {Resource} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/resource.mjs'; + * new Resource() + * </script> + * ``` + * + * @since 1.25.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @summary A Resource class + */ +class Resource extends BaseWithOptions { + + /** + * + * @param {Object|undefined} options + */ + constructor(options) { + super(options); + + let uri = this.getOption(this.constructor.getURLAttribute()); + + if (uri === undefined) { + throw new Error('missing source') + } else if (uri instanceof URL) { + uri = uri.toString(); + } else if (!isString(uri)) { + throw new Error('unsupported url type') + } + + this[internalSymbol][this.constructor.getURLAttribute()] = uri; + this[internalStateSymbol] = new ProxyObserver({ + loaded: false, + error: undefined, + }) + + this[referenceSymbol] = undefined; + + } + + /** + * @return {boolean} + */ + isConnected() { + + if (this[referenceSymbol] instanceof HTMLElement) { + return this[referenceSymbol].isConnected; + } + + return false; + } + + /** + * This method is overridden by the special classes and creates the DOM object. + * This method is also called implicitly, if not yet done explicitly, by calling `connect()`. + * + * @throws {Error} this method must be implemented by derived classes + * @return {Monster.DOM.Resource} + */ + create() { + throw new Error("this method must be implemented by derived classes"); + } + + /** + * This method appends the HTMLElement to the specified document. + * If the element has not yet been created, `create()` is called implicitly. + * + * throws {Error} target not found + * @return {Monster.DOM.Resource} + */ + connect() { + + if (!(this[referenceSymbol] instanceof HTMLElement)) { + this.create(); + } + + appendToDocument.call(this); + return this; + } + + /** + * @property {Document} document the document object into which the node is to be appended + * @property {string} src/href url to the corresponding resource + * @property {string} query defines the location where the resource is to be hooked into the dom. + * @property {string} id element attribute id + * @property {string} title element attribute title + * @property {string} class element attribute class + * @property {int} timeout timeout + */ + get defaults() { + return extend({}, super.defaults, { + [this.constructor.getURLAttribute()]: undefined, + [KEY_DOCUMENT]: getGlobalObject('document'), + [KEY_QUERY]: 'head', + [KEY_TIMEOUT]: 10000, + [ATTRIBUTE_ID]: (new ID('resource')).toString(), + [ATTRIBUTE_CLASS]: undefined, + [ATTRIBUTE_TITLE]: undefined + }) + } + + /** + * With `available()` you can check if a resource is available. + * This is the case when the tag is included and the resource is loaded. + * + * @return {Promise} + */ + available() { + const self = this; + if (!(self[referenceSymbol] instanceof HTMLElement)) { + return Promise.reject('no element') + } + + if (!self.isConnected()) { + return Promise.reject('element not connected') + } + + if (self[internalStateSymbol].getSubject()['loaded'] === true) { + + if (self[internalStateSymbol].getSubject()['error'] !== undefined) { + return Promise.reject(self[internalStateSymbol].getSubject()['error']); + } + + return Promise.resolve(); + + } + + return new Promise(function (resolve, reject) { + + const timeout = setTimeout(() => { + reject('timeout'); + }, self.getOption('timeout')) + + const observer = new Observer(() => { + clearTimeout(timeout); + self[internalStateSymbol].detachObserver(observer); + resolve(); + }) + + self[internalStateSymbol].attachObserver(observer); + + }); + + }; + + /** + * @return {string} + */ + static getURLAttribute() { + throw new Error("this method must be implemented by derived classes"); + } + +} + + +/** + * @private + * @return {Promise} + * throws {Error} target not found + */ +function appendToDocument() { + const self = this; + + const targetNode = document.querySelector(self.getOption(KEY_QUERY, 'head')) + if (!(targetNode instanceof HTMLElement)) { + throw new Error('target not found') + } + + addEvents.call(self); + targetNode.appendChild(self[referenceSymbol]); + + return self; +} + +/** + * @private + * @return {addEvents} + */ +function addEvents() { + const self = this; + + const onError = () => { + + self[referenceSymbol].removeEventListener('error', onError); + self[referenceSymbol].removeEventListener('load', onLoad); + + self[internalStateSymbol].setSubject({ + loaded: true, + error: self[referenceSymbol][self.constructor.getURLAttribute()] + ' is not available', + }) + + return; + } + + const onLoad = () => { + self[referenceSymbol].removeEventListener('error', onError); + self[referenceSymbol].removeEventListener('load', onLoad); + self[internalStateSymbol].getSubject()['loaded'] = true; + return; + } + + self[referenceSymbol].addEventListener('load', onLoad, false); + self[referenceSymbol].addEventListener('error', onError, false); + + return self; + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {extend} from "../data/extend.mjs"; +import {BaseWithOptions} from "../types/basewithoptions.mjs"; +import {getGlobalObject} from "../types/global.mjs"; +import {isArray} from "../types/is.mjs"; +import {ATTRIBUTE_HREF, ATTRIBUTE_SRC} from "./constants.mjs"; +import {Resource} from "./resource.mjs"; +import {Data} from "./resource/data.mjs"; +import {Stylesheet} from "./resource/link/stylesheet.mjs"; +import {Script} from "./resource/script.mjs"; + +export {ResourceManager} + +/** + * The ResourceManager is a singleton that manages all resources. + * + * ``` + * <script type="module"> + * import {Resource} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/resourcemanager.mjs'; + * new ResourceManager() + * </script> + * ``` + * + * @since 1.25.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @summary A Resource class + */ + class ResourceManager extends BaseWithOptions { + + /** + * + * @param {Object} options + * throw {Error} unsupported document type + */ + constructor(options) { + super(options); + + if (!(this.getOption('document') instanceof Document)) { + throw new Error('unsupported document type') + } + + + } + + /** + * @property {string} baseurl + */ + getBaseURL() { + this.getOption('document')?.baseURL; + } + + /** + * + * @property {HTMLDocument} document=document Document + * @property {Object} resources + * @property {Array} resources.scripts=[] array with {@link Monster.DOM.Resource.Script} objects + * @property {Array} resources.stylesheets=[] array with {@link Monster.DOM.Resource.Link.Stylesheet} objects + * @property {Array} resources.data=[] array with {@link Monster.DOM.Resource.Data} objects + */ + get defaults() { + return Object.assign({}, super.defaults, { + document: getGlobalObject('document'), + resources: { + scripts: [], + stylesheets: [], + data: [] + } + }) + } + + /** + * Append Tags to DOM + * + * @return {Monster.DOM.ResourceManager} + * @throws {Error} unsupported resource definition + */ + connect() { + runResourceMethod.call(this, 'connect'); + return this; + } + + /** + * Check if available + * + * @return {Promise} + * @throws {Error} unsupported resource definition + */ + available() { + return Promise.all(runResourceMethod.call(this, 'available')); + } + + /** + * Add a script + * + * @param {string|URL} url + * @param [Object|undefined} options + * @return {Monster.DOM.ResourceManager} + * @see Monster.DOM.Resource.Script + */ + addScript(url, options) { + return addResource.call(this, 'scripts', url, options); + } + + + /** + * Add Stylesheet + * + * @param {string|URL} url + * @param [Object|undefined} options + * @return {Monster.DOM.ResourceManager} + * @see Monster.DOM.Resource.Link.Stylesheet + */ + addStylesheet(url, options) { + return addResource.call(this, 'stylesheets', url, options); + } + + /** + * Add Data Tag + * + * @param {string|URL} url + * @param [Object|undefined} options + * @return {Monster.DOM.ResourceManager} + * @see Monster.DOM.Resource.Data + */ + addData(url, options) { + return addResource.call(this, 'data', url, options); + } + + +} + +/** + * @private + * @param {string} method + * @return {Array} + */ +function runResourceMethod(method) { + const self = this; + + const result = []; + + for (const type of ['scripts', 'stylesheets', 'data']) { + const resources = self.getOption('resources.' + type); + if (!isArray(resources)) { + continue; + } + + for (const resource of resources) { + if (!(resource instanceof Resource)) { + throw new Error('unsupported resource definition') + } + + result.push(resource[method]()); + } + + } + + return result; +} + +/** + * + * @param {string} type + * @param {string|URL} url + * @param [Object|undefined} options + * @return {Monster.DOM.ResourceManager} + * @private + */ +function addResource(type, url, options) { + const self = this; + + if (url instanceof URL) { + url = url.toString(); + } + + options = options || {} + + let resource; + switch (type) { + case 'scripts': + resource = new Script(extend({}, options, {[ATTRIBUTE_SRC]: url})) + break; + case 'stylesheets': + resource = new Stylesheet(extend({}, options, {[ATTRIBUTE_HREF]: url})) + break; + case 'data': + resource = new Data(extend({}, options, {[ATTRIBUTE_SRC]: url})) + break; + default: + throw new Error('unsupported type ' + type) + } + + (self.getOption('resources')?.[type]).push(resource); + return self; +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {getGlobal} from "../types/global.mjs"; +import {validateString} from "../types/validate.mjs"; + +export {getDocument, getWindow, getDocumentFragmentFromString} + +/** + * this method fetches the document object + * + * ``` + * <script type="module"> + * import {getDocument} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/util.mjs'; + * console.log(getDocument()) + * </script> + * ``` + * + * in nodejs this functionality can be performed with [jsdom](https://www.npmjs.com/package/jsdom). + * + * ``` + * import {JSDOM} from "jsdom" + * if (typeof window !== "object") { + * const {window} = new JSDOM('', { + * url: 'http://example.com/', + * pretendToBeVisual: true + * }); + * + * [ + * 'self', + * 'document', + * 'Document', + * 'Node', + * 'Element', + * 'HTMLElement', + * 'DocumentFragment', + * 'DOMParser', + * 'XMLSerializer', + * 'NodeFilter', + * 'InputEvent', + * 'CustomEvent' + * ].forEach(key => (getGlobal()[key] = window[key])); + * } + * ``` + * + * @returns {object} + * @since 1.6.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @throws {Error} not supported environment + */ +function getDocument() { + let document = getGlobal()?.['document']; + if (typeof document !== 'object') { + throw new Error("not supported environment") + } + + return document; +} + +/** + * this method fetches the window object + * + * ``` + * <script type="module"> + * import {getWindow} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/util.mjs'; + * console.log(getWindow(null)) + * </script> + * ``` + * + * in nodejs this functionality can be performed with [jsdom](https://www.npmjs.com/package/jsdom). + * + * ``` + * import {JSDOM} from "jsdom" + * if (typeof window !== "object") { + * const {window} = new JSDOM('', { + * url: 'http://example.com/', + * pretendToBeVisual: true + * }); + * + * getGlobal()['window']=window; + * + * [ + * 'self', + * 'document', + * 'Document', + * 'Node', + * 'Element', + * 'HTMLElement', + * 'DocumentFragment', + * 'DOMParser', + * 'XMLSerializer', + * 'NodeFilter', + * 'InputEvent', + * 'CustomEvent' + * ].forEach(key => (getGlobal()[key] = window[key])); + * } + * ``` + * + * @returns {object} + * @since 1.6.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @throws {Error} not supported environment + */ +function getWindow() { + let window = getGlobal()?.['window']; + if (typeof window !== 'object') { + throw new Error("not supported environment") + } + + return window; +} + + +/** + * this method fetches the document object + * + * ``` + * <script type="module"> + * import {getDocumentFragmentFromString} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/util.mjs'; + * console.log(getDocumentFragmentFromString('<div></div>')) + * </script> + * ``` + * + * in nodejs this functionality can be performed with [jsdom](https://www.npmjs.com/package/jsdom). + * + * ``` + * import {JSDOM} from "jsdom" + * if (typeof window !== "object") { + * const {window} = new JSDOM('', { + * url: 'http://example.com/', + * pretendToBeVisual: true + * }); + * + * [ + * 'self', + * 'document', + * 'Document', + * 'Node', + * 'Element', + * 'HTMLElement', + * 'DocumentFragment', + * 'DOMParser', + * 'XMLSerializer', + * 'NodeFilter', + * 'InputEvent', + * 'CustomEvent' + * ].forEach(key => (getGlobal()[key] = window[key])); + * } + * ``` + * + * @returns {DocumentFragment} + * @since 1.6.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @throws {Error} not supported environment + * @throws {TypeError} value is not a string + */ +function getDocumentFragmentFromString(html) { + validateString(html); + + const document = getDocument(); + const template = document.createElement('template'); + template.innerHTML = html; + + return template.content; +} +'use strict'; + + +/** + * @author schukai GmbH + */ + +export { + DEFAULT_THEME, + ATTRIBUTE_PREFIX, + ATTRIBUTE_OPTIONS, + ATTRIBUTE_OPTIONS_SELECTOR, + ATTRIBUTE_THEME_PREFIX, + ATTRIBUTE_THEME_NAME, + ATTRIBUTE_UPDATER_ATTRIBUTES, + ATTRIBUTE_UPDATER_SELECT_THIS, + ATTRIBUTE_UPDATER_REPLACE, + ATTRIBUTE_UPDATER_INSERT, + ATTRIBUTE_UPDATER_INSERT_REFERENCE, + ATTRIBUTE_UPDATER_REMOVE, + ATTRIBUTE_UPDATER_BIND, + ATTRIBUTE_TEMPLATE_PREFIX, + ATTRIBUTE_ROLE, + ATTRIBUTE_DISABLED, + ATTRIBUTE_VALUE, + ATTRIBUTE_OBJECTLINK, + ATTRIBUTE_ERRORMESSAGE, + TAG_SCRIPT, + TAG_STYLE, + TAG_LINK, + ATTRIBUTE_ID, + ATTRIBUTE_CLASS, + ATTRIBUTE_TITLE, + ATTRIBUTE_SRC, + ATTRIBUTE_HREF, + ATTRIBUTE_TYPE, + ATTRIBUTE_NONCE, + ATTRIBUTE_TRANSLATE, + ATTRIBUTE_TABINDEX, + ATTRIBUTE_SPELLCHECK, + ATTRIBUTE_SLOT, + ATTRIBUTE_PART, + ATTRIBUTE_LANG, + ATTRIBUTE_ITEMTYPE, + ATTRIBUTE_ITEMSCOPE, + ATTRIBUTE_ITEMREF, + ATTRIBUTE_ITEMID, + ATTRIBUTE_ITEMPROP, + ATTRIBUTE_IS, + ATTRIBUTE_INPUTMODE, + ATTRIBUTE_ACCESSKEY, + ATTRIBUTE_AUTOCAPITALIZE, + ATTRIBUTE_AUTOFOCUS, + ATTRIBUTE_CONTENTEDITABLE, + ATTRIBUTE_DIR, + ATTRIBUTE_DRAGGABLE, + ATTRIBUTE_ENTERKEYHINT, + ATTRIBUTE_EXPORTPARTS, + ATTRIBUTE_HIDDEN, + objectUpdaterLinkSymbol, + +} + +/** + * default theme + * @memberOf Monster.DOM + * @type {string} + */ +const DEFAULT_THEME = 'monster'; + +/** + * @memberOf Monster.DOM + * @since 1.8.0 + * @type {string} + */ +const ATTRIBUTE_PREFIX = 'data-monster-'; + +/** + * This is the name of the attribute to pass options to a control + * + * @memberOf Monster.DOM + * @since 1.8.0 + * @type {string} + */ +const ATTRIBUTE_OPTIONS = ATTRIBUTE_PREFIX + 'options'; + +/** + * This is the name of the attribute to pass options to a control + * + * @memberOf Monster.DOM + * @since 1.30.0 + * @type {string} + */ +const ATTRIBUTE_OPTIONS_SELECTOR = ATTRIBUTE_PREFIX + 'options-selector'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.8.0 + */ +const ATTRIBUTE_THEME_PREFIX = ATTRIBUTE_PREFIX + 'theme-'; + +/** + * @memberOf Monster.DOM + * @type {string} + */ +const ATTRIBUTE_THEME_NAME = ATTRIBUTE_THEME_PREFIX + 'name'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.8.0 + */ +const ATTRIBUTE_UPDATER_ATTRIBUTES = ATTRIBUTE_PREFIX + 'attributes'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.27.1 + */ +const ATTRIBUTE_UPDATER_SELECT_THIS = ATTRIBUTE_PREFIX + 'select-this'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.8.0 + */ +const ATTRIBUTE_UPDATER_REPLACE = ATTRIBUTE_PREFIX + 'replace'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.8.0 + */ +const ATTRIBUTE_UPDATER_INSERT = ATTRIBUTE_PREFIX + 'insert'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.8.0 + */ +const ATTRIBUTE_UPDATER_INSERT_REFERENCE = ATTRIBUTE_PREFIX + 'insert-reference'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.8.0 + */ +const ATTRIBUTE_UPDATER_REMOVE = ATTRIBUTE_PREFIX + 'remove'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.9.0 + */ +const ATTRIBUTE_UPDATER_BIND = ATTRIBUTE_PREFIX + 'bind'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.27.0 + */ +const ATTRIBUTE_TEMPLATE_PREFIX = ATTRIBUTE_PREFIX + 'template-prefix'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.14.0 + */ +const ATTRIBUTE_ROLE = ATTRIBUTE_PREFIX + 'role'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.24.0 + */ +const ATTRIBUTE_DISABLED = 'disabled'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.24.0 + */ +const ATTRIBUTE_VALUE = 'value'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.9.0 + */ +const ATTRIBUTE_OBJECTLINK = ATTRIBUTE_PREFIX + 'objectlink'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.24.0 + */ +const ATTRIBUTE_ERRORMESSAGE = ATTRIBUTE_PREFIX + 'error'; + +/** + * @memberOf Monster.DOM + * @type {symbol} + * @since 1.24.0 + */ +const objectUpdaterLinkSymbol = Symbol('monsterUpdater'); + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const TAG_SCRIPT = 'script'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const TAG_STYLE = 'style'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const TAG_LINK = 'link'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ + +const ATTRIBUTE_ID = 'id'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ + +const ATTRIBUTE_CLASS = 'class'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_TITLE = 'title'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_SRC = 'src'; +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_HREF = 'href'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_TYPE = 'type'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_NONCE = 'nonce'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_TRANSLATE = 'translate'; + + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_TABINDEX = 'tabindex'; + + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_SPELLCHECK = 'spellcheck'; + + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_SLOT = 'slot'; + + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_PART = 'part'; + + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_LANG = 'lang'; + + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_ITEMTYPE = 'itemtype'; + + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_ITEMSCOPE = 'itemscope'; + + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_ITEMREF = 'itemref'; + + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_ITEMID = 'itemid'; + + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_ITEMPROP = 'itemprop'; + + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_IS = 'is'; + + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_INPUTMODE = 'inputmode'; + + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_ACCESSKEY = 'accesskey'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_AUTOCAPITALIZE = 'autocapitalize'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_AUTOFOCUS = 'autofocus'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_CONTENTEDITABLE = 'contenteditable'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_DIR = 'dir'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_DRAGGABLE = 'draggable'; + + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_ENTERKEYHINT = 'enterkeyhint'; +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_EXPORTPARTS = 'exportparts'; +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_HIDDEN = 'hidden'; +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../constants.mjs"; +import {extend} from "../data/extend.mjs"; +import {Pathfinder} from "../data/pathfinder.mjs"; + +import {parseDataURL} from "../types/dataurl.mjs"; +import {getGlobalObject} from "../types/global.mjs"; +import {isArray, isFunction, isObject, isString} from "../types/is.mjs"; +import {Observer} from "../types/observer.mjs"; +import {ProxyObserver} from "../types/proxyobserver.mjs"; +import {validateFunction, validateInstance, validateObject, validateString} from "../types/validate.mjs"; +import {clone} from "../util/clone.mjs"; +import {addAttributeToken, addToObjectLink, getLinkedObjects, hasObjectLink} from "./attributes.mjs"; +import { + ATTRIBUTE_DISABLED, + ATTRIBUTE_ERRORMESSAGE, + ATTRIBUTE_OPTIONS, + ATTRIBUTE_OPTIONS_SELECTOR, + objectUpdaterLinkSymbol +} from "./constants.mjs"; +import {findDocumentTemplate, Template} from "./template.mjs"; +import {Updater} from "./updater.mjs"; + +export {CustomElement, initMethodSymbol, assembleMethodSymbol, attributeObserverSymbol, registerCustomElement, assignUpdaterToElement} + +/** + * @memberOf Monster.DOM + * @type {symbol} + */ +const initMethodSymbol = Symbol('initMethodSymbol'); + +/** + * @memberOf Monster.DOM + * @type {symbol} + */ +const assembleMethodSymbol = Symbol('assembleMethodSymbol'); + +/** + * this symbol holds the attribute observer callbacks. The key is the attribute name. + * @memberOf Monster.DOM + * @type {symbol} + */ +const attributeObserverSymbol = Symbol('attributeObserver'); + + +/** + * HTMLElement + * @external HTMLElement + * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement + * + * @startuml customelement-sequencediagram.png + * skinparam monochrome true + * skinparam shadowing false + * + * autonumber + * + * Script -> DOM: element = document.createElement('my-element') + * DOM -> CustomElement: constructor() + * CustomElement -> CustomElement: [initMethodSymbol]() + * + * CustomElement --> DOM: Element + * DOM --> Script : element + * + * + * Script -> DOM: document.querySelector('body').append(element) + * + * DOM -> CustomElement : connectedCallback() + * + * note right CustomElement: is only called at\nthe first connection + * CustomElement -> CustomElement : [assembleMethodSymbol]() + * + * ... ... + * + * autonumber + * + * Script -> DOM: document.querySelector('monster-confirm-button').parentNode.removeChild(element) + * DOM -> CustomElement: disconnectedCallback() + * + * + * @enduml + * + * @startuml customelement-class.png + * skinparam monochrome true + * skinparam shadowing false + * HTMLElement <|-- CustomElement + * @enduml + */ + + +/** + * To define a new HTML element we need the power of CustomElement + * + * IMPORTANT: after defining a `CustomElement`, the `registerCustomElement` method must be called + * with the new class name. only then will the tag defined via the `getTag` method be made known to the DOM. + * + * <img src="./images/customelement-class.png"> + * + * You can create the object via the function `document.createElement()`. + * + * ``` + * <script type="module"> + * import {Monster} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source//monster.mjs'; + * document.createElement('monster-') + * </script> + * ``` + * + * ## Interaction + * + * <img src="./images/customelement-sequencediagram.png"> + * + * ## Styling + * + * For optimal display of custom-elements the pseudo-class :defined can be used. + * + * To prevent the custom elements from being displayed and flickering until the control is registered, it is recommended to create a css directive. + * + * In the simplest case, you can simply hide the control. + * + * ``` + * <style> + * + * my-custom-element:not(:defined) { + * display: none; + * } + * + * my-custom-element:defined { + * display: flex; + * } + * + * </style> + * ``` + * + * Alternatively you can also display a loader + * + * ``` + * my-custom-element:not(:defined) { + * display: flex; + * box-shadow: 0 4px 10px 0 rgba(33, 33, 33, 0.15); + * border-radius: 4px; + * height: 200px; + * position: relative; + * overflow: hidden; + * } + * + * my-custom-element:not(:defined)::before { + * content: ''; + * display: block; + * position: absolute; + * left: -150px; + * top: 0; + * height: 100%; + * width: 150px; + * background: linear-gradient(to right, transparent 0%, #E8E8E8 50%, transparent 100%); + * animation: load 1s cubic-bezier(0.4, 0.0, 0.2, 1) infinite; + * } + * + * @keyframes load { + * from { + * left: -150px; + * } + * to { + * left: 100%; + * } + * } + * + * my-custom-element:defined { + * display: flex; + * } + * ``` + * + * @example + * + * // In the example the the user can use his own template by creating a template in the DOM with the ID `my-custom-element`. + * // You can also specify a theme (for example `mytheme`), then it will search for the ID `my-custom-element-mytheme` and + * // if not available for the ID `my-custom-element`. + * + * class MyCustomElement extends CustomElement { + * + * static getTag() { + * return "my-custom-element" + * } + * + * } + * + * // ↦ <my-custom-element></my-custom-element> + * + * @see https://github.com/WICG/webcomponents + * @see https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements + * @since 1.7.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @extends external:HTMLElement + * @summary A base class for HTML5 customcontrols + */ +class CustomElement extends HTMLElement { + + /** + * A new object is created. First the `initOptions` method is called. Here the + * options can be defined in derived classes. Subsequently, the shadowRoot is initialized. + * + * @throws {Error} the options attribute does not contain a valid json definition. + * @since 1.7.0 + */ + constructor() { + super(); + this[internalSymbol] = new ProxyObserver({'options': extend({}, this.defaults)}); + this[attributeObserverSymbol] = {}; + initOptionObserver.call(this); + this[initMethodSymbol](); + } + + /** + * This method determines which attributes are to be monitored by `attributeChangedCallback()`. + * + * @return {string[]} + * @since 1.15.0 + */ + static get observedAttributes() { + return [ATTRIBUTE_OPTIONS, ATTRIBUTE_DISABLED]; + } + + /** + * Derived classes can override and extend this method as follows. + * + * ``` + * get defaults() { + * return Object.assign({}, super.defaults, { + * myValue:true + * }); + * } + * ``` + * + * To set the options via the html tag the attribute data-monster-options must be set. + * As value a JSON object with the desired values must be defined. + * + * Since 1.18.0 the JSON can be specified as a DataURI. + * + * ``` + * new Monster.Types.DataUrl(btoa(JSON.stringify({ + * shadowMode: 'open', + * delegatesFocus: true, + * templates: { + * main: undefined + * } + * })),'application/json',true).toString() + * ``` + * + * The attribute data-monster-options-selector can be used to access a script tag that contains additional configuration. + * + * As value a selector must be specified, which belongs to a script tag and contains the configuration as json. + * + * ``` + * <script id="id-for-this-config" type="application/json"> + * { + * "config-key": "config-value" + * } + * </script> + * ``` + * + * The individual configuration values can be found in the table. + * + * @property {boolean} disabled=false Object The Boolean disabled attribute, when present, makes the element not mutable, focusable, or even submitted with the form. + * @property {string} shadowMode=open `open` Elements of the shadow root are accessible from JavaScript outside the root, for example using. `close` Denies access to the node(s) of a closed shadow root from JavaScript outside it + * @property {Boolean} delegatesFocus=true A boolean that, when set to true, specifies behavior that mitigates custom element issues around focusability. When a non-focusable part of the shadow DOM is clicked, the first focusable part is given focus, and the shadow host is given any available :focus styling. + * @property {Object} templates Templates + * @property {string} templates.main=undefined Main template + * + * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/attachShadow + * @since 1.8.0 + */ + get defaults() { + return { + ATTRIBUTE_DISABLED: this.getAttribute(ATTRIBUTE_DISABLED), + shadowMode: 'open', + delegatesFocus: true, + templates: { + main: undefined + } + }; + } + + /** + * There is no check on the name by this class. the developer is responsible for assigning an appropriate tag. + * if the name is not valid, registerCustomElement() will issue an error + * + * @link https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name + * @return {string} + * @throws {Error} the method getTag must be overwritten by the derived class. + * @since 1.7.0 + */ + static getTag() { + throw new Error("the method getTag must be overwritten by the derived class."); + } + + /** + * At this point a `CSSStyleSheet` object can be returned. If the environment does not + * support a constructor, then an object can also be built using the following detour. + * + * If `undefined` is returned then the shadowRoot does not get a stylesheet. + * + * ``` + * const doc = document.implementation.createHTMLDocument('title'); + * + * let style = doc.createElement("style"); + * style.innerHTML="p{color:red;}"; + * + * // WebKit Hack + * style.appendChild(document.createTextNode("")); + * // Add the <style> element to the page + * doc.head.appendChild(style); + * return doc.styleSheets[0]; + * ; + * ``` + * + * @return {CSSStyleSheet|CSSStyleSheet[]|string|undefined} + */ + static getCSSStyleSheet() { + return undefined; + } + + /** + * attach a new observer + * + * @param {Observer} observer + * @returns {CustomElement} + */ + attachObserver(observer) { + this[internalSymbol].attachObserver(observer) + return this; + } + + /** + * detach a observer + * + * @param {Observer} observer + * @returns {CustomElement} + */ + detachObserver(observer) { + this[internalSymbol].detachObserver(observer) + return this; + } + + /** + * @param {Observer} observer + * @returns {ProxyObserver} + */ + containsObserver(observer) { + return this[internalSymbol].containsObserver(observer) + } + + /** + * nested options can be specified by path `a.b.c` + * + * @param {string} path + * @param {*} defaultValue + * @return {*} + * @since 1.10.0 + */ + getOption(path, defaultValue) { + let value; + + try { + value = new Pathfinder(this[internalSymbol].getRealSubject()['options']).getVia(path); + } catch (e) { + + } + + if (value === undefined) return defaultValue; + return value; + } + + /** + * Set option and inform elements + * + * @param {string} path + * @param {*} value + * @return {CustomElement} + * @since 1.14.0 + */ + setOption(path, value) { + new Pathfinder(this[internalSymbol].getSubject()['options']).setVia(path, value); + return this; + } + + /** + * @since 1.15.0 + * @param {string|object} options + * @return {CustomElement} + */ + setOptions(options) { + + if (isString(options)) { + options = parseOptionsJSON.call(this, options) + } + + const self = this; + extend(self[internalSymbol].getSubject()['options'], self.defaults, options); + + return self; + } + + /** + * Is called once via the constructor + * + * @return {CustomElement} + * @since 1.8.0 + */ + [initMethodSymbol]() { + return this; + } + + /** + * Is called once when the object is included in the DOM for the first time. + * + * @return {CustomElement} + * @since 1.8.0 + */ + [assembleMethodSymbol]() { + + const self = this; + let elements, nodeList; + + const AttributeOptions = getOptionsFromAttributes.call(self); + if (isObject(AttributeOptions) && Object.keys(AttributeOptions).length > 0) { + self.setOptions(AttributeOptions); + } + + const ScriptOptions = getOptionsFromScriptTag.call(self); + if (isObject(ScriptOptions) && Object.keys(ScriptOptions).length > 0) { + self.setOptions(ScriptOptions); + } + + + if (self.getOption('shadowMode', false) !== false) { + try { + initShadowRoot.call(self); + elements = self.shadowRoot.childNodes; + + } catch (e) { + + } + + try { + initCSSStylesheet.call(this); + } catch (e) { + addAttributeToken(self, ATTRIBUTE_ERRORMESSAGE, e.toString()); + } + } + + if (!(elements instanceof NodeList)) { + if (!(elements instanceof NodeList)) { + initHtmlContent.call(this); + elements = this.childNodes; + } + } + + try { + nodeList = new Set([ + ...elements, + ...getSlottedElements.call(self) + ]) + } catch (e) { + nodeList = elements + } + + assignUpdaterToElement.call(self, nodeList, clone(self[internalSymbol].getRealSubject()['options'])); + return self; + } + + /** + * Called every time the element is inserted into the DOM. Useful for running setup code, such as + * fetching resources or rendering. Generally, you should try to delay work until this time. + * + * @return {void} + * @since 1.7.0 + */ + connectedCallback() { + let self = this; + if (!hasObjectLink(self, objectUpdaterLinkSymbol)) { + self[assembleMethodSymbol]() + } + } + + /** + * Called every time the element is removed from the DOM. Useful for running clean up code. + * + * @return {void} + * @since 1.7.0 + */ + disconnectedCallback() { + + } + + /** + * The custom element has been moved into a new document (e.g. someone called document.adoptNode(el)). + * + * @return {void} + * @since 1.7.0 + */ + adoptedCallback() { + + } + + /** + * Called when an observed attribute has been added, removed, updated, or replaced. Also called for initial + * values when an element is created by the parser, or upgraded. Note: only attributes listed in the observedAttributes + * property will receive this callback. + * + * @param {string} attrName + * @param {string} oldVal + * @param {string} newVal + * @return {void} + * @since 1.15.0 + */ + attributeChangedCallback(attrName, oldVal, newVal) { + const self = this; + + const callback = self[attributeObserverSymbol]?.[attrName]; + + if (isFunction(callback)) { + callback.call(self, newVal, oldVal); + } + + } + + /** + * + * @param {Node} node + * @return {boolean} + * @throws {TypeError} value is not an instance of + * @since 1.19.0 + */ + hasNode(node) { + const self = this; + + if (containChildNode.call(self, validateInstance(node, Node))) { + return true; + } + + if (!(self.shadowRoot instanceof ShadowRoot)) { + return false; + } + + return containChildNode.call(self.shadowRoot, node); + + } + +} + +/** + * @private + * @param {String|undefined} query + * @param {String|undefined|null} name name of the slot (if the parameter is undefined, all slots are searched, if the parameter has the value null, all slots without a name are searched. if a string is specified, the slots with this name are searched.) + * @return {*} + * @this CustomElement + * @since 1.23.0 + * @throws {Error} query must be a string + */ +function getSlottedElements(query, name) { + const self = this; + const result = new Set; + + if (!(self.shadowRoot instanceof ShadowRoot)) { + return result; + } + + let selector = 'slot'; + if (name !== undefined) { + if (name === null) { + selector += ':not([name])'; + } else { + selector += '[name=' + validateString(name) + ']'; + } + + } + + const slots = self.shadowRoot.querySelectorAll(selector); + + for (const [, slot] of Object.entries(slots)) { + slot.assignedElements().forEach(function (node) { + + if (!(node instanceof HTMLElement)) return; + + if (isString(query)) { + node.querySelectorAll(query).forEach(function (n) { + result.add(n); + }); + + if (node.matches(query)) { + result.add(node); + } + + } else if (query !== undefined) { + throw new Error('query must be a string') + } else { + result.add(node); + } + }) + } + + return result; +} + +/** + * @this CustomElement + * @private + * @param {Node} node + * @return {boolean} + */ +function containChildNode(node) { + const self = this; + + if (self.contains(node)) { + return true; + } + + for (const [, e] of Object.entries(self.childNodes)) { + if (e.contains(node)) { + return true; + } + + containChildNode.call(e, node); + } + + + return false; +} + +/** + * @since 1.15.0 + * @private + * @this CustomElement + */ +function initOptionObserver() { + const self = this; + + let lastDisabledValue = undefined; + self.attachObserver(new Observer(function () { + const flag = self.getOption('disabled'); + + if (flag === lastDisabledValue) { + return; + } + + lastDisabledValue = flag; + + if (!(self.shadowRoot instanceof ShadowRoot)) { + return; + } + + const query = 'button, command, fieldset, keygen, optgroup, option, select, textarea, input, [data-monster-objectlink]'; + const elements = self.shadowRoot.querySelectorAll(query); + + let nodeList; + try { + nodeList = new Set([ + ...elements, + ...getSlottedElements.call(self, query) + ]) + } catch (e) { + nodeList = elements + } + + for (const element of [...nodeList]) { + if (flag === true) { + element.setAttribute(ATTRIBUTE_DISABLED, ''); + } else { + element.removeAttribute(ATTRIBUTE_DISABLED); + } + } + + })); + + self.attachObserver(new Observer(function () { + + // not initialised + if (!hasObjectLink(self, objectUpdaterLinkSymbol)) { + return; + } + // inform every element + const updaters = getLinkedObjects(self, objectUpdaterLinkSymbol); + + for (const list of updaters) { + for (const updater of list) { + let d = clone(self[internalSymbol].getRealSubject()['options']); + Object.assign(updater.getSubject(), d); + } + } + + })); + + // disabled + self[attributeObserverSymbol][ATTRIBUTE_DISABLED] = () => { + if (self.hasAttribute(ATTRIBUTE_DISABLED)) { + self.setOption(ATTRIBUTE_DISABLED, true); + } else { + self.setOption(ATTRIBUTE_DISABLED, undefined); + } + } + + // data-monster-options + self[attributeObserverSymbol][ATTRIBUTE_OPTIONS] = () => { + const options = getOptionsFromAttributes.call(self); + if (isObject(options) && Object.keys(options).length > 0) { + self.setOptions(options); + } + } + + // data-monster-options-selector + self[attributeObserverSymbol][ATTRIBUTE_OPTIONS_SELECTOR] = () => { + const options = getOptionsFromScriptTag.call(self); + if (isObject(options) && Object.keys(options).length > 0) { + self.setOptions(options); + } + } + + +} + +/** + * @private + * @return {object} + * @throws {TypeError} value is not a object + */ +function getOptionsFromScriptTag() { + const self = this; + + if (!self.hasAttribute(ATTRIBUTE_OPTIONS_SELECTOR)) { + return {}; + } + + const node = document.querySelector(self.getAttribute(ATTRIBUTE_OPTIONS_SELECTOR)); + if (!(node instanceof HTMLScriptElement)) { + addAttributeToken(self, ATTRIBUTE_ERRORMESSAGE, 'the selector ' + ATTRIBUTE_OPTIONS_SELECTOR + ' for options was specified (' + self.getAttribute(ATTRIBUTE_OPTIONS_SELECTOR) + ') but not found.'); + return {}; + } + + let obj = {}; + + try { + obj = parseOptionsJSON.call(this, node.textContent.trim()) + } catch (e) { + addAttributeToken(self, ATTRIBUTE_ERRORMESSAGE, 'when analyzing the configuration from the script tag there was an error. ' + e); + } + + return obj; + +} + +/** + * @private + * @return {object} + */ +function getOptionsFromAttributes() { + const self = this; + + if (this.hasAttribute(ATTRIBUTE_OPTIONS)) { + try { + return parseOptionsJSON.call(self, this.getAttribute(ATTRIBUTE_OPTIONS)) + } catch (e) { + addAttributeToken(self, ATTRIBUTE_ERRORMESSAGE, 'the options attribute ' + ATTRIBUTE_OPTIONS + ' does not contain a valid json definition (actual: ' + this.getAttribute(ATTRIBUTE_OPTIONS) + ').' + e); + } + } + + return {}; +} + +/** + * @private + * @param data + * @return {Object} + */ +function parseOptionsJSON(data) { + + const self = this, obj = {}; + + if (!isString(data)) { + return obj; + } + + // the configuration can be specified as a data url. + try { + let dataUrl = parseDataURL(data); + data = dataUrl.content; + } catch (e) { + + } + + try { + let obj = JSON.parse(data); + return validateObject(obj); + } catch (e) { + throw e; + } + + + return obj; +} + +/** + * @private + * @return {initHtmlContent} + */ +function initHtmlContent() { + + try { + let template = findDocumentTemplate(this.constructor.getTag()); + this.appendChild(template.createDocumentFragment()); + } catch (e) { + + let html = this.getOption('templates.main', ''); + if (isString(html) && html.length > 0) { + this.innerHTML = html; + } + + } + + return this; + +} + +/** + * @private + * @return {CustomElement} + * @memberOf Monster.DOM + * @this CustomElement + * @since 1.16.0 + * @throws {TypeError} value is not an instance of + */ +function initCSSStylesheet() { + const self = this; + + if (!(this.shadowRoot instanceof ShadowRoot)) { + return self; + } + + const styleSheet = this.constructor.getCSSStyleSheet(); + + if (styleSheet instanceof CSSStyleSheet) { + if (styleSheet.cssRules.length > 0) { + this.shadowRoot.adoptedStyleSheets = [styleSheet]; + } + } else if (isArray(styleSheet)) { + const assign = []; + for (let s of styleSheet) { + + if (isString(s)) { + let trimedStyleSheet = s.trim() + if (trimedStyleSheet !== '') { + const style = document.createElement('style') + style.innerHTML = trimedStyleSheet; + self.shadowRoot.prepend(style); + } + continue; + } + + validateInstance(s, CSSStyleSheet); + + if (s.cssRules.length > 0) { + assign.push(s); + } + + } + + if (assign.length > 0) { + this.shadowRoot.adoptedStyleSheets = assign; + } + + } else if (isString(styleSheet)) { + + let trimedStyleSheet = styleSheet.trim() + if (trimedStyleSheet !== '') { + const style = document.createElement('style') + style.innerHTML = styleSheet; + self.shadowRoot.prepend(style); + } + + } + + return self; + +} + +/** + * @private + * @return {CustomElement} + * @throws {Error} html is not set. + * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/attachShadow + * @memberOf Monster.DOM + * @since 1.8.0 + */ +function initShadowRoot() { + + let template, html; + + try { + template = findDocumentTemplate(this.constructor.getTag()); + } catch (e) { + + html = this.getOption('templates.main', ''); + if (!isString(html) || html === undefined || html === "") { + throw new Error("html is not set."); + } + + } + + this.attachShadow({ + mode: this.getOption('shadowMode', 'open'), + delegatesFocus: this.getOption('delegatesFocus', true) + }); + + if (template instanceof Template) { + this.shadowRoot.appendChild(template.createDocumentFragment()); + return this; + } + + this.shadowRoot.innerHTML = html; + return this; +} + +/** + * This method registers a new element. The string returned by `CustomElement.getTag()` is used as the tag. + * + * @param {CustomElement} element + * @return {void} + * @since 1.7.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @throws {DOMException} Failed to execute 'define' on 'CustomElementRegistry': is not a valid custom element name + */ +function registerCustomElement(element) { + validateFunction(element); + getGlobalObject('customElements').define(element.getTag(), element); +} + + +/** + * + * @param element + * @param object + * @return {Promise[]} + * @since 1.23.0 + * @memberOf Monster.DOM + */ +function assignUpdaterToElement(elements, object) { + + const updaters = new Set; + + if (elements instanceof NodeList) { + elements = new Set([ + ...elements + ]) + } + + let result = []; + + elements.forEach((element) => { + if (!(element instanceof HTMLElement)) return; + if ((element instanceof HTMLTemplateElement)) return; + + const u = new Updater(element, object) + updaters.add(u); + + result.push(u.run().then(() => { + return u.enableEventProcessing(); + })); + + }); + + if (updaters.size > 0) { + addToObjectLink(this, objectUpdaterLinkSymbol, updaters); + } + + return result; +} +'use strict'; + +/** + * In this namespace you will find classes and methods for handling the DOM. + * + * @namespace Monster.DOM + * @memberOf Monster + * @author schukai GmbH + */ +const ns = {};'use strict'; + +/** + * @author schukai GmbH + */ + + +import {getGlobalFunction} from "../types/global.mjs"; +import {TokenList} from "../types/tokenlist.mjs"; +import {validateInstance, validateString, validateSymbol} from "../types/validate.mjs"; +import {ATTRIBUTE_OBJECTLINK} from "./constants.mjs"; + +export { + findClosestObjectLink, + addToObjectLink, + removeObjectLink, + hasObjectLink, + getLinkedObjects, + toggleAttributeToken, + addAttributeToken, + removeAttributeToken, + containsAttributeToken, + replaceAttributeToken, + clearAttributeTokens, + findClosestByAttribute, + findClosestByClass +} + +/** + * Get the closest object link of a node + * + * if a node is specified without a object link, a recursive search upwards is performed until the corresponding + * object link is found, or undefined is returned. + * + * ``` + * <script type="module"> + * import {getUpdaterFromNode} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/updater.mjs'; + * console.log(findClosestObjectLink()) + * </script> + * ``` + * + * @param {HTMLElement} element + * @return {HTMLElement|undefined} + * @since 1.10.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @throws {TypeError} value is not an instance of HTMLElement + */ +function findClosestObjectLink(element) { + return findClosestByAttribute(element, ATTRIBUTE_OBJECTLINK); +} + +/** + * Adds a class attribute to an element. + * + * ``` + * <script type="module"> + * import {addToObjectLink} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/attributes.mjs'; + * addToObjectLink(); + * </script> + * ``` + * + * @since 1.9.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @param {HTMLElement} element + * @param {Symbol} symbol + * @param {Object} object + * @return {boolean} + */ +function addToObjectLink(element, symbol, object) { + + validateInstance(element, HTMLElement); + validateSymbol(symbol) + + if (element?.[symbol] === undefined) { + element[symbol] = new Set; + } + + addAttributeToken(element, ATTRIBUTE_OBJECTLINK, symbol.toString()); + element[symbol].add(object); + return element; + +} + +/** + * Removes an object from an element + * + * ``` + * <script type="module"> + * import {removeObjectLink} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/attributes.mjs'; + * removeObjectLink(); + * </script> + * ``` + * + * @since 1.9.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @param {HTMLElement} element + * @param {Symbol} symbol + * @return {boolean} + */ +function removeObjectLink(element, symbol) { + + validateInstance(element, HTMLElement); + validateSymbol(symbol) + + if (element?.[symbol] === undefined) { + return element + } + + removeAttributeToken(element, ATTRIBUTE_OBJECTLINK, symbol.toString()); + delete element[symbol]; + return element; + +} + + +/** + * Checks if an element has an object link + * + * ``` + * <script type="module"> + * import {hasObjectLink} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/attributes.mjs'; + * hasObjectLink(); + * </script> + * ``` + * + * @since 1.9.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @param {HTMLElement} element + * @param {Symbol} symbol + * @return {boolean} + */ +function hasObjectLink(element, symbol) { + + validateInstance(element, HTMLElement); + validateSymbol(symbol) + + if (element?.[symbol] === undefined) { + return false + } + + return containsAttributeToken(element, ATTRIBUTE_OBJECTLINK, symbol.toString()); + +} + +/** + * The ObjectLink can be used to attach objects to HTMLElements. The elements are kept in a set under a unique + * symbol and can be read via an iterator {@see {@link getLinkedObjects}}. + * + * In addition, elements with an objectLink receive the attribute `data-monster-objectlink`. + * + * With the method {@see {@link addToObjectLink}} the objects can be added. + * + * ``` + * <script type="module"> + * import {getLinkedObjects} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/attributes.mjs'; + * getLinkedObjects(); + * </script> + * ``` + * + * @since 1.9.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @param {HTMLElement} element + * @param {Symbol} symbol + * @return {Iterator} + * @throws {Error} there is no object link for symbol + */ +function getLinkedObjects(element, symbol) { + + validateInstance(element, HTMLElement); + validateSymbol(symbol) + + if (element?.[symbol] === undefined) { + throw new Error('there is no object link for ' + symbol.toString()); + } + + return element?.[symbol][Symbol.iterator](); + +} + + +/** + * With this method tokens in an attribute can be switched on or off. For example, classes can be switched on and off in the elements class attribute. + * + * Tokens are always separated by a space. + * + * ``` + * <script type="module"> + * import {toggleAttributeToken} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/attributes.mjs'; + * toggleAttributeToken(); + * </script> + * ``` + * + * @since 1.9.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @param {HTMLElement} element + * @param {string} key + * @param {string} token + * @return {HTMLElement} + */ +function toggleAttributeToken(element, key, token) { + validateInstance(element, HTMLElement); + validateString(token) + validateString(key) + + if (!element.hasAttribute(key)) { + element.setAttribute(key, token); + return element; + } + + element.setAttribute(key, new TokenList(element.getAttribute(key)).toggle(token).toString()); + + return element +} + +/** + * This method can be used to add a token to an attribute. Tokens are always separated by a space. + * + * ``` + * <script type="module"> + * import {addAttributeToken} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/attributes.mjs'; + * addAttributeToken(); + * </script> + * ``` + * + * @since 1.9.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @param {HTMLElement} element + * @param {string} key + * @param {string} token + * @return {HTMLElement} + */ +function addAttributeToken(element, key, token) { + validateInstance(element, HTMLElement); + validateString(token) + validateString(key) + + if (!element.hasAttribute(key)) { + element.setAttribute(key, token); + return element; + } + + element.setAttribute(key, new TokenList(element.getAttribute(key)).add(token).toString()); + + return element +} + +/** + * This function can be used to remove tokens from an attribute. + * + * Tokens are always separated by a space. + * + * ``` + * <script type="module"> + * import {removeAttributeToken} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/attributes.mjs'; + * removeAttributeToken(); + * </script> + * ``` + * + * @since 1.9.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @param {HTMLElement} element + * @param {string} key + * @param {string} token + * @return {HTMLElement} + */ +function removeAttributeToken(element, key, token) { + validateInstance(element, HTMLElement); + validateString(token) + validateString(key) + + if (!element.hasAttribute(key)) { + return element; + } + + element.setAttribute(key, new TokenList(element.getAttribute(key)).remove(token).toString()); + + return element +} + +/** + * This method can be used to determine whether an attribute has a token. + * + * Tokens are always separated by a space. + * + * ``` + * <script type="module"> + * import {containsAttributeToken} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/attributes.mjs'; + * containsAttributeToken(); + * </script> + * ``` + * + * @since 1.9.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @param {HTMLElement} element + * @param {string} key + * @param {string} token + * @return {boolean} + */ +function containsAttributeToken(element, key, token) { + validateInstance(element, HTMLElement); + validateString(token) + validateString(key) + + if (!element.hasAttribute(key)) { + return false; + } + + return new TokenList(element.getAttribute(key)).contains(token); + +} + +/** + * Tokens are always separated by a space. + * + * ``` + * <script type="module"> + * import {replaceAttributeToken} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/attributes.mjs'; + * replaceAttributeToken(); + * </script> + * ``` + * + * @since 1.9.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @param {HTMLElement} element + * @param {string} key + * @param {string} from + * @param {string} to + * @return {HTMLElement} + */ +function replaceAttributeToken(element, key, from, to) { + validateInstance(element, HTMLElement); + validateString(from) + validateString(to) + validateString(key) + + if (!element.hasAttribute(key)) { + return element; + } + + element.setAttribute(key, new TokenList(element.getAttribute(key)).replace(from, to).toString()); + + return element +} + +/** + * Tokens are always separated by a space. + * + * ``` + * <script type="module"> + * import {clearAttributeTokens} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/attributes.mjs'; + * clearAttributeTokens(); + * </script> + * ``` + * + * @since 1.9.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @param {HTMLElement} element + * @param {string} key + * @return {HTMLElement} + */ +function clearAttributeTokens(element, key) { + validateInstance(element, HTMLElement); + validateString(key) + + if (!element.hasAttribute(key)) { + return element; + } + + element.setAttribute(key, ""); + + return element +} + +/** + * This function searches, starting from an `HTMLElemement`, for the next element that has a certain attribute. + * + * ```html + * <div data-my-attribute="2" id="2"> + * <div id="1"></div> + * </div> + * ``` + * + * ```javascript + * // if no value is specified (undefined), then only the attribute is checked. + * findClosestByAttribute(document.getElementById('1'),'data-my-attribute'); // ↦ node with id 2 + * findClosestByAttribute(document.getElementById('2'),'data-my-attribute'); // ↦ node with id 2 + * + * // if a value is specified, for example an empty string, then the name and the value are checked. + * findClosestByAttribute(document.getElementById('1'),'data-my-attribute', ''); // ↦ undefined + * findClosestByAttribute(document.getElementById('1'),'data-my-attribute', '2'); // ↦ node with id 2 + * ``` + * + * ``` + * <script type="module"> + * import {findClosestByAttribute} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/attributes.mjs'; + * findClosestByAttribute(); + * </script> + * ``` + * + * @since 1.14.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @param {HTMLElement} element + * @param {string} key + * @param {string|undefined} value + * @return {HTMLElement|undefined} + * @summary find closest node + */ +function findClosestByAttribute(element, key, value) { + validateInstance(element, getGlobalFunction('HTMLElement')); + + if (element.hasAttribute(key)) { + if (value === undefined) { + return element; + } + + if (element.getAttribute(key) === value) { + return element; + } + + } + + let selector = validateString(key); + if (value !== undefined) selector += "=" + validateString(value); + let result = element.closest('[' + selector + ']'); + if (result instanceof HTMLElement) { + return result; + } + return undefined; +} + +/** + * This function searches, starting from an `HTMLElemement`, for the next element that has a certain attribute. + * + * ```html + * <div class="myclass" id="2"> + * <div id="1"></div> + * </div> + * ``` + * + * ```javascript + * // if no value is specified (undefined), then only the attribute is checked. + * findClosestByClass(document.getElementById('1'),'myclass'); // ↦ node with id 2 + * findClosestByClass(document.getElementById('2'),'myclass'); // ↦ node with id 2 + * ``` + * + * ``` + * <script type="module"> + * import {findClosestByClass} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/attributes.mjs'; + * findClosestByClass(); + * </script> + * ``` + * + * @since 1.27.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @param {HTMLElement} element + * @param {string} className + * @return {HTMLElement|undefined} + * @summary find closest node + */ +function findClosestByClass(element, className) { + validateInstance(element, getGlobalFunction('HTMLElement')); + + if (element?.classList?.contains(validateString(className))) { + return element; + } + + let result = element.closest('.' + className); + if (result instanceof HTMLElement) { + return result; + } + + return undefined; +} +'use strict'; + + +/** + * @author schukai GmbH + */ + +import {isArray, isObject} from "../types/is.mjs"; +import {validateInstance, validateString} from "../types/validate.mjs"; +import {getDocument} from "./util.mjs"; + +export {fireEvent, fireCustomEvent, findTargetElementFromEvent} + +/** + * The function sends an event + * + * ``` + * <script type="module"> + * import {fireEvent} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/events.mjs'; + * fireEvent() + * </script> + * ``` + * + * @param {HTMLElement|HTMLCollection|NodeList} element + * @param {string} type + * @return {void} + * @since 1.10.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @throws {TypeError} value is not an instance of HTMLElement or HTMLCollection + * @summary Construct and send and event + */ +function fireEvent(element, type) { + + const document = getDocument(); + + if (element instanceof HTMLElement) { + + if (type === 'click') { + element.click(); + return; + } + + let event = new Event(validateString(type), { + bubbles: true, + cancelable: true, + }); + + element.dispatchEvent(event); + + } else if (element instanceof HTMLCollection || element instanceof NodeList) { + for (let e of element) { + fireEvent(e, type); + } + } else { + throw new TypeError('value is not an instance of HTMLElement or HTMLCollection') + } + +} + +/** + * You can call the function via the monster namespace `new Monster.DOM.fireCustomEvent()`. + * + * ``` + * <script type="module"> + * import {fireCustomEvent} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/events.mjs'; + * fireCustomEvent() + * </script> + * ``` + * + * @param {HTMLElement|HTMLCollection|NodeList} element + * @param {string} type + * @return {void} + * @since 1.29.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @throws {TypeError} value is not an instance of HTMLElement or HTMLCollection + * @summary Construct and send and event + */ +function fireCustomEvent(element, type, detail) { + + const document = getDocument(); + + if (element instanceof HTMLElement) { + + if (!isObject(detail)) { + detail = {detail}; + } + + let event = new CustomEvent(validateString(type), { + bubbles: true, + cancelable: true, + detail + }); + + element.dispatchEvent(event); + + } else if (element instanceof HTMLCollection || element instanceof NodeList) { + for (let e of element) { + fireCustomEvent(e, type, detail); + } + } else { + throw new TypeError('value is not an instance of HTMLElement or HTMLCollection') + } + +} + +/** + * This function gets the path `Event.composedPath()` from an event and tries to find the next element + * up the tree `element.closest()` with the attribute and value. If no value, or a value that is undefined or null, + * is specified, only the attribute is searched. + * + * ``` + * <script type="module"> + * import {findTargetElementFromEvent} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/events.mjs'; + * findTargetElementFromEvent() + * </script> + * ``` + * + * @since 1.14.0 + * @param {Event} event + * @param {string} attributeName + * @param {string|null|undefined} attributeValue + * @throws {Error} unsupported event + * @memberOf Monster.DOM + * @throws {TypeError} value is not a string + * @throws {TypeError} value is not an instance of HTMLElement + * @summary Help function to find the appropriate control + */ +function findTargetElementFromEvent(event, attributeName, attributeValue) { + validateInstance(event, Event); + + if (typeof event.composedPath !== 'function') { + throw new Error('unsupported event'); + } + + const path = event.composedPath(); + + // closest cannot be used here, because closest is not correct for slotted elements + if (isArray(path)) { + for (let i = 0; i < path.length; i++) { + const o = path[i]; + + if (o instanceof HTMLElement && + o.hasAttribute(attributeName) + && (attributeValue === undefined || o.getAttribute(attributeName) === attributeValue)) { + return o; + } + } + } + + return undefined; + +} +'use strict'; + + +/** + * @author schukai GmbH + */ + + +import {internalSymbol} from "../../constants.mjs"; +import {Base} from "../../types/base.mjs"; +import {getGlobal, getGlobalFunction} from "../../types/global.mjs"; +import {isFunction} from "../../types/is.mjs"; +import {validateInstance, validateString} from "../../types/validate.mjs"; + +export {Factory} + +/** + * A factory for creating worker instances. + * + * ``` + * <script type="module"> + * import {Factory} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/worker/factory.mjs'; + * console.log(new Factory()) + * </script> + * ``` + * + * @since 1.25.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM.Worker + * @summary A small factory to create worker + */ +class Factory extends Base { + + + /** + * + */ + constructor() { + super(); + this[internalSymbol] = { + worker: new WeakMap + } + } + + /** + * Creates a worker from a URL + * + * @param {string|URL} url + * @param {function} messageHandler + * @param {function} errorHandler + * @return {Worker} + */ + createFromURL = function (url, messageHandler, errorHandler) { + + if (url instanceof URL) { + url = url.toString(); + } + + const workerClass = getGlobalFunction('Worker'); + var worker = new workerClass(validateString(url)); + + if (isFunction(messageHandler)) { + worker.onmessage = (event) => { + messageHandler.call(worker, event); + } + } + + if (isFunction(errorHandler)) { + worker.onerror = (event) => { + errorHandler.call(worker, event); + } + } + + return worker; + }; + + /** + * Creates a worker from a script + * + * @param {string} content + * @param {function} messageHandler + * @param {function} errorHandler + * @return {Worker} + * @see https://developer.mozilla.org/de/docs/Web/API/URL/createObjectURL + */ + createFromScript = function (content, messageHandler, errorHandler) { + const blobFunction = new getGlobalFunction('Blob') + const blob = new blobFunction([validateString(content)], {type: 'script/javascript'}); + + const url = getGlobalFunction('URL').createObjectURL(blob); + const worker = this.createFromURL(url, messageHandler, errorHandler); + + this[internalSymbol]['worker'].set(worker, url); + + return worker; + + }; + + /** + * Terminate the worker and call revokeObjectURL if necessary. + * + * @param worker + * @return {Monster.DOM.Worker.Factory} + */ + terminate(worker) { + + const workerClass = getGlobalFunction('Worker'); + validateInstance(worker, workerClass); + + worker.terminate(); + + if (this[internalSymbol]['worker'].has(worker)) { + const url = this[internalSymbol]['worker'].get(worker); + URL.revokeObjectURL(url); + } + + return this; + } + + +} +'use strict'; + +/** + * In this namespace you will find classes and methods for handling the DOM. + * + * @namespace Monster.DOM.Worker + * @memberOf Monster + * @author schukai GmbH + */ +const ns = {};'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from '../types/base.mjs'; +import {getGlobalFunction, getGlobalObject} from '../types/global.mjs'; +import {validateInstance, validateString} from "../types/validate.mjs"; +import {ATTRIBUTE_TEMPLATE_PREFIX} from "./constants.mjs"; +import {getDocumentTheme} from "./theme.mjs"; + +export {Template} + +/** + * The template class provides methods for creating templates. + * + * ``` + * <script type="module"> + * import {Template} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/template.mjs'; + * new Template() + * </script> + * ``` + * + * @since 1.6.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @summary A template class + */ +class Template extends Base { + /** + * + * @param {HTMLTemplateElement} template + * @throws {TypeError} value is not an instance of + * @throws {TypeError} value is not a function + * @throws {Error} the function is not defined + */ + constructor(template) { + super(); + const HTMLTemplateElement = getGlobalFunction('HTMLTemplateElement'); + validateInstance(template, HTMLTemplateElement); + this.template = template; + } + + /** + * + * @returns {HTMLTemplateElement} + */ + getTemplateElement() { + return this.template; + } + + /** + * + * @return {DocumentFragment} + * @throws {TypeError} value is not an instance of + */ + createDocumentFragment() { + return this.template.content.cloneNode(true); + } + +} + +/** + * This method loads a template with the given ID and returns it. + * + * To do this, it first reads the theme of the document and looks for the `data-monster-theme-name` attribute in the HTML tag. + * + * ``` + * <html data-monster-theme-name="my-theme"> + * ``` + * + * If no theme was specified, the default theme is `monster`. + * + * Now it is looked if there is a template with the given ID and theme `id-theme` and if yes it is returned. + * If there is no template a search for a template with the given ID `id` is done. If this is also not found, an error is thrown. + * + * You can call the method via the monster namespace `Monster.DOM.findDocumentTemplate()`. + * + * ``` + * <script type="module"> + * import {findTemplate} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/template.mjs'; + * findDocumentTemplate() + * </script> + * ``` + * + * @example + * + * import { findDocumentTemplate } from "https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/template.mjs"; + * + * const template = document.createElement("template"); + * template.id = "myTemplate"; + * template.innerHTML = "<p>my default template</p>"; + * document.body.appendChild(template); + * + * const themedTemplate = document.createElement("template"); + * themedTemplate.id = "myTemplate-myTheme"; + * themedTemplate.innerHTML = "<p>my themed template</p>"; + * document.body.appendChild(themedTemplate); + * + * // loads the temple and since no theme is set the default template + * const template1 = findDocumentTemplate("myTemplate"); + * console.log(template1.createDocumentFragment()); + * // ↦ '<p>my default template</p>' + * + * // now we set our own theme + * document + * .querySelector("html") + * .setAttribute("data-monster-theme-name", "myTheme"); + * + * // now we don't get the default template, + * // but the template with the theme in the id + * const template2 = findDocumentTemplate("myTemplate"); + * console.log(template2.createDocumentFragment()); + * // ↦ '<p>my themed template</p>' + * + * @param {string} id + * @param {Node} currentNode + * @return {Monster.DOM.Template} + * @since 1.7.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @throws {Error} template id not found. + * @throws {TypeError} value is not a string + */ +export function findDocumentTemplate(id, currentNode) { + validateString(id); + + const document = getGlobalObject('document'); + const HTMLTemplateElement = getGlobalFunction('HTMLTemplateElement'); + const DocumentFragment = getGlobalFunction('DocumentFragment'); + const Document = getGlobalFunction('Document'); + + + let prefixID; + + if (!(currentNode instanceof Document || currentNode instanceof DocumentFragment)) { + + if (currentNode instanceof Node) { + + if (currentNode.hasAttribute(ATTRIBUTE_TEMPLATE_PREFIX)) { + prefixID = currentNode.getAttribute(ATTRIBUTE_TEMPLATE_PREFIX) + } + + currentNode = currentNode.getRootNode(); + + if (!(currentNode instanceof Document || currentNode instanceof DocumentFragment)) { + currentNode = currentNode.ownerDocument; + } + + } + + if (!(currentNode instanceof Document || currentNode instanceof DocumentFragment)) { + currentNode = document; + } + } + + let template; + let theme = getDocumentTheme() + + if (prefixID) { + let themedPrefixID = prefixID + '-' + id + '-' + theme.getName(); + + // current + themedPrefixID + template = currentNode.getElementById(themedPrefixID); + if (template instanceof HTMLTemplateElement) { + return new Template(template); + } + + // document + themedPrefixID + template = document.getElementById(themedPrefixID); + if (template instanceof HTMLTemplateElement) { + return new Template(template); + } + } + + let themedID = id + '-' + theme.getName(); + + // current + themedID + template = currentNode.getElementById(themedID); + if (template instanceof HTMLTemplateElement) { + return new Template(template); + } + + // document + themedID + template = document.getElementById(themedID); + if (template instanceof HTMLTemplateElement) { + return new Template(template); + } + + // current + ID + template = currentNode.getElementById(id); + if (template instanceof HTMLTemplateElement) { + return new Template(template); + } + + // document + ID + template = document.getElementById(id); + if (template instanceof HTMLTemplateElement) { + return new Template(template); + } + + throw new Error("template " + id + " not found.") +} + +'use strict'; + + +/** + * @author schukai GmbH + */ + +import {getDocument, getWindow} from "./util.mjs"; + +export {domReady, windowReady} + +/** + * This variable is a promise that is fulfilled as soon as the dom is available. + * + * The DOMContentLoaded event is fired when the original HTML document is fully loaded and parsed + * without waiting for stylesheets, images, and subframes to finish loading. + * + * ``` + * <script type="module"> + * import {domReady} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/ready.mjs'; + * domReady().then(()=>{ + * // ... + * }) + * </script> + * ``` + * + * @since 1.31.0 + * @memberOf Monster.DOM + * @summary variable to check if dom is ready + * @type {Promise} + * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/DOMContentLoaded_event + * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/readyState + */ +const domReady = new Promise(resolve => { + + const document = getDocument(); + + if (document.readyState === "loading") { + document.addEventListener('DOMContentLoaded', resolve); + } else { + resolve(); + } +}); + + +/** + * This variable is a promise that is fulfilled as soon as the windows is available. + * + * The load event fires when the entire page is loaded, including all dependent resources such as stylesheets, + * assets, and images. Unlike DOMContentLoaded, which fires as soon as the DOM of the page is loaded, + * without waiting for the resources to finish loading. + * + * ``` + * <script type="module"> + * import {windowReady} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/ready.mjs'; + * windowReady().then(()=>{ + * // ... + * }) + * </script> + * ``` + * + * @since 1.31.0 + * @memberOf Monster.DOM + * @summary variable to check if window is ready + * @type {Promise} + * @see https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event + * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/readyState + */ +const windowReady = new Promise(resolve => { + + const document = getDocument(); + const window = getWindow(); + + if (document.readyState === 'complete') { + resolve(); + } else { + window.addEventListener('load', resolve); + } +}); +'use strict'; + + +/** + * @author schukai GmbH + */ + +import {extend} from "../data/extend.mjs"; +import {BaseWithOptions} from "../types/basewithoptions.mjs"; +import {getGlobalObject} from "../types/global.mjs"; +import {isArray} from "../types/is.mjs"; +import {Stack} from "../types/stack.mjs"; +import {validateInstance, validateString} from "../types/validate.mjs"; + +export {FocusManager} + +/** + * @private + * @type {string} + */ +const KEY_DOCUMENT = 'document'; + +/** + * @private + * @type {string} + */ +const KEY_CONTEXT = 'context'; + + +/** + * @private + * @type {Symbol} + */ +const stackSymbol = Symbol('stack'); + + +/** + * With the focusmanager the focus can be stored in a document, recalled and moved. + * + * ``` + * <script type="module"> + * import {FocusManager} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/focusmanager.mjs'; + * new FocusManager() + * </script> + * ``` + * + * @since 1.25.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @throws {Error} unsupported locale + * @summary Handle the focus + */ + class FocusManager extends BaseWithOptions { + + /** + * + * @param {Object|undefined} options + */ + constructor(options) { + super(options); + validateInstance(this.getOption(KEY_DOCUMENT), HTMLDocument); + + this[stackSymbol] = new Stack(); + } + + /** + * @property {HTMLDocument} document the document object into which the node is to be appended + */ + get defaults() { + return extend({}, super.defaults, { + [KEY_DOCUMENT]: getGlobalObject('document'), + [KEY_CONTEXT]: undefined, + }) + } + + /** + * Remembers the current focus on a stack. + * Several focus can be stored. + * + * @return {Monster.DOM.FocusManager} + */ + storeFocus() { + + const active = this.getActive(); + if (active instanceof Node) { + this[stackSymbol].push(active) + } + return this; + } + + /** + * The last focus on the stack is set again + * + * @return {Monster.DOM.FocusManager} + */ + restoreFocus() { + + const last = this[stackSymbol].pop(); + if (last instanceof Node) { + this.focus(last); + } + + return this; + } + + /** + * + * @param {Node} element + * @param {boolean} preventScroll + * @throws {TypeError} value is not an instance of + * @return {Monster.DOM.FocusManager} + */ + focus(element, preventScroll) { + + validateInstance(element, Node) + + element.focus({ + preventScroll: preventScroll ?? false + }) + + return this; + } + + /** + * + * @return {Element} + */ + getActive() { + return this.getOption(KEY_DOCUMENT).activeElement; + } + + /** + * Select all elements that can be focused + * + * @param {string|undefined} query + * @return {array} + * @throws {TypeError} value is not an instance of + */ + getFocusable(query) { + + let contextElement = this.getOption(KEY_CONTEXT); + if (contextElement === undefined) { + contextElement = this.getOption(KEY_DOCUMENT); + } + + validateInstance(contextElement, Node) + + if (query !== undefined) { + validateString(query); + } + + return [...contextElement.querySelectorAll( + 'details, button, input, [tabindex]:not([tabindex="-1"]), select, textarea, a[href], body' + )].filter((element) => { + + if (query !== undefined && !element.matches(query)) { + return false; + } + + if (element.hasAttribute('disabled')) return false; + if (element.getAttribute("aria-hidden") === 'true') return false; + + const rect = element.getBoundingClientRect(); + if(rect.width===0) return false; + if(rect.height===0) return false; + + return true; + }); + } + + /** + * @param {string} query + * @return {Monster.DOM.FocusManager} + */ + focusNext(query) { + + const current = this.getActive(); + const focusable = this.getFocusable(query); + + if (!isArray(focusable) || focusable.length === 0) { + return this; + } + + if (current instanceof Node) { + let index = focusable.indexOf(current); + + if (index > -1) { + this.focus(focusable[index + 1] || focusable[0]); + } else { + this.focus(focusable[0]); + } + } else { + this.focus(focusable[0]) + } + + return this; + } + + /** + * @param {string} query + * @return {Monster.DOM.FocusManager} + */ + focusPrev(query) { + + const current = this.getActive(); + const focusable = this.getFocusable(query); + + if (!isArray(focusable) || focusable.length === 0) { + return this; + } + + if (current instanceof Node) { + let index = focusable.indexOf(current); + + if (index > -1) { + this.focus(focusable[index - 1] || focusable[focusable.length - 1]); + } else { + this.focus(focusable[focusable.length - 1]); + } + } else { + this.focus(focusable[focusable.length - 1]) + } + + return this; + } + + +} + + + + + + + +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from '../types/base.mjs'; +import {getGlobalFunction} from "../types/global.mjs"; +import {ProxyObserver} from "../types/proxyobserver.mjs"; +import {validateInstance, validateString} from "../types/validate.mjs"; + +export {ATTRIBUTEPREFIX,Assembler} + +/** + * attribute prefix + * + * @type {string} + * @memberOf Monster.DOM + */ +const ATTRIBUTEPREFIX = "data-monster-"; + +/** + * Assembler class + * + * ``` + * <script type="module"> + * import {Assembler} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/assembler.mjs'; + * console.log(new Assembler()) + * </script> + * ``` + * + * @since 1.6.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @summary Allows you to build an html fragment + */ +class Assembler extends Base { + + /** + * @param {DocumentFragment} fragment + * @throws {TypeError} value is not an instance of + * @throws {TypeError} value is not a function + * @throws {Error} the function is not defined + */ + constructor(fragment) { + super(); + this.attributePrefix = ATTRIBUTEPREFIX; + validateInstance(fragment, getGlobalFunction('DocumentFragment')); + this.fragment = fragment; + } + + /** + * + * @param {string} prefix + * @returns {Assembler} + * @throws {TypeError} value is not a string + */ + setAttributePrefix(prefix) { + validateString(prefix); + this.attributePrefix = prefix; + return this; + } + + /** + * + * @returns {string} + */ + getAttributePrefix() { + return this.attributePrefix; + } + + /** + * + * @param {ProxyObserver|undefined} data + * @return {DocumentFragment} + * @throws {TypeError} value is not an instance of + */ + createDocumentFragment(data) { + + if (data === undefined) { + data = new ProxyObserver({}); + } + + validateInstance(data, ProxyObserver); + let fragment = this.fragment.cloneNode(true); + return fragment; + } + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from "../types/base.mjs"; +import {isObject, isString} from "../types/is.mjs"; +import {validateInstance, validateInteger, validateObject, validateString} from "../types/validate.mjs"; +import {Locale, parseLocale} from "./locale.mjs"; + +export {Translations} + +/** + * With this class you can manage translations and access the keys. + * + * ``` + * <script type="module"> + * import {Translations} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/i18n/translations.mjs'; + * new Translations() + * </script> + * ``` + * + * @example + * + * import {Translations} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/i18n/translations.mjs'; + * import {parseLocale} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/i18n/locale.mjs'; + * + * const translation = new Translations(parseLocale('en-GB')); + * + * translation.assignTranslations({ + * text1: "click", + * text2: { + * 'one': 'click once', + * 'other': 'click n times' + * } + * }); + * + * console.log(translation.getText('text1')); + * // ↦ click + * + * console.log(translation.getPluralRuleText('text2',1)); + * // -> click once + * console.log(translation.getPluralRuleText('text2',2)); + * // -> click n times + * + * @since 1.13.0 + * @copyright schukai GmbH + * @memberOf Monster.I18n + * @see https://datatracker.ietf.org/doc/html/rfc3066 + */ +class Translations extends Base { + + /** + * + * @param {Locale} locale + */ + constructor(locale) { + super(); + + if (isString(locale)) { + locale = parseLocale(locale); + } + + this.locale = validateInstance(locale, Locale); + this.storage = new Map(); + + } + + + /** + * Fetches a text using the specified key. + * If no suitable key is found, `defaultText` is taken. + * + * @param {string} key + * @param {string|undefined} defaultText + * @return {string} + * @throws {Error} key not found + */ + getText(key, defaultText) { + if (!this.storage.has(key)) { + if (defaultText === undefined) { + throw new Error('key ' + key + ' not found'); + } + + return validateString(defaultText); + } + + let r = this.storage.get(key); + if (isObject(r)) { + return this.getPluralRuleText(key, 'other', defaultText); + } + + return this.storage.get(key); + } + + /** + * A number `count` can be passed to this method. In addition to a number, one of the keywords can also be passed directly. + * "zero", "one", "two", "few", "many" and "other". Remember: not every language has all rules. + * + * The appropriate text for this number is then selected. If no suitable key is found, `defaultText` is taken. + * + * @param {string} key + * @param {integer|count} count + * @param {string|undefined} defaultText + * @return {string} + */ + getPluralRuleText(key, count, defaultText) { + if (!this.storage.has(key)) { + return validateString(defaultText); + } + + let r = validateObject(this.storage.get(key)); + + let keyword; + if (isString(count)) { + keyword = count.toLocaleString(); + } else { + count = validateInteger(count); + if (count === 0) { + // special handlig for zero count + if (r.hasOwnProperty('zero')) { + return validateString(r['zero']); + } + } + + keyword = new Intl.PluralRules(this.locale.toString()).select(validateInteger(count)); + } + + if (r.hasOwnProperty(keyword)) { + return validateString(r[keyword]); + } + + if (r.hasOwnProperty(DEFAULT_KEY)) { + return validateString(r[DEFAULT_KEY]); + } + + return validateString(defaultText); + } + + /** + * Set a text for a key + * + * ``` + * translations.setText("text1": "Make my day!"); + * // plural rules + * translations.setText("text6": { + * "zero": "There are no files on Disk.", + * "one": "There is one file on Disk.", + * "other": "There are files on Disk." + * "default": "There are files on Disk." + * }); + * ``` + * + * @param {string} key + * @param {string|object} text + * @return {Translations} + * @throws {TypeError} value is not a string or object + */ + setText(key, text) { + + if (isString(text) || isObject(text)) { + this.storage.set(validateString(key), text); + return this; + } + + throw new TypeError('value is not a string or object'); + + } + + /** + * This method can be used to transfer overlays from an object. The keys are transferred and the values are entered as text. + * + * The values can either be character strings or, in the case of texts with plural forms, objects. The plural forms must be stored as text via a standard key "zero", "one", "two", "few", "many" and "other". + * + * Additionally, the key default can be specified, which will be used if no other key fits. + * + * In some languages, like for example in german, there is no own more number at the value 0. In these languages the function applies additionally zero. + * + * ``` + * translations.assignTranslations({ + * "text1": "Make my day!", + * "text2": "I'll be back!", + * "text6": { + * "zero": "There are no files on Disk.", + * "one": "There is one file on Disk.", + * "other": "There are files on Disk." + * "default": "There are files on Disk." + * }); + * ``` + * + * @param {object} translations + * @return {Translations} + */ + assignTranslations(translations) { + validateObject(translations); + + for (const [k, v] of Object.entries(translations)) { + this.setText(k, v); + } + + return this; + + } + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from "../types/base.mjs"; +import {validateString} from "../types/validate.mjs"; +import {clone} from "../util/clone.mjs"; + +export {Locale, parseLocale} + +/** + * @memberOf Monster.I18n + * @type {symbol} + */ +const propertiesSymbol = Symbol('properties'); + +/** + * @type {symbol} + * @memberOf Monster.I18n + */ +const localeStringSymbol = Symbol('localeString'); + +/** + * The Locale class is a base class for the language classes. + * + * ``` + * <script type="module"> + * import {Locale} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/i18n/locale.mjs'; + * new Locale() + * </script> + * ``` + * + * RFC + * + * ``` + * A Language-Tag consists of: + * langtag ; generated tag + * -or- private-use ; a private use tag + * + * langtag = (language + * ["-" script] + * ["-" region] + * *("-" variant) + * *("-" extension) + * ["-" privateuse]) + * + * language = "en", "ale", or a registered value + * + * script = "Latn", "Cyrl", "Hant" ISO 15924 codes + * + * region = "US", "CS", "FR" ISO 3166 codes + * "419", "019", or UN M.49 codes + * + * variant = "rozaj", "nedis", "1996", multiple subtags can be used in a tag + * + * extension = single letter followed by additional subtags; more than one extension + * may be used in a language tag + * + * private-use = "x-" followed by additional subtags, as many as are required + * Note that these can start a tag or appear at the end (but not + * in the middle) + * ``` + * + * @since 1.13.0 + * @copyright schukai GmbH + * @memberOf Monster.I18n + * @see https://datatracker.ietf.org/doc/html/rfc3066 + */ +class Locale extends Base { + + /** + * @param {string} language + * @param {string} [region] + * @param {string} [script] + * @param {string} [variants] + * @param {string} [extlang] + * @param {string} [privateUse] + * @throws {Error} unsupported locale + */ + constructor(language, region, script, variants, extlang, privateUse) { + super(); + + this[propertiesSymbol] = { + language: (language === undefined) ? undefined : validateString(language), + script: (script === undefined) ? undefined : validateString(script), + region: (region === undefined) ? undefined : validateString(region), + variants: (variants === undefined) ? undefined : validateString(variants), + extlang: (extlang === undefined) ? undefined : validateString(extlang), + privateUse: (privateUse === undefined) ? undefined : validateString(privateUse), + }; + + let s = []; + if (language !== undefined) s.push(language); + if (script !== undefined) s.push(script); + if (region !== undefined) s.push(region); + if (variants !== undefined) s.push(variants); + if (extlang !== undefined) s.push(extlang); + if (privateUse !== undefined) s.push(privateUse); + + if (s.length === 0) { + throw new Error('unsupported locale'); + } + + this[localeStringSymbol] = s.join('-'); + + } + + /** + * @return {string} + */ + get localeString() { + return this[localeStringSymbol]; + } + + /** + * @return {string|undefined} + */ + get language() { + return this[propertiesSymbol].language; + } + + /** + * @return {string|undefined} + */ + get region() { + return this[propertiesSymbol].region; + } + + /** + * @return {string|undefined} + */ + get script() { + return this[propertiesSymbol].script; + } + + /** + * @return {string|undefined} + */ + get variants() { + return this[propertiesSymbol].variants; + } + + /** + * @return {string|undefined} + */ + get extlang() { + return this[propertiesSymbol].extlang; + } + + /** + * @return {string|undefined} + */ + get privateUse() { + return this[propertiesSymbol].privateValue; + } + + + /** + * @return {string} + */ + toString() { + return "" + this.localeString; + } + + /** + * The structure has the following: language, script, region, variants, extlang, privateUse + * + * @return {Monster.I18n.LocaleMap} + */ + getMap() { + return clone(this[propertiesSymbol]) + } + + +} + +/** + * @typedef {Object} LocaleMap + * @property {string} language + * @property {string} script + * @property {string} region + * @property {string} variants + * @property {string} extlang + * @property {string} privateUse + * @memberOf Monster.I18n + */ + +/** + * Parse local according to rfc4646 standard + * + * Limitations: The regex cannot handle multiple variants or private. + * + * You can call the method via the monster namespace `Monster.I18n.createLocale()`. + * + * ``` + * <script type="module"> + * import {Monster} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source//monster.mjs'; + * new Monster.I18n.createLocale() + * </script> + * ``` + * + * Alternatively, you can also integrate this function individually. + * + * ``` + * <script type="module"> + * import {createLocale} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/i18n/locale.mjs'; + * createLocale() + * </script> + * ``` + * + * RFC + * + * ``` + * The syntax of the language tag in ABNF [RFC4234] is: + * + * Language-Tag = langtag + * / privateuse ; private use tag + * / grandfathered ; grandfathered registrations + * + * langtag = (language + * ["-" script] + * ["-" region] + * *("-" variant) + * *("-" extension) + * ["-" privateuse]) + * + * language = (2*3ALPHA [ extlang ]) ; shortest ISO 639 code + * / 4ALPHA ; reserved for future use + * / 5*8ALPHA ; registered language subtag + * + * extlang = *3("-" 3ALPHA) ; reserved for future use + * + * script = 4ALPHA ; ISO 15924 code + * + * region = 2ALPHA ; ISO 3166 code + * / 3DIGIT ; UN M.49 code + * + * variant = 5*8alphanum ; registered variants + * / (DIGIT 3alphanum) + * + * extension = singleton 1*("-" (2*8alphanum)) + * + * singleton = %x41-57 / %x59-5A / %x61-77 / %x79-7A / DIGIT + * ; "a"-"w" / "y"-"z" / "A"-"W" / "Y"-"Z" / "0"-"9" + * ; Single letters: x/X is reserved for private use + * + * privateuse = ("x"/"X") 1*("-" (1*8alphanum)) + * + * grandfathered = 1*3ALPHA 1*2("-" (2*8alphanum)) + * ; grandfathered registration + * ; Note: i is the only singleton + * ; that starts a grandfathered tag + * + * alphanum = (ALPHA / DIGIT) ; letters and numbers + * + * Figure 1: Language Tag ABNF + * ``` + * + * @param {string} locale + * @returns {Locale} + * @since 1.14.0 + * @copyright schukai GmbH + * @memberOf Monster.I18n + * @throws {TypeError} value is not a string + * @throws {Error} unsupported locale + */ +function parseLocale(locale) { + + locale = validateString(locale).replace(/_/g, "-"); + + let language, region, variants, parts, script, extlang, + regexRegular = "(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang)", + regexIrregular = "(en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)", + regexGrandfathered = "(" + regexIrregular + "|" + regexRegular + ")", + regexPrivateUse = "(x(-[A-Za-z0-9]{1,8})+)", + regexSingleton = "[0-9A-WY-Za-wy-z]", + regexExtension = "(" + regexSingleton + "(-[A-Za-z0-9]{2,8})+)", + regexVariant = "([A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3})", + regexRegion = "([A-Za-z]{2}|[0-9]{3})", + regexScript = "([A-Za-z]{4})", + regexExtlang = "([A-Za-z]{3}(-[A-Za-z]{3}){0,2})", + regexLanguage = "(([A-Za-z]{2,3}(-" + regexExtlang + ")?)|[A-Za-z]{4}|[A-Za-z]{5,8})", + regexLangtag = "(" + regexLanguage + "(-" + regexScript + ")?" + "(-" + regexRegion + ")?" + "(-" + regexVariant + ")*" + "(-" + regexExtension + ")*" + "(-" + regexPrivateUse + ")?" + ")", + regexLanguageTag = "^(" + regexGrandfathered + "|" + regexLangtag + "|" + regexPrivateUse + ")$", + regex = new RegExp(regexLanguageTag), match; + + + if ((match = regex.exec(locale)) !== null) { + if (match.index === regex.lastIndex) { + regex.lastIndex++; + } + } + + if (match === undefined || match === null) { + throw new Error('unsupported locale'); + } + + if (match[6] !== undefined) { + language = match[6]; + + parts = language.split('-'); + if (parts.length > 1) { + language = parts[0]; + extlang = parts[1]; + } + + } + + if (match[14] !== undefined) { + region = match[14]; + } + + if (match[12] !== undefined) { + script = match[12]; + } + + if (match[16] !== undefined) { + variants = match[16]; + } + + return new Locale(language, region, script, variants, extlang); + +} +'use strict'; + +/** + * @author schukai GmbH + */ + + +import {internalSymbol} from "../constants.mjs"; +import {extend} from "../data/extend.mjs"; + +import {Formatter as TextFormatter} from "../text/formatter.mjs"; +import {validateInstance, validateString} from "../types/validate.mjs"; +import {Translations} from "./translations.mjs"; + +export {Formatter} + +/** + * @private + * @type {symbol} + */ +const internalTranslationSymbol = Symbol('internalTranslation') + +/** + * The Formatter extends the Text.Formatter with the possibility to replace the key by a translation. + * + * ``` + * <script type="module"> + * import {Formatter} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/i18n/formatter.mjs'; + * new Formatter() + * </script> + * ``` + * + * @example + * + * import {Formatter} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/i18n/formatter.mjs'; + * import {Translations} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/i18n/translations.mjs'; + * + * const translations = new Translations('en') + * .assignTranslations({ + * thekey: "${animal} has eaten the ${food}!" + * }); + * + * new Formatter({}, translations).format("thekey:animal=dog::food=cake") + * // ↦ dog has eaten the cake! + * + * @since 1.26.0 + * @copyright schukai GmbH + * @memberOf Monster.I18n + */ +class Formatter extends TextFormatter { + + /** + * Default values for the markers are `${` and `}` + * + * @param {object} object + * @throws {TypeError} value is not a object + */ + constructor(object, translation, options) { + super(object, options); + this[internalTranslationSymbol] = validateInstance(translation, Translations); + } + + /** + * @property {object} marker + * @property {array} marker.open=["i18n{","${"] + * @property {array} marker.close=["${"] + * @property {object} parameter + * @property {string} parameter.delimiter="::" + * @property {string} parameter.assignment="=" + * @property {object} callbacks + * @property {function} callbacks.i18n=()=>{} + */ + get defaults() { + const self = this; + return extend({}, super.defaults, { + callbacks: { + i18n: (value) => { + return self[internalTranslationSymbol].getText(validateString(value)); + } + }, + marker: { + open: ['i18n{', '${'], + close: ['}'], + }, + }) + } + + /** + * + * @param {string} text + * @return {string} + * @throws {TypeError} value is not a string + * @throws {Error} too deep nesting + * @throws {Error} key not found + * @throws {Error} the closing marker is missing + */ + format(text) { + validateString(text) + + const openMarker = this[internalSymbol]['marker']['open']?.[0]; + const closeMarker = this[internalSymbol]['marker']['close']?.[0]; + + if (text.indexOf(openMarker) === 0) { + text = text.substring(openMarker.length); + + if (text.indexOf(closeMarker) === text.length - closeMarker.length) { + text = text.substring(0, text.length - closeMarker.length); + } else { + throw new Error("the closing marker is missing") + } + } + + + const parts = validateString(text).split('::') + const translationKey = parts.shift().trim(); // key value delimiter + const parameter = parts.join('::').trim(); + + + let assembledText = openMarker + 'static:' + translationKey + ' | call:i18n'; + if (parameter.length > 0) { + assembledText += '::' + parameter; + } + assembledText += closeMarker; + return super.format(assembledText); + } + + +} +'use strict'; + +/** + * In this namespace you will find classes and methods for handling locale and localized texts. + * + * @namespace Monster.I18n.Providers + * @memberOf Monster + * @author schukai GmbH + */ +const ns = {}; +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../../constants.mjs"; +import {extend} from "../../data/extend.mjs"; +import {Formatter} from "../../text/formatter.mjs"; +import {getGlobalFunction} from "../../types/global.mjs"; +import {isInstance, isString} from "../../types/is.mjs"; +import {validateObject, validateString} from "../../types/validate.mjs"; +import {parseLocale} from "../locale.mjs"; +import {Provider} from "../provider.mjs"; +import {Translations} from "../translations.mjs"; + +export {Fetch} + +/** + * The fetch provider retrieves a JSON file from the given URL and returns a translation object. + * + * ``` + * <script type="module"> + * import {Fetch} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/i18n/providers/fetch.mjs'; + * new Fetch() + * </script> + * ``` + * + * @example <caption>das ist ein test</caption> + * + * import {Fetch} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/i18n/providers/fetch.mjs'; + * + * // fetch from API + * const translation = new Fetch('https://example.com/${language}.json').getTranslation('en-GB'); + * // ↦ https://example.com/en.json + * + * @since 1.13.0 + * @copyright schukai GmbH + * @memberOf Monster.I18n.Providers + * @see {@link https://datatracker.ietf.org/doc/html/rfc3066} + * @tutorial i18n-locale-and-formatter + */ + class Fetch extends Provider { + + /** + * As options the key `fetch` can be passed. This config object is passed to the fetch method as init. + * + * The url may contain placeholders (language, script, region, variants, extlang, privateUse), so you can specify one url for all translations. + * + * ``` + * new Fetch('https://www.example.com/assets/${language}.json') + * ``` + * + * @param {string|URL} url + * @param {Object} options see {@link Monster.I18n.Providers.Fetch#defaults} + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/fetch} + */ + constructor(url, options) { + super(options); + + if (isInstance(url, URL)) { + url = url.toString(); + } + + if (options === undefined) { + options = {}; + } + + validateString(url); + + /** + * @property {string} + */ + this.url = url; + + /** + * @private + * @property {Object} options + */ + this[internalSymbol] = extend({}, super.defaults, this.defaults, validateObject(options)); + + } + + /** + * Defaults + * + * @property {Object} fetch + * @property {String} fetch.method=GET + * @property {String} fetch.mode=cors + * @property {String} fetch.cache=no-cache + * @property {String} fetch.credentials=omit + * @property {String} fetch.redirect=follow + * @property {String} fetch.referrerPolicy=no-referrer + * + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API} + */ + get defaults() { + + return { + fetch: { + method: 'GET', // *GET, POST, PUT, DELETE, etc. + mode: 'cors', // no-cors, *cors, same-origin + cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached + credentials: 'omit', // include, *same-origin, omit + redirect: 'follow', // manual, *follow, error + referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url + } + } + + } + + /** + * + * @param {Locale|string} locale + * @return {Promise} + */ + getTranslations(locale) { + + if (isString(locale)) { + locale = parseLocale(locale); + } + + let formatter = new Formatter(locale.getMap()) + + return getGlobalFunction('fetch')(formatter.format(this.url), this.getOption('fetch', {})) + .then((response) => response.json()).then(data => { + return new Translations(locale).assignTranslations(data); + }); + + } + + +} + +'use strict'; + +/** + * In this namespace you will find classes and methods for handling locale and localized texts. + * + * @namespace Monster.I18n + * @memberOf Monster + * @author schukai GmbH + */ +const ns = {};'use strict'; + +/** + * @author schukai GmbH + */ + +import {BaseWithOptions} from "../types/basewithoptions.mjs"; +import {Locale} from "./locale.mjs" +import {Translations} from "./translations.mjs" + +export {Provider} + +/** + * A provider makes a translation object available. + * + * ``` + * <script type="module"> + * import {Provider} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/i18n/provider.mjs'; + * new Provider() + * </script> + * ``` + * + * @since 1.13.0 + * @copyright schukai GmbH + * @memberOf Monster.I18n + * @see {@link https://datatracker.ietf.org/doc/html/rfc3066} + */ +class Provider extends BaseWithOptions { + + /** + * @param {Locale|string} locale + * @return {Promise} + */ + getTranslations(locale) { + return new Promise((resolve, reject) => { + try { + resolve(new Translations(locale)); + } catch (e) { + reject(e); + } + + }); + } + +} +'use strict'; + +/** + * Property-Keys + * @author schukai GmbH + */ + +export { + internalSymbol, + internalStateSymbol +} + + +/** + * @private + * @type {symbol} + * @memberOf Monster + * @since 1.24.0 + */ +const internalSymbol = Symbol('internalData'); + +/** + * @private + * @type {symbol} + * @memberOf Monster + * @since 1.25.0 + */ +const internalStateSymbol = Symbol('state'); + +'use strict'; + + +/** + * @author schukai GmbH + */ + +import {Base} from "./base.mjs"; +import {isString} from "./is.mjs"; +import {validateArray, validateString} from "./validate.mjs"; + +export {MediaType, parseMediaType} + +/** + * @private + * @type {symbol} + */ +const internal = Symbol('internal'); + +/** + * @typedef {Object} Parameter + * @property {string} key + * @property {string} value + * @memberOf Monster.Types + */ + + +/** + * You can create an object via the monster namespace `new Monster.Types.MediaType()`. + * + * ``` + * <script type="module"> + * import {MediaType} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/mediatype.mjs'; + * console.log(new MediaType()) + * </script> + * ``` + * + * @since 1.8.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +class MediaType extends Base { + + /** + * + * @param {String} type + * @param {String} subtype + * @param {Monster.Types.Parameter[]} parameter + */ + constructor(type, subtype, parameter) { + super(); + + this[internal] = { + type: validateString(type).toLowerCase(), + subtype: validateString(subtype).toLowerCase(), + parameter: [] + } + + if (parameter !== undefined) { + this[internal]['parameter'] = validateArray(parameter); + } + + + } + + /** + * @return {String} + */ + get type() { + return this[internal].type; + } + + /** + * @return {String} + */ + get subtype() { + return this[internal].subtype; + } + + /** + * @return {Monster.Types.Parameter[]} + */ + get parameter() { + return this[internal].parameter; + } + + /** + * + * + * @return {Map} + */ + get parameter() { + + const result = new Map + + this[internal]['parameter'].forEach(p => { + + let value = p.value; + + // internally special values are partly stored with quotes, this function removes them. + if (value.startsWith('"') && value.endsWith('"')) { + value = value.substring(1, value.length - 1); + } + + result.set(p.key, value); + }) + + + return result; + } + + /** + * + * @return {string} + */ + toString() { + + let parameter = []; + for (let a of this[internal].parameter) { + parameter.push(a.key + '=' + a.value); + } + + return this[internal].type + '/' + this[internal].subtype + (parameter.length > 0 ? ';' + parameter.join(';') : ''); + } + +} + +/** + * You can call the function via the monster namespace `Monster.Types.parseMediaType()`. + * + * ``` + * <script type="module"> + * import {Monster} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source//monster.mjs'; + * console.log(Monster.Types.parseMediaType()) + * </script> + * ``` + * + * Alternatively, you can also integrate this function individually. + * + * ``` + * <script type="module"> + * import {parseMediaType} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/dataurl.mjs'; + * console.log(parseMediaType()) + * </script> + * ``` + * + * Specification: + * + * ``` + * dataurl := "data:" [ mediatype ] [ ";base64" ] "," data + * mediatype := [ type "/" subtype ] *( ";" parameter ) + * data := *urlchar + * parameter := attribute "=" value + * ``` + * + * @param {String} mediatype + * @return {Monster.Types.MediaType} + * @see https://datatracker.ietf.org/doc/html/rfc2045#section-5.1 + * @throws {TypeError} the mimetype can not be parsed + * @throws {TypeError} blank value is not allowed + * @throws {TypeError} malformed data url + * @memberOf Monster.Types + */ +function parseMediaType(mediatype) { + + const regex = /(?<type>[A-Za-z]+|\*)\/(?<subtype>([a-zA-Z0-9.\+_\-]+)|\*|)(?<parameter>\s*;\s*([a-zA-Z0-9]+)\s*(=\s*("?[A-Za-z0-9_\-]+"?))?)*/g; + const result = regex.exec(validateString(mediatype)); + + const groups = result?.['groups']; + if (groups === undefined) { + throw new TypeError('the mimetype can not be parsed') + } + + const type = groups?.['type']; + const subtype = groups?.['subtype']; + const parameter = groups?.['parameter']; + + if (subtype === "" || type === "") { + throw new TypeError('blank value is not allowed'); + } + + return new MediaType(type, subtype, parseParameter(parameter)); + + +} + +/** + * @private + * @since 1.18.0 + * @param {String} parameter + * @return {Monster.Types.Parameter[]|undefined} + * @memberOf Monster.Types + */ +function parseParameter(parameter) { + + if (!isString(parameter)) { + return undefined; + } + + let result = []; + + parameter.split(';').forEach((entry) => { + + entry = entry.trim(); + if (entry === "") { + return; + } + + const kv = entry.split('=') + + let key = validateString(kv?.[0]).trim(); + let value = validateString(kv?.[1]).trim(); + + // if values are quoted, they remain so internally + result.push({ + key: key, + value: value + }) + + + }) + + return result; + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +export {typeOf} + +/** + * The built-in typeof method is known to have some historical weaknesses. This function tries to provide a better and more accurate result. + * + * ``` + * <script type="module"> + * import {typeOf} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/typeof.mjs'; + * console.log(typeOf()) + * </script> + * ``` + * + * @example + * + * import {typeOf} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/typeof.mjs'; + * + * console.log(typeOf(undefined)); // ↦ undefined + * console.log(typeOf("")); // ↦ string + * console.log(typeOf(5)); // ↦ number + * console.log(typeOf({})); // ↦ object + * console.log(typeOf([])); // ↦ array + * console.log(typeOf(new Map)); // ↦ map + * console.log(typeOf(true)); // ↦ boolean + * + * @param {*} value + * @return {string} + * @since 1.7.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @throws {TypeError} value is not a primitive + */ +function typeOf(value) { + let type = ({}).toString.call(value).match(/\s([a-zA-Z]+)/)[1]; + if ('Object' === type) { + + const name=value.constructor.name; + if (name) { + return name.toLowerCase(); + } + + const results = (/^(class|function)\s+(\w+)/).exec(value.constructor.toString()); + type = (results && results.length > 2) ? results[2] : ''; + } + + return type.toLowerCase(); +} + +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from './base.mjs'; +import {Observer} from "./observer.mjs"; +import {validateInstance} from "./validate.mjs"; + +export {ObserverList} + +/** + * With the help of the ObserverList class, observer can be managed. + * + * ``` + * <script type="module"> + * import {ObserverList} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/observerlist.mjs'; + * console.log(ObserverList()) + * console.log(ObserverList()) + * </script> + * ``` + * + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +class ObserverList extends Base { + + /** + * + */ + constructor() { + super(); + this.observers = []; + } + + /** + * + * @param {Observer} observer + * @return {ObserverList} + * @throws {TypeError} value is not an instance of Observer + */ + attach(observer) { + validateInstance(observer, Observer) + + this.observers.push(observer); + return this; + }; + + /** + * + * @param {Observer} observer + * @return {ObserverList} + * @throws {TypeError} value is not an instance of Observer + */ + detach(observer) { + validateInstance(observer, Observer) + + var i = 0, l = this.observers.length; + for (; i < l; i++) { + if (this.observers[i] === observer) { + this.observers.splice(i, 1); + } + } + + return this; + }; + + /** + * + * @param {Observer} observer + * @return {boolean} + * @throws {TypeError} value is not an instance of Observer + */ + contains(observer) { + validateInstance(observer, Observer) + var i = 0, l = this.observers.length; + for (; i < l; i++) { + if (this.observers[i] === observer) { + return true; + } + } + return false; + }; + + /** + * + * @param subject + * @return {Promise} + */ + notify(subject) { + + let pomises = [] + + let i = 0, l = this.observers.length; + for (; i < l; i++) { + pomises.push(this.observers[i].update(subject)); + } + + return Promise.all(pomises); + }; + +} +'use strict'; + + +/** + * @author schukai GmbH + */ + +import {random} from "../math/random.mjs"; +import {getGlobal} from "./global.mjs"; +import {ID} from "./id.mjs"; + +export {RandomID} + +/** + * @private + * @type {number} + */ +let internalCounter = 0; + +/** + * The `RandomID` class provides a unique ID for an item. + * + * ``` + * <script type="module"> + * import {RandomID} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/randomid.mjs'; + * console.log(new RandomID()) + * </script> + * ``` + * + * @since 1.6.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @summary class to generate random numbers + */ +class RandomID extends ID { + + /** + * create new object + */ + constructor() { + super(); + + internalCounter += 1; + + this.id = getGlobal().btoa(random(1, 10000)) + .replace(/=/g, '') + /** No numbers at the beginning of the ID, because of possible problems with DOM */ + .replace(/^[0-9]+/, 'X') + internalCounter; + } + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../constants.mjs"; +import {random} from "../math/random.mjs"; +import {isObject} from '../types/is.mjs'; +import {Base} from "./base.mjs"; +import {getGlobalObject} from "./global.mjs"; + +export {UUID} + +/** + * The UUID class makes it possible to get a unique UUID for an object. + * + * ``` + * <script type="module"> + * import {Base} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/uuid.mjs'; + * new UUID() + * </script> + * ``` + * + * @since 1.25.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @throws {Error} unsupported + */ + class UUID extends Base { + + /** + * + */ + constructor() { + super(); + + let uuid = createWithCrypto(); + + if (uuid === undefined) { + uuid = createWithRandom(); + } + + + if (uuid === undefined) { + throw new Error('unsupported') + } + + this[internalSymbol] = { + value: uuid + } + + } + + /** + * + * @return {string} + */ + toString() { + return this[internalSymbol]['value']; + } + + +} + +/** + * @private + * @return {string|undefined} + */ +function createWithRandom() { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { + var r = random(0, 65000) * 16 | 0, + v = ((c === 'x') ? r : (r & 0x3 | 0x8)); + return v.toString(16)[0]; + }) +} + + +/** + * @private + * @return {string|undefined} + */ +function createWithCrypto() { + const crypt = getGlobalObject('crypto'); + if (!isObject(crypt)) return; + if (typeof crypt?.['randomUUID']) return; + return crypt.randomUUID(); +} + +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from './base.mjs'; +import {isObject} from './is.mjs'; +import {TokenList} from './tokenlist.mjs'; +import {UniqueQueue} from './uniquequeue.mjs'; + +export {Observer} + +/** + * An observer manages a callback function + * + * ``` + * import {Observer} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/observer.mjs'; + * new Observer() + * ``` + * + * The update method is called with the subject object as this pointer. For this reason the callback should not + * be an arrow function, because it gets the this pointer of its own context. + * + * ``` + * new Observer(()=>{ + * // this is not subject + * }) + * + * new Observer(function() { + * // this is subject + * }) + * ``` + * + * Additional arguments can be passed to the callback. To do this, simply specify them. + * + * ``` + * Observer(function(a, b, c) { + * console.log(a, b, c); // ↦ "a", 2, true + * }, "a", 2, true) + * ``` + * + * The callback function must have as many parameters as arguments are given. + * + * @example + * + * import {Observer} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/observer.mjs'; + * + * const observer = new Observer(function(a, b, c) { + * console.log(this, a, b, c); // ↦ "a", 2, true + * }, "a", 2, true); + * + * observer.update({value:true}).then(()=>{}); + * // ↦ {value: true} "a" 2 true + * + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +class Observer extends Base { + + /** + * + * @param {function} callback + * @param {*} args + */ + constructor(callback, ...args) { + super(); + + if (typeof callback !== 'function') { + throw new Error("observer callback must be a function") + } + + this.callback = callback; + this.arguments = args; + this.tags = new TokenList; + this.queue = new UniqueQueue(); + } + + /** + * + * @param {string} tag + * @returns {Observer} + */ + addTag(tag) { + this.tags.add(tag); + return this; + } + + /** + * + * @param {string} tag + * @returns {Observer} + */ + removeTag(tag) { + this.tags.remove(tag); + return this; + } + + /** + * + * @returns {Array} + */ + getTags() { + return this.tags.entries() + } + + /** + * + * @param {string} tag + * @returns {boolean} + */ + hasTag(tag) { + return this.tags.contains(tag) + } + + /** + * + * @param {object} subject + * @returns {Promise} + */ + update(subject) { + let self = this; + + return new Promise(function (resolve, reject) { + if (!isObject(subject)) { + reject("subject must be an object"); + return; + } + + self.queue.add(subject); + + setTimeout(() => { + + try { + // the queue and the settimeout ensure that an object is not + // informed of the same change more than once. + if (self.queue.isEmpty()) { + resolve(); + return; + } + + let s = self.queue.poll(); + let result = self.callback.apply(s, self.arguments); + + if (isObject(result) && result instanceof Promise) { + result.then(resolve).catch(reject); + return; + } + + resolve(result); + + } catch (e) { + reject(e); + } + }, 0) + + }); + + }; + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {isIterable, isString} from '../types/is.mjs'; +import {validateFunction, validateString} from '../types/validate.mjs'; +import {Base} from './base.mjs'; + +export {TokenList} + +/** + * A tokenlist allows you to manage tokens (individual character strings such as css classes in an attribute string). + * + * The tokenlist offers various functions to manipulate values. For example, you can add, remove or replace a class in a CSS list. + * + * ``` + * <script type="module"> + * import {TokenList} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/tokenlist.mjs'; + * new TokenList("myclass row") + * </script> + * ``` + * + * This class implements the [iteration protocol](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols). + * + * ``` + * typeof new TokenList("myclass row")[Symbol.iterator]; + * // ↦ "function" + * ``` + * + * @since 1.2.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +class TokenList extends Base { + + /** + * + * @param {array|string|iteratable} init + */ + constructor(init) { + super(); + this.tokens = new Set(); + + if (typeof init !== "undefined") { + this.add(init); + } + + } + + /** + * Iterator protocol + * + * @returns {Symbol.iterator} + */ + getIterator() { + return this[Symbol.iterator](); + } + + /** + * Iterator + * + * @returns {{next: ((function(): ({value: *, done: boolean}))|*)}} + */ + [Symbol.iterator]() { + // Use a new index for each iterator. This makes multiple + // iterations over the iterable safe for non-trivial cases, + // such as use of break or nested looping over the same iterable. + let index = 0; + let entries = this.entries() + + return { + next: () => { + if (index < entries.length) { + return {value: entries?.[index++], done: false} + } else { + return {done: true} + } + } + } + } + + /** + * Returns true if it contains token, otherwise false + * + * ``` + * new TokenList("start middle end").contains('start')); // ↦ true + * new TokenList("start middle end").contains('end')); // ↦ true + * new TokenList("start middle end").contains('xyz')); // ↦ false + * new TokenList("start middle end").contains(['end','start','middle'])); // ↦ true + * new TokenList("start middle end").contains(['end','start','xyz'])); // ↦ false + * ``` + * + * @param {array|string|iteratable} value + * @returns {boolean} + */ + contains(value) { + if (isString(value)) { + value = value.trim() + let counter = 0; + value.split(" ").forEach(token => { + if (this.tokens.has(token.trim()) === false) return false; + counter++ + }) + return counter > 0 ? true : false; + } + + if (isIterable(value)) { + let counter = 0; + for (let token of value) { + validateString(token); + if (this.tokens.has(token.trim()) === false) return false; + counter++ + } + return counter > 0 ? true : false; + } + + return false; + } + + /** + * add tokens + * + * ``` + * new TokenList().add("abc xyz").toString(); // ↦ "abc xyz" + * new TokenList().add(["abc","xyz"]).toString(); // ↦ "abc xyz" + * new TokenList().add(undefined); // ↦ add nothing + * ``` + * + * @param {array|string|iteratable} value + * @returns {TokenList} + * @throws {TypeError} unsupported value + */ + add(value) { + if (isString(value)) { + value.split(" ").forEach(token => { + this.tokens.add(token.trim()); + }) + } else if (isIterable(value)) { + for (let token of value) { + validateString(token); + this.tokens.add(token.trim()); + } + } else if (typeof value !== "undefined") { + throw new TypeError("unsupported value"); + } + + return this; + } + + /** + * remove all tokens + * + * @returns {TokenList} + */ + clear() { + this.tokens.clear(); + return this; + } + + /** + * Removes token + * + * ``` + * new TokenList("abc xyz").remove("xyz").toString(); // ↦ "abc" + * new TokenList("abc xyz").remove(["xyz"]).toString(); // ↦ "abc" + * new TokenList("abc xyz").remove(undefined); // ↦ remove nothing + * ``` + * + * @param {array|string|iteratable} value + * @returns {TokenList} + * @throws {TypeError} unsupported value + */ + remove(value) { + if (isString(value)) { + value.split(" ").forEach(token => { + this.tokens.delete(token.trim()); + }) + } else if (isIterable(value)) { + for (let token of value) { + validateString(token); + this.tokens.delete(token.trim()); + } + } else if (typeof value !== "undefined") { + throw new TypeError("unsupported value", "types/tokenlist.mjs"); + } + + return this; + } + + /** + * this method replaces a token with a new token. + * + * if the passed token exists, it is replaced with newToken and TokenList is returned. + * if the token does not exist, newToken is not set and TokenList is returned. + * + * @param {string} token + * @param {string} newToken + * @returns {TokenList} + */ + replace(token, newToken) { + validateString(token); + validateString(newToken); + if (!this.contains(token)) { + return this; + } + + let a = Array.from(this.tokens) + let i = a.indexOf(token); + if (i === -1) return this; + + a.splice(i, 1, newToken); + this.tokens = new Set(); + this.add(a); + + return this; + + + } + + /** + * Removes token from string. If token doesn't exist it's added. + * + * ``` + * new TokenList("abc def ghi").toggle("def xyz").toString(); // ↦ "abc ghi xyz" + * new TokenList("abc def ghi").toggle(["abc","xyz"]).toString(); // ↦ "def ghi xyz" + * new TokenList().toggle(undefined); // ↦ nothing + * ``` + * + * @param {array|string|iteratable} value + * @returns {boolean} + * @throws {TypeError} unsupported value + */ + toggle(value) { + + if (isString(value)) { + value.split(" ").forEach(token => { + toggleValue.call(this, token); + }) + } else if (isIterable(value)) { + for (let token of value) { + toggleValue.call(this, token); + } + } else if (typeof value !== "undefined") { + throw new TypeError("unsupported value", "types/tokenlist.mjs"); + } + + return this; + + } + + /** + * returns an array with all tokens + * + * @returns {array} + */ + entries() { + return Array.from(this.tokens) + } + + /** + * executes the provided function with each value of the set + * + * @param {function} callback + * @returns {TokenList} + */ + forEach(callback) { + validateFunction(callback); + this.tokens.forEach(callback); + return this; + } + + /** + * returns the individual tokens separated by a blank character + * + * @returns {string} + */ + toString() { + return this.entries().join(' '); + } + +} + +/** + * @private + * @param token + * @returns {toggleValue} + * @throws {Error} must be called with TokenList.call + */ +function toggleValue(token) { + if (!(this instanceof TokenList)) throw Error("must be called with TokenList.call") + validateString(token); + token = token.trim(); + if (this.contains(token)) { + this.remove(token); + return this; + } + this.add(token); + return this; +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from './base.mjs'; + +export {Queue} + +/** + * You can create the instance via the monster namespace `new Monster.Types.Queue()`. + * + * ``` + * <script type="module"> + * import {Queue} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/queue.mjs'; + * new Queue() + * </script> + * ``` + * + * @example + * + * import {Queue} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/queue.mjs'; + * + * const queue = new Queue; + * + * queue.add(2); + * queue.add(true); + * queue.add("Hello"); + * queue.add(4.5); + * + * console.log(queue.poll()); + * // ↦ 2 + * console.log(queue.poll()); + * // ↦ true + * console.log(queue.poll()); + * // ↦ "Hello" + * console.log(queue.poll()); + * // ↦ 4.5 + * console.log(queue.poll()); + * // ↦ undefined + * + * + * @since 1.4.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @summary A Queue (Fifo) + */ +class Queue extends Base { + + /** + * + */ + constructor() { + super(); + this.data = []; + } + + + /** + * @return {boolean} + */ + isEmpty() { + return this.data.length === 0; + } + + /** + * Read the element at the front of the queue without removing it. + * + * @return {*} + */ + peek() { + if (this.isEmpty()) { + return undefined; + } + + return this.data[0]; + } + + /** + * Add a new element to the end of the queue. + * + * @param {*} value + * @returns {Queue} + */ + add(value) { + this.data.push(value) + return this; + } + + /** + * remove all entries + * + * @returns {Queue} + */ + clear() { + this.data = []; + return this; + } + + /** + * Remove the element at the front of the queue + * If the queue is empty, return undefined. + * + * @return {*} + */ + poll() { + if (this.isEmpty()) { + return undefined; + } + return this.data.shift(); + } + + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from './base.mjs'; + +export {Stack} + +/** + * You can call the method via the monster namespace `new Monster.Types.Queue()`. + * + * ``` + * <script type="module"> + * import {ID} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/stack.mjs'; + * console.log(new Stack()) + * </script> + * ``` + * + * @since 1.4.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +class Stack extends Base { + + /** + * + */ + constructor() { + super(); + this.data = []; + } + + + /** + * @return {boolean} + */ + isEmpty() { + return this.data.length === 0; + } + + /** + * looks at the object at the top of this stack without removing it from the stack. + * + * @return {*} + */ + peek() { + if (this.isEmpty()) { + return undefined; + } + + return this.data?.[this.data.length - 1]; + } + + /** + * pushes an item onto the top of this stack. + * + * @param {*} value + * @returns {Queue} + */ + push(value) { + this.data.push(value) + return this; + } + + /** + * remove all entries + * + * @returns {Queue} + */ + clear() { + this.data = []; + return this; + } + + /** + * removes the object at the top of this stack and returns + * that object as the value of this function. is the stack empty + * the return value is undefined. + * + * @return {*} + */ + pop() { + if (this.isEmpty()) { + return undefined; + } + return this.data.pop(); + } + + +} +'use strict'; + +/** + * @author schukai GmbH + */ +import {validateString} from "./validate.mjs"; + +export {toBinary, fromBinary} + +/** + * You can call the function via the monster namespace `Monster.Types.toBinary()`. + * + * ``` + * <script type="module"> + * import {toBinary} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/binary.mjs'; + * toBinary() + * </script> + * ``` + * + * @param {String} binary + * @return {String} + * @throws {TypeError} value is not a string + * @memberOf Monster.Types + * @since 1.18.0 + */ +function toBinary(string) { + const codeUnits = new Uint16Array(validateString(string).length); + for (let i = 0; i < codeUnits.length; i++) { + codeUnits[i] = string.charCodeAt(i); + } + + const charCodes = new Uint8Array(codeUnits.buffer); + let result = ''; + + for (let i = 0; i < charCodes.byteLength; i++) { + result += String.fromCharCode(charCodes[i]); + } + + return result; +} + +/** + * You can call the function via the monster namespace `Monster.Types.fromBinary()`. + * + * ``` + * <script type="module"> + * import {fromBinary} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/binary.mjs'; + * fromBinary() + * </script> + * ``` + * + * @param {String} binary + * @return {String} + * @throws {TypeError} value is not a string + * @memberOf Monster.Types + * @since 1.18.0 + */ +function fromBinary(binary) { + const bytes = new Uint8Array(validateString(binary).length); + for (let i = 0; i < bytes.length; i++) { + bytes[i] = binary.charCodeAt(i); + } + const charCodes = new Uint16Array(bytes.buffer); + let result = ''; + for (let i = 0; i < charCodes.length; i++) { + result += String.fromCharCode(charCodes[i]); + } + return result; +} + +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../constants.mjs"; +import {extend} from "../data/extend.mjs"; +import {Pathfinder} from "../data/pathfinder.mjs"; + +import {Base} from "./base.mjs"; +import {validateObject} from "./validate.mjs"; + +export {BaseWithOptions} + +/** + * This is the base class with options from which some monster classes are derived. + * + * This class is actually only used as a base class. + * + * ```html + * <script type="module"> + * import {BaseWithOptions} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/basewithoptions.mjs'; + * new BaseWithOptions() + * </script> + * ``` + * + * Classes that require the possibility of options can be derived directly from this class. + * Derived classes almost always override the `defaul` getter with their own values. + * + * ```javascript + * class My extends BaseWithOptions { + * get defaults() { + * return Object.assign({}, super.defaults, { + * mykey: true + * }); + * } + * } + * ``` + * + * The class was formerly called Object. + * + * @since 1.13.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +class BaseWithOptions extends Base { + + /** + * + * @param {object} options + */ + constructor(options) { + super(); + + if (options === undefined) { + options = {}; + } + + this[internalSymbol] = extend({}, this.defaults, validateObject(options)); + + } + + /** + * This getter provides the options. Derived classes overwrite + * this getter with their own values. It is good karma to always include + * the values from the parent class. + * + * ```javascript + * get defaults() { + * return Object.assign({}, super.defaults, { + * mykey: true + * }); + * } + * + * ``` + * + * @return {object} + */ + get defaults() { + return {} + } + + /** + * nested options can be specified by path `a.b.c` + * + * @param {string} path + * @param {*} defaultValue + * @return {*} + * @since 1.10.0 + */ + getOption(path, defaultValue) { + let value; + + try { + value = new Pathfinder(this[internalSymbol]).getVia(path); + } catch (e) { + + } + + if (value === undefined) return defaultValue; + return value; + } + + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {validateString} from "./validate.mjs"; + +export {escapeString} + +/** + * This function prefixes all special characters that may appear in a regex with a slash. + * + * ``` + * <script type="module"> + * import {escapeString} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/validate.mjs'; + * escapeString() + * </script> + * ``` + * + * @param {string} value + * @return {string} + * @since 1.26.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @throws {TypeError} value is not a string + */ +function escapeString(value) { + return validateString(value) + .replace(/[|\\{}()[\]^$+*?.]/g, '\\$&') + .replace(/-/g, '\\x2d'); +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from './base.mjs'; +import {isArray, isObject, isPrimitive} from "./is.mjs"; +import {Observer} from "./observer.mjs"; +import {ObserverList} from "./observerlist.mjs"; +import {validateObject} from "./validate.mjs"; +import {extend} from "../data/extend.mjs"; + +export {ProxyObserver} + +/** + * An observer manages a callback function + * + * ``` + * <script type="module"> + * import {ProxyObserver} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/proxyobserver.mjs'; + * new ProxyObserver() + * </script> + * ``` + * + * With the ProxyObserver you can attach observer for observation. with each change at the object to be observed an update takes place. + * + * this also applies to nested objects. + * + * @example + * + * import {ProxyObserver} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/proxyobserver.mjs'; + * import {Observer} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/observer.mjs'; + * import {isObject} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/is.mjs'; + * + * const o = new Observer(function () { + * if (isObject(this) && this instanceof ProxyObserver) { + * // do something (this ist ProxyObserver) + * const subject = this.getSubject(); + * console.log(subject); + * } + * }); + * + * let realSubject = { + * a: { + * b: { + * c: true + * }, + * d: 9 + * } + * } + * + * const p = new ProxyObserver(realSubject); + * p.attachObserver(o); + * const s = p.getSubject(); + * s.a.b.c = false; + * + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ + class ProxyObserver extends Base { + + /** + * + * @param {object} object + * @throws {TypeError} value is not a object + */ + constructor(object) { + super(); + + this.realSubject = validateObject(object); + this.subject = new Proxy(object, getHandler.call(this)); + + this.objectMap = new WeakMap(); + this.objectMap.set(this.realSubject, this.subject); + + this.proxyMap = new WeakMap(); + this.proxyMap.set(this.subject, this.realSubject); + + this.observers = new ObserverList; + } + + /** + * get the real object + * + * changes to this object are not noticed by the observers, so you can make a large number of changes and inform the observers later. + * + * @returns {object} + */ + getSubject() { + return this.subject + } + + /** + * @since 1.24.0 + * @param {Object} obj + * @return {Monster.Types.ProxyObserver} + */ + setSubject(obj) { + + let i, k = Object.keys(this.subject); + for (i = 0; i < k.length; i++) { + delete this.subject[k[i]]; + } + + this.subject = extend(this.subject, obj); + return this; + } + + /** + * get the proxied object + * + * @returns {object} + */ + getRealSubject() { + return this.realSubject + } + + /** + * attach a new observer + * + * @param {Observer} observer + * @returns {ProxyObserver} + */ + attachObserver(observer) { + this.observers.attach(observer) + return this; + } + + /** + * detach a observer + * + * @param {Observer} observer + * @returns {ProxyObserver} + */ + detachObserver(observer) { + this.observers.detach(observer) + return this; + } + + /** + * notify all observer + * + * @returns {Promise} + */ + notifyObservers() { + return this.observers.notify(this); + } + + /** + * @param {Observer} observer + * @returns {boolean} + */ + containsObserver(observer) { + return this.observers.contains(observer) + } + +} + +/** + * + * @returns {{defineProperty: (function(*=, *=, *=): *), setPrototypeOf: (function(*, *=): boolean), set: (function(*, *, *, *): boolean), get: ((function(*=, *=, *=): (undefined))|*), deleteProperty: ((function(*, *): (boolean))|*)}} + * @private + * @see {@link https://gitlab.schukai.com/-/snippets/49} + */ +function getHandler() { + + const proxy = this; + + // https://262.ecma-international.org/9.0/#sec-proxy-object-internal-methods-and-internal-slots + const handler = { + + // https://262.ecma-international.org/9.0/#sec-proxy-object-internal-methods-and-internal-slots-get-p-receiver + get: function (target, key, receiver) { + + const value = Reflect.get(target, key, receiver); + + if (typeof key === "symbol") { + return value; + } + + if (isPrimitive(value)) { + return value; + } + + // set value as proxy if object or array + if ((isArray(value) || isObject(value))) { + if (proxy.objectMap.has(value)) { + return proxy.objectMap.get(value); + } else if (proxy.proxyMap.has(value)) { + return value; + } else { + let p = new Proxy(value, handler); + proxy.objectMap.set(value, p); + proxy.proxyMap.set(p, value); + return p; + } + + } + + return value; + + }, + + // https://262.ecma-international.org/9.0/#sec-proxy-object-internal-methods-and-internal-slots-set-p-v-receiver + set: function (target, key, value, receiver) { + + if (proxy.proxyMap.has(value)) { + value = proxy.proxyMap.get(value); + } + + if (proxy.proxyMap.has(target)) { + target = proxy.proxyMap.get(target); + } + + let current = Reflect.get(target, key, receiver); + if (proxy.proxyMap.has(current)) { + current = proxy.proxyMap.get(current); + } + + if (current === value) { + return true; + } + + let result; + let descriptor = Reflect.getOwnPropertyDescriptor(target, key); + + if (descriptor === undefined) { + descriptor = { + writable: true, + enumerable: true, + configurable: true + } + } + + descriptor['value'] = value; + result = Reflect.defineProperty(target, key, descriptor); + + if (typeof key !== "symbol") { + proxy.observers.notify(proxy); + } + + return result; + }, + + + // https://262.ecma-international.org/9.0/#sec-proxy-object-internal-methods-and-internal-slots-delete-p + deleteProperty: function (target, key) { + if (key in target) { + delete target[key]; + + if (typeof key !== "symbol") { + proxy.observers.notify(proxy); + } + + return true; + } + return false; + }, + + // https://262.ecma-international.org/9.0/#sec-proxy-object-internal-methods-and-internal-slots-defineownproperty-p-desc + defineProperty: function (target, key, descriptor) { + + let result = Reflect.defineProperty(target, key, descriptor); + if (typeof key !== "symbol") { + proxy.observers.notify(proxy); + } + return result; + }, + + // https://262.ecma-international.org/9.0/#sec-proxy-object-internal-methods-and-internal-slots-setprototypeof-v + setPrototypeOf: function (target, key) { + let result = Reflect.setPrototypeOf(object1, key); + + if (typeof key !== "symbol") { + proxy.observers.notify(proxy); + } + + return result; + } + + }; + + + return handler; +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {isArray, isInstance} from "./is.mjs"; +import {Node} from "./node.mjs"; +import {validateInstance} from "./validate.mjs"; + +export {NodeList} + +/** + * You can create the instance via the monster namespace `new Monster.Types.NodeList()`. + * + * ``` + * <script type="module"> + * import {NodeList} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/nodelist.mjs'; + * new NodeList() + * </script> + * ``` + * + * @since 1.26.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @summary A NodeList class + */ +class NodeList extends Set { + + /** + * @throws {Error} invalid value type + * @param {NodeList|Node|Array<Node>}values + */ + constructor(values) { + super(); + + const self = this + + if (values === undefined) return; + + if (isArray(values)) { + values.forEach(value => self.add(value)); + } else if (isInstance(values, NodeList)) { + values.forEach(value => self.add(value)); + } else if (isInstance(values, Node)) { + self.add(values); + } else { + throw new Error('invalid value type'); + } + } + + /** + * + * @param {Node} node + * @return {Monster.Types.NodeList} + */ + add(node) { + super.add(validateInstance(node, Node)); + return this; + } + + /** + * @param {Node} node + * @returns {NodeList} + */ + remove(node) { + super.delete(validateInstance(node, Node)); + return this; + } + + /** + * @param {Node} node + * @returns {boolean} + */ + has(node) { + return super.has(validateInstance(node, Node)); + return false; + } + + /** + * @returns {NodeList} + */ + clear() { + super.clear(); + return this; + } + + /** + * @returns {NodeList} + */ + toArray() { + return Array.from(this); + } + + /** + * @returns {NodeList} + */ + toJSON() { + return this.toArray(); + } + + /** + * @returns {NodeList} + */ + toString() { + let parts = []; + + for (const node of this.toArray()) { + parts.push(node.toString()) + } + + return parts.join("\n"); + } + + get length() { + return super.size; + } +} + +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from './base.mjs'; + +export {Version, getMonsterVersion} + +/** + * The version object contains a sematic version number + * + * ``` + * <script type="module"> + * import {Version} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/version.mjs'; + * console.log(new Version('1.2.3')) // ↦ 1.2.3 + * console.log(new Version('1')) // ↦ 1.0.0 + * </script> + * ``` + * + * @example + * + * import {Version} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/version.mjs'; + * + * new Version('1.0.0') // ↦ 1.0.0 + * new Version(1) // ↦ 1.0.0 + * new Version(1, 0, 0) // ↦ 1.0.0 + * new Version('1.2.3', 4, 5) // ↦ 1.4.5 + * + * @since 1.0.0 + * @author schukai GmbH + * @copyright schukai GmbH + * @memberOf Monster.Types + * @summary The version object contains a sematic version number + */ +class Version extends Base { + + /** + * + * @param major + * @param minor + * @param patch + * @throws {Error} major is not a number + * @throws {Error} minor is not a number + * @throws {Error} patch is not a number + */ + constructor(major, minor, patch) { + super(); + + if (typeof major === 'string' && minor === undefined && patch === undefined) { + + let parts = major.toString().split('.'); + major = parseInt(parts[0] || 0); + minor = parseInt(parts[1] || 0); + patch = parseInt(parts[2] || 0); + } + + if (major === undefined) { + throw new Error("major version is undefined"); + } + + if (minor === undefined) { + minor = 0; + } + + if (patch === undefined) { + patch = 0; + } + + this.major = parseInt(major); + this.minor = parseInt(minor); + this.patch = parseInt(patch); + + if (isNaN(this.major)) { + throw new Error("major is not a number"); + } + + if (isNaN(this.minor)) { + throw new Error("minor is not a number"); + } + + if (isNaN(this.patch)) { + throw new Error("patch is not a number"); + } + + } + + /** + * + * @returns {string} + */ + toString() { + return this.major + '.' + this.minor + '.' + this.patch; + } + + /** + * returns 0 if equal, -1 if the object version is less and 1 if greater + * then the compared version + * + * @param {string|Version} version Version to compare + * @returns {number} + */ + compareTo(version) { + + if (version instanceof Version) { + version = version.toString(); + } + + if (typeof version !== 'string') { + throw new Error("type exception"); + } + + if (version === this.toString()) { + return 0; + } + + let a = [this.major, this.minor, this.patch]; + let b = version.split('.'); + let len = Math.max(a.length, b.length); + + for (let i = 0; i < len; i += 1) { + if ((a[i] && !b[i] && parseInt(a[i]) > 0) || (parseInt(a[i]) > parseInt(b[i]))) { + return 1; + } else if ((b[i] && !a[i] && parseInt(b[i]) > 0) || (parseInt(a[i]) < parseInt(b[i]))) { + return -1; + } + } + + return 0; + }; + +} + +let monsterVersion; + +/** + * Version of monster + * + * You can call the method via the monster namespace `Monster.getVersion()`. + * + * ``` + * <script type="module"> + * import {Monster} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source//monster.mjs'; + * console.log(Monster.getVersion()) + * </script> + * ``` + * + * Alternatively, you can also integrate this function individually. + * + * ``` + * <script type="module"> + * import {getMonsterVersion} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/version.mjs'; + * console.log(getMonsterVersion()) + * </script> + * ``` + * + * @returns {Monster.Types.Version} + * @since 1.0.0 + * @copyright schukai GmbH + * @author schukai GmbH + * @memberOf Monster + */ +function getMonsterVersion() { + if (monsterVersion instanceof Version) { + return monsterVersion; + } + /**#@+ dont touch, replaced by make with package.json version */ + monsterVersion = new Version('1.31.0') + /**#@-*/ + + return monsterVersion; + +} +'use strict'; + +/** + * Namespace for types. + * + * @namespace Monster.Types + * @memberOf Monster + * @author schukai GmbH + */ +const ns = {}; + +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from './base.mjs'; +import {isPrimitive} from "./is.mjs"; +import {NodeList} from './nodelist.mjs'; +import {validateInstance} from './validate.mjs'; + +export {Node} + +/** + * @private + * @type {symbol} + */ +const internalValueSymbol = Symbol('internalData'); + +/** + * @private + * @type {symbol} + */ +const treeStructureSymbol = Symbol('treeStructure'); + + +/** + * You can create the instance via the monster namespace `new Monster.Types.Node()`. + * + * ``` + * <script type="module"> + * import {Node} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/node.mjs'; + * new Node() + * </script> + * ``` + * + * @since 1.26.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @summary A Node Class + * @see https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Iteration_protocols + */ +class Node extends Base { + + /** + * @param {*} [value] + */ + constructor(value) { + super(); + this[internalValueSymbol] = value; + + this[treeStructureSymbol] = { + parent: null, + childNodes: new NodeList, + level: 0 + } + + } + + /** + * @property {*} + */ + get value() { + return this[internalValueSymbol]; + } + + /** + * @property {*} + */ + set value(value) { + this[internalValueSymbol] = value; + } + + /** + * @property {Monster.Types.Node|null} + */ + get parent() { + return this[treeStructureSymbol].parent; + } + + /** + * @property {integer} + */ + get level() { + return this[treeStructureSymbol].level; + } + + /** + * + * @property {NodeList} + */ + get childNodes() { + return this[treeStructureSymbol].childNodes; + } + + /** + * + * @property {NodeList} + */ + set childNodes(childNodes) { + this[treeStructureSymbol].childNodes = validateInstance(childNodes, NodeList); + setChildLevelAndParent.call(this, this, 1); + } + + /** + * @return {Monster.Types.Node} + * @param {Node} node + */ + appendChild(node) { + this[treeStructureSymbol].childNodes.add(validateInstance(node, Node)); + node[treeStructureSymbol].parent = this; + + node[treeStructureSymbol].level = this.level + 1; + setChildLevelAndParent.call(this, node, 1); + return this; + } + + /** + * @return {Monster.Types.Node} + * @param {Node} node + */ + removeChild(node) { + this[treeStructureSymbol].childNodes.remove(validateInstance(node, Node)); + node[treeStructureSymbol].parent = null; + + node[treeStructureSymbol].level = 0; + setChildLevelAndParent.call(this, node, -1); + return this; + } + + /** + * + * @return {boolean} + */ + hasChildNodes() { + return this[treeStructureSymbol].childNodes.length > 0; + } + + /** + * @return {Monster.Types.Node} + * @param {Node} node + */ + hasChild(node) { + return this[treeStructureSymbol].childNodes.has(validateInstance(node, Node)); + } + + /** + * @since 1.28.0 + * @return {string} + */ + toString() { + + let parts = []; + if (this[internalValueSymbol]) { + let label = this[internalValueSymbol]; + if (!isPrimitive(label)) label = JSON.stringify(this[internalValueSymbol]) + + parts.push(label); + } + + if (!this.hasChildNodes()) { + return parts.join("\n"); + } + + let count = this.childNodes.length, + counter = 0; + + for (const node of this.childNodes) { + counter++; + const prefix = (count === counter ? '└' : '├').padStart(2 * node.level, ' |'); + parts.push(prefix + node.toString()); + } + + return parts.join("\n"); + } + +} + +/** + * @private + * @param {Node} node + * @param {int} operand + * @return {setChildLevelAndParent} + */ +function setChildLevelAndParent(node, operand) { + const self = this; + + if (node !== this) { + node[treeStructureSymbol].parent = this + } + + node[treeStructureSymbol].childNodes.forEach(function (child) { + child[treeStructureSymbol].parent = node; + child[treeStructureSymbol].level = node[treeStructureSymbol].level + operand; + setChildLevelAndParent.call(self, child, operand); + }); + return this; +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../constants.mjs"; + +import {Base} from './base.mjs'; +import {isInstance} from "./is.mjs"; +import {Node} from "./node.mjs"; +import {NodeList} from "./nodelist.mjs"; +import {validateInstance} from "./validate.mjs"; + +export {NodeRecursiveIterator} + +/** + * @private + * @type {symbol} + */ +const isNodeListSymbol = Symbol('isNodeList'); + +/** + * You can create the instance via the monster namespace `new Monster.Types.NodeRecursiveIterator()`. + * + * ``` + * <script type="module"> + * import {NodeRecursiveIterator} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/noderecursiveiterator.mjs'; + * new NodeRecursiveIterator() + * </script> + * ``` + * + * @example + * import {Node} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/node.mjs'; + * import {NodeRecursiveIterator} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/noderecursiveiterator.mjs'; + * + * const node =new Node('1') + * + * // 1 + * // 2 + * // ├ 2.1 + * // ├ 2.2 + * // └ 2.3 + * // 3 + * // 4 + * // ├ 4.1 + * // └ 4.2 + * node.appendChild( + * (new Node('2')) + * .appendChild(new Node('2.1')) + * .appendChild(new Node('2.2')) + * .appendChild(new Node('2.3'))) + * .appendChild(new Node('3')) + * .appendChild(new Node('4') + * .appendChild(new Node('4.1')) + * .appendChild(new Node('4.2'))); + * + * const iterator = new NodeRecursiveIterator(node); + * + * const result = []; + * for (const n of iterator) { + * result.push(n.value); + * } + * + * // ↦ ['1', '2', '2.1', '2.2', '2.3', '3', '4', '4.1', '4.2'] + * + * @since 1.26.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @summary An iterator to run recursively through a tree of nodes + */ + class NodeRecursiveIterator extends Base { + + /** + * @param {Node} [data] + */ + constructor(node) { + super(); + + this[isNodeListSymbol] = false; + + // iterator is a nodelist + if (isInstance(node, NodeList)) { + let children = node; + node = new Node(); + node.childNodes = children; + this[isNodeListSymbol] = true; + } + + this[internalSymbol] = validateInstance(node, Node); + } + + /** + * @private + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield + */ + [Symbol.iterator] = function* () { + + /** + * The end of the generator function is reached. In this case, execution of the generator + * ends and an IteratorResult is returned to the caller in which the value is undefined and done is true. + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield + */ + if (this[internalSymbol] === undefined) { + return; + } + + // iterator is a nodelist and the main node is only a placeholder + if (this[isNodeListSymbol] !== true) { + yield this[internalSymbol]; + } + + if (this[internalSymbol].hasChildNodes()) { + let childNodes = this[internalSymbol].childNodes; + + for (let node of childNodes) { + yield* new NodeRecursiveIterator(node); + } + } + + return; + } + + /** + * @param {function} callback + * @return {Monster.Types.NodeRecursiveIterator} + */ + forEach(callback) { + for (const node of this) { + callback(node); + } + return this; + } + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {validateFunction, validateObject, validateString} from "./validate.mjs"; + +export {getGlobal, getGlobalObject, getGlobalFunction} + +/** + * @type {objec} + * @private + */ +let globalReference; + +/** + * @private + * @throws {Error} unsupported environment. + */ +(function () { + + if (typeof globalThis === 'object') { + globalReference = globalThis; + return; + } + + if (typeof self !== 'undefined') { + globalReference = self; + return; + } else if (typeof window !== 'undefined') { + globalReference = window; + return; + } + + Object.defineProperty(Object.prototype, '__monster__', { + get: function () { + return this; + }, + configurable: true + }); + + if (typeof __monster__ === 'object') { + __monster__.globalThis = __monster__; + delete Object.prototype.__monster__; + + globalReference = globalThis; + return; + } + + try { + globalReference = Function('return this')(); + } catch (e) { + + } + + throw new Error("unsupported environment.") + + +}()); + +/** + * Return globalThis + * + * If globalThis is not available, it will be polyfilled + * + * @since 1.6.0 + * @memberOf Monster.Types + * @returns {objec} globalThis + */ +function getGlobal() { + return globalReference; +} + +/** + * Return global object or throw Error + * + * You can call the method via the monster namespace `Monster.Types.getGlobalObject()`. + * + * ``` + * <script type="module"> + * import {Monster} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@1.30.0/dist/monster.mjs'; + * Monster.Types.getGlobalObject('document') + * // ↦ { } + * </script> + * ``` + * + * Alternatively, you can also integrate this function individually. + * + * ``` + * <script type="module"> + * import {getGlobalObject} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@1.30.0/dist/modules/types/global.mjs'; + * getGlobalObject('document') + * // ↦ { } + * </script> + * ``` + * + * @since 1.6.0 + * @memberOf Monster.Types + * @param {string} name + * @returns {objec} + * @throws {Error} the object is not defined + * @throws {TypeError} value is not a object + * @throws {TypeError} value is not a string + */ +function getGlobalObject(name) { + validateString(name); + let o = globalReference?.[name]; + if (typeof o === 'undefined') throw new Error('the object ' + name + ' is not defined'); + validateObject(o); + return o; +} + +/** + * Return global function or throw Error + * + * You can call the method via the monster namespace `Monster.Types.getGlobalFunction()`. + * + * ``` + * <script type="module"> + * import {Monster} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@1.30.0/dist/monster.mjs'; + * console.log(Monster.Types.getGlobalFunction('parseInt')) // ↦ f parseInt() { } + * </script> + * ``` + * + * Alternatively, you can also integrate this function individually. + * + * ``` + * <script type="module"> + * import {getGlobalFunction} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@1.30.0/dist/modules/types/global.mjs'; + * console.log(getGlobalFunction('parseInt')) // ↦ f parseInt() { } + * </script> + * ``` + * + * @since 1.6.0 + * @memberOf Monster.Types + * @param {string} name + * @return {objec} + * @throws {TypeError} value is not a function + * @throws {Error} the function is not defined + * @throws {TypeError} value is not a string + */ +function getGlobalFunction(name) { + validateString(name); + let f = globalReference?.[name]; + if (typeof f === 'undefined') throw new Error('the function ' + name + ' is not defined'); + validateFunction(f); + return f; +} + + + + +'use strict'; + + +/** + * @author schukai GmbH + */ + +import {Base} from "./base.mjs"; +import {isString} from "./is.mjs"; +import {MediaType, parseMediaType} from "./mediatype.mjs"; +import {validateBoolean, validateInstance, validateString} from "./validate.mjs"; + +export {DataUrl, parseDataURL} + +/** + * @private + * @type {symbol} + */ +const internal = Symbol('internal'); + + +/** + * You can create an object via the monster namespace `new Monster.Types.DataUrl()`. + * + * ``` + * <script type="module"> + * import {DataUrl} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/dataurl.mjs'; + * new DataUrl() + * </script> + * ``` + * + * @since 1.8.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs + * @see https://datatracker.ietf.org/doc/html/rfc2397 + */ +class DataUrl extends Base { + + /** + * + * @param {String} content + * @param {String|Monster.Types.MediaType} mediatype + * @param {boolean} base64=true + */ + constructor(content, mediatype, base64) { + super(); + + if (isString(mediatype)) { + mediatype = parseMediaType(mediatype); + } + + this[internal] = { + content: validateString(content), + mediatype: validateInstance(mediatype, MediaType), + base64: validateBoolean(base64 === undefined ? true : base64) + } + + + } + + get content() { + return this[internal].base64 ? atob(this[internal].content) : this[internal].content; + } + + get mediatype() { + return this[internal].mediatype; + } + + + /** + * + * @return {string} + * @see https://datatracker.ietf.org/doc/html/rfc2397 + */ + toString() { + + let content = this[internal].content; + + if (this[internal].base64 === true) { + content = ';base64,' + content; + } else { + content = ',' + encodeURIComponent(content); + } + + return 'data:' + this[internal].mediatype.toString() + content; + } + +} + +/** + * You can call the function via the monster namespace `Monster.Types.parseDataURL()`. + * + * ``` + * <script type="module"> + * import {parseDataURL} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/dataurl.mjs'; + * parseDataURL(...) + * </script> + * ``` + * + * Specification: + * + * ``` + * dataurl := "data:" [ mediatype ] [ ";base64" ] "," data + * mediatype := [ type "/" subtype ] *( ";" parameter ) + * data := *urlchar + * parameter := attribute "=" value + * ``` + * + * @param {String} dataurl + * @return {Monster.Types.DataUrl} + * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs + * @see https://datatracker.ietf.org/doc/html/rfc2397 + * @throws {TypeError} incorrect or missing data protocol + * @throws {TypeError} malformed data url + * @memberOf Monster.Types + */ +function parseDataURL(dataurl) { + + validateString(dataurl); + + dataurl = dataurl.trim(); + + if (dataurl.substring(0, 5) !== 'data:') { + throw new TypeError('incorrect or missing data protocol') + } + + dataurl = dataurl.substring(5); + + let p = dataurl.indexOf(','); + if (p === -1) { + throw new TypeError('malformed data url') + } + + let content = dataurl.substring(p + 1); + let mediatypeAndBase64 = dataurl.substring(0, p).trim(); + let mediatype = 'text/plain;charset=US-ASCII'; + let base64Flag = false; + + if (mediatypeAndBase64 !== "") { + mediatype = mediatypeAndBase64; + if (mediatypeAndBase64.endsWith('base64')) { + let i = mediatypeAndBase64.lastIndexOf(';'); + mediatype = mediatypeAndBase64.substring(0, i); + base64Flag = true; + } else { + content = decodeURIComponent(content); + } + + mediatype = parseMediaType(mediatype); + } else { + content = decodeURIComponent(content); + } + + return new DataUrl(content, mediatype, base64Flag); + + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import { + isArray, + isBoolean, + isFunction, + isInstance, + isInteger, + isIterable, + isObject, + isPrimitive, + isString, + isSymbol +} from './is.mjs'; + +export { + validateIterable, + validatePrimitive, + validateBoolean, + validateString, + validateObject, + validateInstance, + validateArray, + validateSymbol, + validateFunction, + validateInteger +} + +/** + * This method checks if the type matches the primitive type. this function is identical to isPrimitive() except that a TypeError is thrown. + * + * ``` + * <script type="module"> + * import {validateIterable} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/validate.mjs'; + * console.log(validateIterable('2')) // ↦ TypeError + * console.log(validateIterable([])) // ↦ value + * </script> + * ``` + * + * @param {*} value + * @return {*} + * @since 1.2.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @throws {TypeError} value is not a primitive + * @see {@link isPrimitive} + * @see {@link Monster.Types.isPrimitive} + * @see {@link Monster.Types#isPrimitive} + */ +function validateIterable(value) { + if (!isIterable(value)) { + throw new TypeError('value is not iterable') + } + return value +} + +/** + * This method checks if the type matches the primitive type. this function is identical to isPrimitive() except that a TypeError is thrown. + * + * ``` + * <script type="module"> + * import {validatePrimitive} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/validate.mjs'; + * console.log(validatePrimitive('2')) // ↦ value + * console.log(validatePrimitive([])) // ↦ TypeError + * </script> + * ``` + * + * @param {*} value + * @return {*} + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @throws {TypeError} value is not a primitive + * @see {@link isPrimitive} + * @see {@link Monster.Types.isPrimitive} + * @see {@link Monster.Types#isPrimitive} + */ +function validatePrimitive(value) { + if (!isPrimitive(value)) { + throw new TypeError('value is not a primitive') + } + return value +} + +/** + * This method checks if the type matches the boolean type. this function is identical to isBoolean() except that a TypeError is thrown. + * + * ``` + * <script type="module"> + * import {validateBoolean} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/validate.mjs'; + * console.log(validateBoolean(false)) // ↦ value + * console.log(validateBoolean('2')) // ↦ TypeError + * console.log(validateBoolean([])) // ↦ TypeError + * </script> + * ``` + * + * @param {*} value + * @return {*} + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + + * @throws {TypeError} value is not primitive + */ +function validateBoolean(value) { + if (!isBoolean(value)) { + throw new TypeError('value is not a boolean') + } + return value +} + +/** + * This method checks if the type matches the string type. this function is identical to isString() except that a TypeError is thrown. + * + * ``` + * <script type="module"> + * import {validateString} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/validate.mjs'; + * console.log(validateString('2')) // ↦ value + * console.log(validateString([])) // ↦ TypeError + * </script> + * ``` + * + * @param {*} value + * @return {*} + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @throws {TypeError} value is not a string + */ +function validateString(value) { + if (!isString(value)) { + throw new TypeError('value is not a string') + } + return value +} + + +/** + * This method checks if the type matches the object type. this function is identical to isObject() except that a TypeError is thrown. + * + * ``` + * <script type="module"> + * import {validateObject} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/validate.mjs'; + * console.log(validateObject({})) // ↦ value + * console.log(validateObject('2')) // ↦ TypeError + * console.log(validateObject([])) // ↦ TypeError + * </script> + * ``` + * + * @param {*} value + * @return {*} + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @throws {TypeError} value is not a object + */ +function validateObject(value) { + if (!isObject(value)) { + throw new TypeError('value is not a object') + } + return value +} + +/** + * This method checks if the type matches the object instance. + * + * ``` + * <script type="module"> + * import {validateInstance} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/validate.mjs'; + * console.log(validateInstance({}, Object)) // ↦ value + * console.log(validateInstance('2', Object)) // ↦ TypeError + * console.log(validateInstance([], Object)) // ↦ TypeError + * </script> + * ``` + * + * @param {*} value + * @return {*} + * @since 1.5.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @throws {TypeError} value is not an instance of + */ +function validateInstance(value, instance) { + if (!isInstance(value, instance)) { + let n = ""; + if (isObject(instance) || isFunction(instance)) { + n = instance?.['name'] + } + + if (n) { + n = " " + n; + } + + throw new TypeError('value is not an instance of' + n) + } + return value +} + +/** + * This method checks if the type matches the array type. this function is identical to isArray() except that a TypeError is thrown. + * + * ``` + * <script type="module"> + * import {validateArray} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/validate.mjs'; + * console.log(validateArray('2')) // ↦ TypeError + * console.log(validateArray([])) // ↦ value + * </script> + * ``` + * + * @param {*} value + * @return {*} + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @throws {TypeError} value is not an array + */ +function validateArray(value) { + if (!isArray(value)) { + throw new TypeError('value is not an array') + } + return value +} + +/** + * This method checks if the type matches the symbol type. this function is identical to isSymbol() except that a TypeError is thrown. + * + * ``` + * <script type="module"> + * import {validateSymbol} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/validate.mjs'; + * console.log(validateSymbol('2')) // ↦ TypeError + * console.log(validateSymbol()) // ↦ value + * </script> + * ``` + * + * @param {*} value + * @return {*} + * @since 1.9.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @throws {TypeError} value is not an symbol + */ +function validateSymbol(value) { + if (!isSymbol(value)) { + throw new TypeError('value is not an symbol') + } + return value +} + +/** + * This method checks if the type matches the function type. this function is identical to isFunction() except that a TypeError is thrown. + * + * ``` + * <script type="module"> + * import {validateFunction} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/validate.mjs'; + * console.log(validateFunction(()=>{})) // ↦ value + * console.log(validateFunction('2')) // ↦ TypeError + * console.log(validateFunction([])) // ↦ TypeError + * </script> + * ``` + * + * @param {*} value + * @return {*} + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @throws {TypeError} value is not a function + */ +function validateFunction(value) { + if (!isFunction(value)) { + throw new TypeError('value is not a function') + } + return value +} + +/** + * This method checks if the type is an integer. this function is identical to isInteger() except that a TypeError is thrown. + * + * ``` + * <script type="module"> + * import {validateFunction} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/validate.mjs'; + * console.log(validateInteger(true)) // ↦ TypeError + * console.log(validateInteger('2')) // ↦ TypeError + * console.log(validateInteger(2)) // ↦ value + * </script> + * ``` + * + * @param {*} value + * @return {*} + * @since 1.4.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @throws {TypeError} value is not an integer + */ +function validateInteger(value) { + if (!isInteger(value)) { + throw new TypeError('value is not an integer') + } + return value +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Queue} from "./queue.mjs"; +import {validateObject} from "./validate.mjs"; + +export {UniqueQueue} + +/** + * A UniqueQueue is a queue that contains items only once. + * + * ``` + * <script type="module"> + * import {UniqueQueue} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/uniquequeue.mjs'; + * new UniqueQueue() + * </script> + * ``` + * + * @since 1.4.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @summary A queue for unique values + */ + class UniqueQueue extends Queue { + + /** + * + */ + constructor() { + super(); + this.unique = new WeakSet(); + } + + /** + * Add a new element to the end of the queue. + * + * @param {object} value + * @returns {Queue} + * @throws {TypeError} value is not a object + */ + add(value) { + + validateObject(value); + + if (!this.unique.has(value)) { + this.unique.add(value); + super.add(value); + } + + return this; + } + + /** + * remove all entries + * + * @returns {Queue} + */ + clear() { + super.clear(); + this.unique = new WeakSet; + return this; + } + + /** + * Remove the element at the front of the queue + * If the queue is empty, return undefined. + * + * @return {object} + */ + poll() { + + if (this.isEmpty()) { + return undefined; + } + let value = this.data.shift(); + this.unique.delete(value); + return value; + } + + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +export {Base} + +/** + * This is the base class from which all monster classes are derived. + * + * ``` + * <script type="module"> + * import {Base} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/base.mjs'; + * new Base() + * </script> + * ``` + * + * The class was formerly called Object. + * + * @since 1.5.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ + class Base extends Object { + + /** + * + * @returns {string} + */ + toString() { + return JSON.stringify(this); + }; + + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +export {isIterable, isPrimitive, isSymbol, isBoolean, isString, isObject, isInstance, isArray, isFunction, isInteger} + + +/** + * With this function you can check if a value is iterable. + * + * This method is used in the library to have consistent names. + * + * You can call the method via the monster namespace `Monster.Types.isPrimitive()`. + * + * ``` + * <script type="module"> + * import {isIterable} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/is.mjs'; + * isIterable(null) // ↦ false + * isIterable('hello') // ↦ true + * isIterable([]) // ↦ true + * </script> + * ``` + * + * @param {*} value + * @returns {boolean} + * @since 1.2.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +function isIterable(value) { + if (value === undefined) return false; + if (value === null) return false; + return typeof value?.[Symbol.iterator] === 'function'; +} + + +/** + * Checks whether the value passed is a primitive (string, number, boolean, NaN, undefined, null or symbol) + * + * This method is used in the library to have consistent names. + * + * ``` + * <script type="module"> + * import {isPrimitive} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/is.mjs'; + * isPrimitive('2')) // ↦ true + * isPrimitive([])) // ↦ false + * </script> + * ``` + * + * @param {*} value + * @returns {boolean} + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +function isPrimitive(value) { + var type; + + if (value === undefined || value === null) { + return true; + } + + type = typeof value; + + if (type === 'string' || type === 'number' || type === 'boolean' || type === 'symbol') { + return true; + } + + return false; +} + +/** + * Checks whether the value passed is a symbol + * + * This method is used in the library to have consistent names. + * + * ``` + * <script type="module"> + * import {isSymbol} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/is.mjs'; + * isSymbol(Symbol('a'))) // ↦ true + * isSymbol([]) // ↦ false + * </script> + * ``` + * + * @param {*} value + * @returns {boolean} + * @since 1.9.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +function isSymbol(value) { + return ('symbol' === typeof value) ? true : false; +} + +/** + * Checks whether the value passed is a boolean. + * + * This method is used in the library to have consistent names. + * + * ``` + * <script type="module"> + * import {isBoolean} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/is.mjs'; + * isBoolean('2')) // ↦ false + * isBoolean([])) // ↦ false + * isBoolean(2>4)) // ↦ true + * </script> + * ``` + * + * @param {*} value + * @returns {boolean} + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +function isBoolean(value) { + + if (value === true || value === false) { + return true; + } + + return false; +} + +/** + * Checks whether the value passed is a string + * + * This method is used in the library to have consistent names. + * + * ``` + * <script type="module"> + * import {isString} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/is.mjs'; + * isString('2')) // ↦ true + * isString([])) // ↦ false + * </script> + * ``` + * + * @param {*} value + * @returns {boolean} + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +function isString(value) { + if (value === undefined || typeof value !== 'string') { + return false; + } + return true; +} + +/** + * Checks whether the value passed is a object + * + * This method is used in the library to have consistent names. + * + * ``` + * <script type="module"> + * import {isObject} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/is.mjs'; + * isObject('2')) // ↦ false + * isObject([])) // ↦ false + * </script> + * ``` + * + * @param {*} value + * @returns {boolean} + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +function isObject(value) { + + if (isArray(value)) return false; + if (isPrimitive(value)) return false; + + if (typeof value === 'object') { + return true; + } + + return false; +} + +/** + * Checks whether the value passed is a object and instance of instance. + * + * This method is used in the library to have consistent names. + * + * ``` + * <script type="module"> + * import {isInstance} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/is.mjs'; + * isInstance('2')) // ↦ false + * isInstance([])) // ↦ false + * </script> + * ``` + * + * @param {*} value + * @param {*} instance + * @returns {boolean} + * @since 1.5.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +function isInstance(value, instance) { + if (!isObject(value)) return false; + if (!isFunction(instance)) return false; + if (!instance.hasOwnProperty('prototype')) return false; + return (value instanceof instance) ? true : false; +} + +/** + * Checks whether the value passed is a array + * + * This method is used in the library to have consistent names. + * + * ``` + * <script type="module"> + * import {isArray} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/is.mjs'; + * isArray('2')) // ↦ false + * isArray([])) // ↦ true + * </script> + * ``` + * + * @param {*} value + * @returns {boolean} + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @see https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray + */ +function isArray(value) { + return Array.isArray(value); +} + +/** + * Checks whether the value passed is a function + * + * This method is used in the library to have consistent names. + * + * ``` + * <script type="module"> + * import {isFunction} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/is.mjs'; + * isFunction(()=>{}) // ↦ true + * isFunction('2')) // ↦ false + * isFunction([])) // ↦ false + * </script> + * ``` + * + * @param {*} value + * @returns {boolean} + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +function isFunction(value) { + if (isArray(value)) return false; + if (isPrimitive(value)) return false; + + if (typeof value === 'function') { + return true; + } + + return false; + +} + +/** + * Checks whether the value passed is an integer. + * + * This method is used in the library to have consistent names. + * + * ``` + * <script type="module"> + * import {isInteger} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/is.mjs'; + * isInteger(()=>{}) // ↦ true + * isInteger('2')) // ↦ false + * isInteger(2)) // ↦ true + * </script> + * ``` + * + * @param {*} value + * @returns {boolean} + * @since 1.4.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +function isInteger(value) { + return Number.isInteger(value); +} + +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from './base.mjs'; +import {validateString} from "./validate.mjs"; + +export {ID} + +/** + * @private + * @type {Map<string, integer>} + */ +let internalCounter = new Map; + +/** + * With the id class, sequences of ids can be created. for this purpose, an internal counter is incremented for each prefix. + * thus, the first id with the prefix `myid` will be `myid1` and the second id `myid2`. + * The ids are the same for every call, for example on a web page. + * + * So the ids can also be used for navigation. you just have to take care that the order stays the same. + * + * ``` + * <script type="module"> + * import {ID} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/id.mjs'; + * console.log(new ID()) + * </script> + * ``` + * + * As of version 1.6.0 there is the new RandomID. this ID class is continuous from now on. + * + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @summary Automatic generation of ids + */ + class ID extends Base { + + /** + * create new id with prefix + * + * @param {string} prefix + */ + constructor(prefix) { + super(); + + if (prefix === undefined) { + prefix = "id"; + } + + validateString(prefix); + + if (!internalCounter.has(prefix)) { + internalCounter.set(prefix, 1); + } + + let count = internalCounter.get(prefix); + this.id = prefix + count; + + internalCounter.set(prefix, ++count); + } + + /** + * @return {string} + */ + toString() { + return this.id; + } + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {ID} from "../types/id.mjs"; +import {isObject} from "../types/is.mjs"; +import {validateString} from "../types/validate.mjs"; + +export {trimSpaces} + +/** + * This special trim function allows to trim spaces that have been protected by a special escape character. + * + * ``` + * <script type="module"> + * import {trimSpaces} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/util/trimspaces.mjs'; + * trimSpaces(' hello \\ ') + * </script> + * ``` + * + * Hint: One stroke is escaped by the javascript interpreter, the second stroke escapes the stroke. + * + * ```text + * a\ b ↦ a b + * a\\ b ↦ a\ b + * ``` + * + * @since 1.24.0 + * @memberOf Monster.Util + * @copyright schukai GmbH + * @param {string} value + * @return {string} + * @throws {TypeError} value is not a string + */ + function trimSpaces(value) { + + validateString(value); + + let placeholder = new Map; + const regex = /((?<pattern>\\(?<char>.)){1})/mig; + + // The separator for args must be escaped + // undefined string which should not occur normally and is also not a regex + let result = value.matchAll(regex) + + for (let m of result) { + let g = m?.['groups']; + if (!isObject(g)) { + continue; + } + + let p = g?.['pattern']; + let c = g?.['char']; + + if (p && c) { + let r = '__' + new ID().toString() + '__'; + placeholder.set(r, c); + value = value.replace(p, r); + } + + } + + value = value.trim(); + placeholder.forEach((v, k) => { + value = value.replace(k, '\\' + v) + }) + + return value; + +} + +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../constants.mjs"; + +import {Base} from "../types/base.mjs"; +import {isInteger} from "../types/is.mjs"; +import {validateFunction, validateInteger} from "../types/validate.mjs"; + +export {DeadMansSwitch} + +/** + * The dead man's switch allows to set a timer which can be reset again and again within a defined period of time. + * + * ``` + * <script type="module"> + * import {DeadMansSwitch} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/util/deadmansswitch.mjs'; + * new DeadMansSwitch(); + * </script> + * ``` + * + * @example + * import {DeadMansSwitch} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/util/deadmansswitch.mjs'; + * + * const deadmansswitch = new DeadMansSwitch(100, ()=>{ + * console.log('yeah!') + * // ↦ "yeah!" + * }) + * + * deadmansswitch.touch(); // from here wait again 100 ms + * deadmansswitch.touch(200); // from here wait 200 ms + * + * @copyright schukai GmbH + * @since 1.29.0 + * @memberOf Monster.Util + * @summary Class to be able to execute function chains + */ + class DeadMansSwitch extends Base { + + /** + * Create new dead man's switch + * + * @param {Integer} delay + * @param {function} callback + * @throw {TypeError} the arguments must be either integer or functions + * @throws {TypeError} value is not an integer + */ + constructor(delay, callback) { + super(); + + init.call(this, validateInteger(delay), validateFunction(callback)); + } + + /** + * + * @param {Integer|undefined} [delay] + */ + touch(delay) { + + if (this[internalSymbol]['isAlreadyRun'] === true) { + throw new Error('has already run') + } + + if (isInteger(delay)) { + this[internalSymbol]['delay'] = delay + } else if (delay !== undefined) { + throw new Error('unsupported argument') + } + + clearTimeout(this[internalSymbol]['timer']); + + initCallback.call(this); + + return this; + } +} + +/** + * @private + */ +function initCallback() { + + const self = this; + + self[internalSymbol]['timer'] = setTimeout(() => { + self[internalSymbol]['isAlreadyRun'] = true; + self[internalSymbol]['callback'](); + }, self[internalSymbol]['delay']) +} + +/** + * @private + * @param {integer} delay + * @param {function} callback + */ +function init(delay, callback) { + const self = this; + + self[internalSymbol] = { + callback, + delay, + isAlreadyRun: false, + timer: undefined + }; + + initCallback.call(self); + +} + + + +'use strict'; + +/** + * @author schukai GmbH + */ + +import {validateObject} from '../types/validate.mjs'; + +export {deepFreeze} + +/** + * Deep freeze a object + * + * ``` + * <script type="module"> + * import {deepFreeze} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/util/freeze.mjs'; + * deepFreeze({}) + * </script> + * ``` + * + * @param {object} object object to be freeze + * @since 1.0.0 + * @returns {object} + * @memberOf Monster.Util + * @copyright schukai GmbH + * @throws {TypeError} value is not a object + */ + function deepFreeze(object) { + + validateObject(object) + + // Retrieve the defined property names of the object + var propNames = Object.getOwnPropertyNames(object); + + // Freeze properties before freezing yourself + for (let name of propNames) { + let value = object[name]; + + object[name] = (value && typeof value === "object") ? + deepFreeze(value) : value; + } + + return Object.freeze(object); +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from '../types/base.mjs'; +import {isFunction} from '../types/is.mjs'; + +export {Comparator} + +/** + * The comparator allows a comparison function to be abstracted. + * + * ``` + * <script type="module"> + * import {Comparator} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/util/comparator.mjs'; + * console.log(new Comparator()) + * </script> + * ``` + * + * The following are some examples of the application of the class. + * + * ``` + * new Comparator().lessThanOrEqual(2, 5) // ↦ true + * new Comparator().greaterThan(4, 2) // ↦ true + * new Comparator().equal(4, 4) // ↦ true + * new Comparator().equal(4, 5) // ↦ false + * ``` + * + * You can also pass your own comparison function, and thus define the comparison function. + * + * ``` + * new Comparator(function (a, b) { + * if (a.v === b.v) return 0; + * return a.v < b.v ? -1 : 1; + * }).equal({v: 2}, {v: 2}); // ↦ true + * ``` + * + * @example + * + * import {Comparator} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/util/comparator.mjs'; + * + * console.log(new Comparator().lessThanOrEqual(2, 5)) + * // ↦ true + * console.log(new Comparator().greaterThan(4, 2)) + * // ↦ true + * console.log(new Comparator().equal(4, 4)) + * // ↦ true + * console.log(new Comparator().equal(4, 5)) + * // ↦ false + * + * @since 1.3.0 + * @memberOf Monster.Util + */ +class Comparator extends Base { + + /** + * create new comparator + * + * @param {Monster.Util~exampleCallback} [callback] Comparator callback + * @throw {TypeError} unsupported type + * @throw {TypeError} impractical comparison + */ + constructor(callback) { + super(); + + if (isFunction(callback)) { + this.compare = callback + } else if (callback !== undefined) { + throw new TypeError("unsupported type") + } else { + // default compare function + + /** + * + * @param {*} a + * @param {*} b + * @return {integer} -1, 0 or 1 + */ + this.compare = function (a, b) { + + if (typeof a !== typeof b) { + throw new TypeError("impractical comparison", "types/comparator.mjs") + } + + if (a === b) { + return 0; + } + return a < b ? -1 : 1; + }; + } + + } + + /** + * changes the order of the operators + * + * @return {Comparator} + */ + reverse() { + const original = this.compare; + this.compare = (a, b) => original(b, a); + return this; + } + + /** + * Checks if two variables are equal. + * + * @param {*} a + * @param {*} b + * + * @return {boolean} + */ + equal(a, b) { + return this.compare(a, b) === 0; + } + + + /** + * Checks if variable `a` is greater than `b` + * + * @param {*} a + * @param {*} b + * + * @return {boolean} + */ + greaterThan(a, b) { + return this.compare(a, b) > 0; + } + + /** + * Checks if variable `a` is greater than or equal to `b` + * + * @param {*} a + * @param {*} b + * + * @return {boolean} + */ + greaterThanOrEqual(a, b) { + return this.greaterThan(a, b) || this.equal(a, b); + } + + /** + * Checks if variable `a` is less than or equal to `b` + * + * @param {*} a + * @param {*} b + * + * @return {boolean} + */ + lessThanOrEqual(a, b) { + return this.lessThan(a, b) || this.equal(a, b); + } + + /** + * Checks if variable a is less than b + * + * @param {*} a + * @param {*} b + * + * @return {boolean} + */ + lessThan(a, b) { + return this.compare(a, b) < 0; + } + + +} + + +/** + * This is the description for the callback function used by the operator + * + * ``` + * new Comparator(function (a, b) { + * if (a.v === b.v) return 0; + * return a.v < b.v ? -1 : 1; + * }).equal({v: 2}, {v: 2}); // ↦ true + * ``` + * + * @callback Monster.Util~exampleCallback + * @param {*} a + * @param {*} b + * @return {integer} -1, 0 or 1 + * @memberOf Monster.Util + * @see Monster.Util.Comparator + */ + +'use strict'; + +/** + * Namespace for utilities. + * + * @namespace Monster.Util + * @memberOf Monster + * @author schukai GmbH + */ +const ns = {};'use strict'; + +/** + * @author schukai GmbH + */ + + +import {getGlobal} from '../types/global.mjs'; +import {isArray, isFunction, isObject, isPrimitive} from '../types/is.mjs'; +import {typeOf} from "../types/typeof.mjs"; +import {validateObject} from "../types/validate.mjs"; + +export {clone} + +/** + * With this function, objects can be cloned. + * The entire object tree is run through. + * + * Proxy, Element, HTMLDocument and DocumentFragment instances are not cloned. + * Global objects such as windows are also not cloned, + * + * If an object has a method `getClone()`, this method is used to create the clone. + * + * ``` + * <script type="module"> + * import {clone} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/util/clone.mjs'; + * clone({}) + * </script> + * ``` + * + * @param {*} obj object to be cloned + * @returns {*} + * @since 1.0.0 + * @memberOf Monster.Util + * @copyright schukai GmbH + * @throws {Error} unable to clone obj! its type isn't supported. + */ +function clone(obj) { + + // typeof null results in 'object'. https://2ality.com/2013/10/typeof-null.html + if (null === obj) { + return obj; + } + + // Handle the two simple types, null and undefined + if (isPrimitive(obj)) { + return obj; + } + + // Handle the two simple types, null and undefined + if (isFunction(obj)) { + return obj; + } + + // Handle Array + if (isArray(obj)) { + let copy = []; + for (var i = 0, len = obj.length; i < len; i++) { + copy[i] = clone(obj[i]); + } + + return copy; + } + + if (isObject(obj)) { + + + // Handle Date + if (obj instanceof Date) { + let copy = new Date(); + copy.setTime(obj.getTime()); + return copy; + } + + /** Do not clone DOM nodes */ + if (typeof Element !== 'undefined' && obj instanceof Element) return obj; + if (typeof HTMLDocument !== 'undefined' && obj instanceof HTMLDocument) return obj; + if (typeof DocumentFragment !== 'undefined' && obj instanceof DocumentFragment) return obj; + + /** Do not clone global objects */ + if (obj === getGlobal()) return obj; + if (typeof globalContext !== 'undefined' && obj === globalContext) return obj; + if (typeof window !== 'undefined' && obj === window) return obj; + if (typeof document !== 'undefined' && obj === document) return obj; + if (typeof navigator !== 'undefined' && obj === navigator) return obj; + if (typeof JSON !== 'undefined' && obj === JSON) return obj; + + // Handle Proxy-Object + try { + // try/catch because possible: TypeError: Function has non-object prototype 'undefined' in instanceof check + if (obj instanceof Proxy) { + return obj; + } + } catch (e) { + } + + return cloneObject(obj) + + } + + throw new Error("unable to clone obj! its type isn't supported."); +} + +/** + * + * @param {object} obj + * @returns {object} + * @private + */ +function cloneObject(obj) { + + validateObject(obj); + + const constructor = obj?.['constructor']; + + /** Object has clone method */ + if(typeOf(constructor)==='function') { + const prototype = constructor?.prototype; + if(typeof prototype==='object') { + if(prototype.hasOwnProperty('getClone')&& typeOf(obj.getClone) === 'function') { + return obj.getClone(); + } + } + } + + let copy = {}; + if (typeof obj.constructor === 'function' && + typeof obj.constructor.call === 'function') { + copy = new obj.constructor(); + } + + for (let key in obj) { + + if (!obj.hasOwnProperty(key)) { + continue; + } + + if (isPrimitive(obj[key])) { + copy[key] = obj[key]; + continue; + } + + copy[key] = clone(obj[key]); + } + + return copy; +} + +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../constants.mjs"; +import {Base} from "../types/base.mjs"; +import {getGlobalFunction} from "../types/global.mjs"; +import {isFunction, isInteger} from "../types/is.mjs"; +import {Queue} from "../types/queue.mjs"; +import {validateFunction, validateInteger} from "../types/validate.mjs"; + +export {Processing} + +/** + * @private + */ +class Callback { + + /** + * + * @param {function} callback + * @param {int|undefined} time + * @throws {TypeError} value is not a function + * @throws {TypeError} value is not an integer + * @private + */ + constructor(callback, time) { + this[internalSymbol] = { + callback: validateFunction(callback), + time: validateInteger(time ?? 0) + }; + } + + /** + * @private + * @param {*} data + * @return {Promise} + */ + run(data) { + const self = this; + return new Promise((resolve, reject) => { + + getGlobalFunction('setTimeout')(() => { + try { + resolve(self[internalSymbol].callback(data)); + } catch (e) { + reject(e); + } + + }, + self[internalSymbol].time); + + + }) + + } +} + +/** + * This class allows to execute several functions in order. + * + * Functions and timeouts can be passed. If a timeout is passed, it applies to all further functions. + * In the example + * + * `timeout1, function1, function2, function3, timeout2, function4` + * + * the timeout1 is valid for the functions 1, 2 and 3 and the timeout2 for the function4. + * + * So the execution time is timeout1+timeout1+timeout1+timeout2 + * + * The result of `run()` is a promise. + * + * ``` + * <script type="module"> + * import {Processing} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/util/processing.mjs'; + * new Processing(); + * </script> + * ``` + * + * @example + * import {Processing} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/util/processing.mjs'; + * + * let startTime = +new Date(); + * + * new Processing((url)=>{ + * return fetch(url) + * },(response)=>{ + * // do something with the response + * console.log(response.status, +new Date()-startTime) + * },200,()=>{ + * // this function is called 200 seconds after fetch is received. + * console.log('finished', +new Date()-startTime) + * return 'done' + * }).run('https://monsterjs.org/assets/world.json').then(r=>{ + * console.log(r) + * // ↦ "done" + * }) + * + * @copyright schukai GmbH + * @since 1.21.0 + * @memberOf Monster.Util + * @summary Class to be able to execute function chains + */ +class Processing extends Base { + + /** + * Create new Processing + * + * Functions and timeouts can be passed. If a timeout is passed, it applies to all further functions. + * In the example + * + * `timeout1, function1, function2, function3, timeout2, function4` + * + * the timeout1 is valid for the functions 1, 2 and 3 and the timeout2 for the function4. + * + * So the execution time is timeout1+timeout1+timeout1+timeout2 + * + * @param {int} timeout Timeout + * @param {function} callback Callback + * @throw {TypeError} the arguments must be either integer or functions + */ + constructor() { + super(); + + this[internalSymbol] = { + queue: new Queue + }; + + let time = 0 + + for (const [, arg] of Object.entries(arguments)) { + if (isInteger(arg) && arg >= 0) { + time = arg; + } else if (isFunction(arg)) { + this[internalSymbol].queue.add(new Callback(arg, time)) + } else { + throw new TypeError('the arguments must be either integer or functions') + } + } + + + } + + /** + * Adds a function with the desired timeout + * If no timeout is specified, the timeout of the previous function is used. + * + * @param {function} callback + * @param {int|undefined} time + * @throws {TypeError} value is not a function + * @throws {TypeError} value is not an integer + */ + add(callback, time) { + this[internalSymbol].queue.add(new Callback(callback, time)) + return this; + } + + + /** + * Executes the defined functions in order. + * + * @param {*} data + * @return {Promise} + */ + run(data) { + const self = this; + if (this[internalSymbol].queue.isEmpty()) { + return Promise.resolve(data); + } + + return this[internalSymbol].queue.poll().run(data).then((result) => { + return self.run(result); + }); + + } + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {isArray, isObject} from "../types/is.mjs"; +import {typeOf} from "../types/typeof.mjs"; + +export {extend} + +/** + * Extend copies all enumerable own properties from one or + * more source objects to a target object. It returns the modified target object. + * + * ``` + * <script type="module"> + * import {extend} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/extend.mjs'; + * extend(a, b) + * </script> + * ``` + * + * @param {object} target + * @param {object} + * @return {object} + * @since 1.10.0 + * @copyright schukai GmbH + * @memberOf Monster.Data + * @throws {Error} unsupported argument + * @throws {Error} type mismatch + */ +function extend() { + let o, i; + + for (i = 0; i < arguments.length; i++) { + let a = arguments[i]; + + if (!(isObject(a) || isArray(a))) { + throw new Error('unsupported argument ' + JSON.stringify(a)); + } + + if (o === undefined) { + o = a; + continue; + } + + for (let k in a) { + + let v = a?.[k]; + + if (v === o?.[k]) { + continue; + } + + if ((isObject(v)&&typeOf(v)==='object') || isArray(v)) { + + if (o[k] === undefined) { + if (isArray(v)) { + o[k] = []; + } else { + o[k] = {}; + } + } else { + if (typeOf(o[k]) !== typeOf(v)) { + throw new Error("type mismatch: " + JSON.stringify(o[k]) + "(" + typeOf(o[k]) + ") != " + JSON.stringify(v) + "(" + typeOf(v) + ")"); + } + } + + o[k] = extend(o[k], v); + + } else { + o[k] = v; + } + + } + } + + return o; +} +'use strict'; + +/** + * @author schukai GmbH + */ + + +import {Base} from '../types/base.mjs'; +import {validateString} from '../types/validate.mjs'; +import {Transformer} from './transformer.mjs'; + +export {Pipe} + +const DELIMITER = '|'; + +/** + * The pipe class makes it possible to combine several processing steps. + * + * ``` + * <script type="module"> + * import {Pipe} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/pipe.mjs'; + * new Pipe() + * </script> + * ``` + * + * A pipe consists of commands whose input and output are connected with the pipe symbol `|`. + * + * With the Pipe, processing steps can be combined. Here, the value of an object is accessed via the pathfinder (path command). + * the word is then converted to uppercase letters and a prefix Hello is added. the two backslash safe the space char. + * + * @example + * import {Pipe} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/pipe.mjs'; + * + * let obj = { + * a: { + * b: { + * c: { + * d: "world" + * } + * } + * } + * } + * + * console.log(new Pipe('path:a.b.c.d | toupper | prefix:Hello\\ ').run(obj)); + * // ↦ Hello WORLD + * + * @since 1.5.0 + * @copyright schukai GmbH + * @memberOf Monster.Data + */ +class Pipe extends Base { + + /** + * + * @param {string} pipe a pipe consists of commands whose input and output are connected with the pipe symbol `|`. + * @throws {TypeError} + */ + constructor(pipe) { + super(); + validateString(pipe); + + this.pipe = pipe.split(DELIMITER).map((v) => { + return new Transformer(v); + }); + + + } + + /** + * + * @param {string} name + * @param {function} callback + * @param {object} context + * @returns {Transformer} + * @throws {TypeError} value is not a string + * @throws {TypeError} value is not a function + */ + setCallback(name, callback, context) { + + for (const [, t] of Object.entries(this.pipe)) { + t.setCallback(name, callback, context); + } + + return this; + } + + /** + * run a pipe + * + * @param {*} value + * @returns {*} + */ + run(value) { + return this.pipe.reduce((accumulator, transformer, currentIndex, array) => { + return transformer.run(accumulator); + }, value); + } +} +'use strict'; + +/** + * @author schukai GmbH + */ + + +import {isFunction, isObject, isString} from "../types/is.mjs"; +import {validateString} from "../types/validate.mjs"; +import {clone} from "../util/clone.mjs"; +import {DELIMITER, Pathfinder, WILDCARD} from "./pathfinder.mjs"; + +export {buildMap, PARENT, assembleParts} + +/** + * @type {string} + * @memberOf Monster.Data + */ +const PARENT = '^'; + + +/** + * With the help of the function `buildMap()`, maps can be easily created from data objects. + * + * Either a simple definition `a.b.c` or a template `${a.b.c}` can be specified as the path. + * Key and value can be either a definition or a template. The key does not have to be defined. + * + * ``` + * <script type="module"> + * import {buildMap} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/buildmap.mjs'; + * console.log(buildMap()) + * </script> + * ``` + * + * The templates determine the appearance of the keys and the value of the map. Either a single value `id` can be taken or a composite key `${id} ${name}` can be used. + * + * If you want to access values of the parent data set, you have to use the `^` character `${id} ${^.name}`. + * + * @example + * + * import {buildMap} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/buildmap.mjs'; + * // a typical data structure as reported by an api + * + * let map; + * let obj = { + * "data": [ + * { + * "id": 10, + * "name": "Cassandra", + * "address": { + * "street": "493-4105 Vulputate Street", + * "city": "Saumur", + * "zip": "52628" + * } + * }, + * { + * "id": 20, + * "name": "Holly", + * "address": { + * "street": "1762 Eget Rd.", + * "city": "Schwalbach", + * "zip": "952340" + * } + * }, + * { + * "id": 30, + * "name": "Guy", + * "address": { + * "street": "957-388 Sollicitudin Avenue", + * "city": "Panchià", + * "zip": "420729" + * } + * } + * ] + * }; + * + * // The function is passed this data structure and with the help of the selector `'data.*'` the data to be considered are selected. + * // The key is given by a simple definition `'id'` and the value is given by a template `'${name} (${address.zip} ${address.city})'`. + * map = buildMap(obj, 'data.*', '${name} (${address.zip} ${address.city})', 'id'); + * console.log(map); + * + * // ↦ Map(3) { + * // '10' => 'Cassandra (52628 Saumur)', + * // '20' => 'Holly (952340 Schwalbach)', + * // '30' => 'Guy (420729 Panchià)' + * // } + * + * // If no key is specified, the key from the selection, here the array index, is taken. + * map = buildMap(obj, 'data.*', '${name} (${address.zip} ${address.city})'); + * console.log(map); + * + * // ↦ Map(3) { + * // '0' => 'Cassandra (52628 Saumur)', + * // '1' => 'Holly (952340 Schwalbach)', + * // '2' => 'Guy (420729 Panchià)' + * // } + * + * // a filter (function(value, key) {}) can be specified to accept only defined entries. + * map = buildMap(obj, 'data.*', '${name} (${address.zip} ${address.city})', 'id', function (value, key) { + * return (value['id'] >= 20) ? true : false + * }); + * console.log(map); + * + * // ↦ Map(2) { + * // 20 => 'Holly (952340 Schwalbach)', + * // 30 => 'Guy (420729 Panchià)' + * // } + * + * @param {*} subject + * @param {string|Monster.Data~exampleSelectorCallback} selector + * @param {string} [valueTemplate] + * @param {string} [keyTemplate] + * @param {Monster.Data~exampleFilterCallback} [filter] + * @return {*} + * @memberOf Monster.Data + * @throws {TypeError} value is neither a string nor a function + * @throws {TypeError} the selector callback must return a map + */ +function buildMap(subject, selector, valueTemplate, keyTemplate, filter) { + return assembleParts(subject, selector, filter, function (v, k, m) { + k = build(v, keyTemplate, k); + v = build(v, valueTemplate); + this.set(k, v); + }); + +} + + +/** + * @private + * @param {*} subject + * @param {string|Monster.Data~exampleSelectorCallback} selector + * @param {Monster.Data~exampleFilterCallback} [filter] + * @param {function} callback + * @return {Map} + * @throws {TypeError} selector is neither a string nor a function + */ +function assembleParts(subject, selector, filter, callback) { + + const result = new Map(); + + let map; + if (isFunction(selector)) { + map = selector(subject) + if (!(map instanceof Map)) { + throw new TypeError('the selector callback must return a map'); + } + } else if (isString(selector)) { + map = new Map; + buildFlatMap.call(map, subject, selector); + } else { + throw new TypeError('selector is neither a string nor a function') + } + + if (!(map instanceof Map)) { + return result; + } + + map.forEach((v, k, m) => { + if (isFunction(filter)) { + if (filter.call(m, v, k) !== true) return; + } + + callback.call(result, v, k, m); + + }); + + return result; +} + +/** + * @private + * @param subject + * @param selector + * @param key + * @param parentMap + * @return {*} + */ +function buildFlatMap(subject, selector, key, parentMap) { + + const result = this; + const currentMap = new Map; + + const resultLength = result.size; + + if (key === undefined) key = []; + + let parts = selector.split(DELIMITER); + let current = "", currentPath = []; + do { + + current = parts.shift(); + currentPath.push(current); + + if (current === WILDCARD) { + + let finder = new Pathfinder(subject); + let map; + + try { + map = finder.getVia(currentPath.join(DELIMITER)); + } catch (e) { + let a = e; + map = new Map(); + } + + for (const [k, o] of map) { + + let copyKey = clone(key); + + currentPath.map((a) => { + copyKey.push((a === WILDCARD) ? k : a) + }) + + let kk = copyKey.join(DELIMITER); + let sub = buildFlatMap.call(result, o, parts.join(DELIMITER), copyKey, o); + + if (isObject(sub) && parentMap !== undefined) { + sub[PARENT] = parentMap; + } + + currentMap.set(kk, sub); + } + + } + + + } while (parts.length > 0); + + // no set in child run + if (resultLength === result.size) { + for (const [k, o] of currentMap) { + result.set(k, o); + } + } + + return subject; + +} + + +/** + * With the help of this filter callback, values can be filtered out. Only if the filter function returns true, the value is taken for the map. + * + * @callback Monster.Data~exampleFilterCallback + * @param {*} value Value + * @param {string} key Key + * @memberOf Monster.Data + * @see {@link Monster.Data.buildMap} + */ + +/** + * Alternatively to a string selector a callback can be specified. this must return a map. + * + * @example + * import {buildMap} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/buildmap.mjs'; + * + * let obj = { + * "data": [ + * { + * "id": 10, + * "name": "Cassandra", + * "enrichment": { + * variants: [ + * { + * sku: 1, label: "XXS", price: [ + * {vk: '12.12 €'}, + * {vk: '12.12 €'} + * ] + * }, + * { + * sku: 2, label: "XS", price: [ + * {vk: '22.12 €'}, + * {vk: '22.12 €'} + * ] + * }, + * { + * sku: 3, label: "S", price: [ + * {vk: '32.12 €'}, + * {vk: '32.12 €'} + * ] + * }, + * { + * sku: 4, label: "L", price: [ + * {vk: '42.12 €'}, + * {vk: '42.12 €'} + * ] + * } + * ] + * + * } + * }, + * { + * "id": 20, + * "name": "Yessey!", + * "enrichment": { + * variants: [ + * { + * sku: 1, label: "XXS", price: [ + * {vk: '12.12 €'}, + * {vk: '12.12 €'} + * ] + * }, + * { + * sku: 2, label: "XS", price: [ + * {vk: '22.12 €'}, + * {vk: '22.12 €'} + * ] + * }, + * { + * sku: 3, label: "S", price: [ + * {vk: '32.12 €'}, + * {vk: '32.12 €'} + * ] + * }, + * { + * sku: 4, label: "L", price: [ + * {vk: '42.12 €'}, + * {vk: '42.12 €'} + * ] + * } + * ] + * + * } + * } + * ] + * }; + * + * let callback = function (subject) { + * let m = new Map; + * + * for (const [i, b] of Object.entries(subject.data)) { + * + * let key1 = i; + * + * for (const [j, c] of Object.entries(b.enrichment.variants)) { + * let key2 = j; + * + * for (const [k, d] of Object.entries(c.price)) { + * + * let key3 = k; + * + * d.name = b.name; + * d.label = c.label; + * d.id = [key1, key2, key3].join('.'); + * + * m.set(d.id, d); + * } + * + * } + * } + * return m; + * } + * + * let map = buildMap(obj, callback, '${name} ${vk}', '${id}') + * + * // ↦ Map(3) { + * // "0.0.0":"Cassandra 12.12 €", + * // "0.0.1":"Cassandra 12.12 €", + * // "0.1.0":"Cassandra 22.12 €", + * // "0.1.1":"Cassandra 22.12 €", + * // "0.2.0":"Cassandra 32.12 €", + * // "0.2.1":"Cassandra 32.12 €", + * // "0.3.0":"Cassandra 42.12 €", + * // "0.3.1":"Cassandra 42.12 €", + * // "1.0.0":"Yessey! 12.12 €", + * // "1.0.1":"Yessey! 12.12 €", + * // "1.1.0":"Yessey! 22.12 €", + * // "1.1.1":"Yessey! 22.12 €", + * // "1.2.0":"Yessey! 32.12 €", + * // "1.2.1":"Yessey! 32.12 €", + * // "1.3.0":"Yessey! 42.12 €", + * // "1.3.1":"Yessey! 42.12 €" + * // } + * + * @callback Monster.Data~exampleSelectorCallback + * @param {*} subject subject + * @return Map + * @since 1.17.0 + * @memberOf Monster.Data + * @see {@link Monster.Data.buildMap} + */ + +/** + * @private + * @param {*} subject + * @param {string|undefined} definition + * @param {*} defaultValue + * @return {*} + */ +function build(subject, definition, defaultValue) { + if (definition === undefined) return defaultValue ? defaultValue : subject; + validateString(definition); + + const regexp = /(?<placeholder>\${(?<path>[a-z\^A-Z.\-_0-9]*)})/gm + const array = [...definition.matchAll(regexp)]; + + let finder = new Pathfinder(subject); + + if (array.length === 0) { + return finder.getVia(definition); + } + + array.forEach((a) => { + let groups = a?.['groups']; + let placeholder = groups?.['placeholder'] + if (placeholder === undefined) return; + + let path = groups?.['path'] + + let v = finder.getVia(path); + if (v === undefined) v = defaultValue; + + definition = definition.replaceAll(placeholder, v); + + + }) + + return definition; + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../constants.mjs"; + +import {Base} from "../types/base.mjs"; +import {parseDataURL} from "../types/dataurl.mjs"; +import {isString} from "../types/is.mjs"; +import {ProxyObserver} from "../types/proxyobserver.mjs"; +import {validateObject} from "../types/validate.mjs"; +import {extend} from "./extend.mjs"; +import {Pathfinder} from "./pathfinder.mjs"; + +export {Datasource} + +/** + * @private + * @type {symbol} + * @memberOf Monster.Data + * @since 1.24.0 + */ +const internalDataSymbol = Symbol('internalData'); + +/** + * The datasource class is the basis for dealing with different data sources. + * It provides a unified interface for accessing data + * + * ``` + * <script type="module"> + * import {Datasource} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/datasource.mjs'; + * new Datasource() + * </script> + * ``` + * + * @example + * + * import {Datasource} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/datasource.mjs' + * + * class MyDatasource extends Datasource { + * + * } + * + * const ds = new MyDatasource(); + * + * @since 1.22.0 + * @copyright schukai GmbH + * @memberOf Monster.Data + * @summary The datasource class encapsulates the access to data objects. + */ +class Datasource extends Base { + + /** + * + */ + constructor() { + super(); + this[internalSymbol] = new ProxyObserver({ + 'options': extend({}, this.defaults) + }); + + this[internalDataSymbol] = new ProxyObserver({ + + }); + + + } + + /** + * attach a new observer + * + * @param {Observer} observer + * @returns {Datasource} + */ + attachObserver(observer) { + this[internalDataSymbol].attachObserver(observer) + return this; + } + + /** + * detach a observer + * + * @param {Observer} observer + * @returns {Datasource} + */ + detachObserver(observer) { + this[internalDataSymbol].detachObserver(observer) + return this; + } + + /** + * @param {Observer} observer + * @returns {boolean} + */ + containsObserver(observer) { + return this[internalDataSymbol].containsObserver(observer); + } + + /** + * Derived classes can override and extend this method as follows. + * + * ``` + * get defaults() { + * return Object.assign({}, super.defaults, { + * myValue:true + * }); + * } + * ``` + */ + get defaults() { + return {}; + } + + /** + * Set option + * + * @param {string} path + * @param {*} value + * @return {Datasource} + */ + setOption(path, value) { + new Pathfinder(this[internalSymbol].getSubject()['options']).setVia(path, value); + return this; + } + + /** + * @param {string|object} options + * @return {Datasource} + * @throws {Error} the options does not contain a valid json definition + */ + setOptions(options) { + + if (isString(options)) { + options = parseOptionsJSON(options) + } + + const self = this; + extend(self[internalSymbol].getSubject()['options'], self.defaults, options); + + return self; + } + + /** + * nested options can be specified by path `a.b.c` + * + * @param {string} path + * @param {*} defaultValue + * @return {*} + */ + getOption(path, defaultValue) { + let value; + + try { + value = new Pathfinder(this[internalSymbol].getRealSubject()['options']).getVia(path); + } catch (e) { + + } + + if (value === undefined) return defaultValue; + return value; + } + + /** + * @throws {Error} this method must be implemented by derived classes. + * @return {Promise} + */ + read() { + throw new Error("this method must be implemented by derived classes") + } + + /** + * @throws {Error} this method must be implemented by derived classes. + * @return {Promise} + */ + write() { + throw new Error("this method must be implemented by derived classes") + } + + + /** + * Returns real object + * + * @return {Object|Array} + */ + get() { + const self = this; + return self[internalDataSymbol].getRealSubject(); + } + + /** + * @param {Object|Array} data + * @return {Datasource} + */ + set(data) { + const self = this; + self[internalDataSymbol].setSubject(data); + return self; + } + +} + +/** + * @private + * @param data + * @return {Object} + * @throws {Error} the options does not contain a valid json definition + */ +function parseOptionsJSON(data) { + if (isString(data)) { + + // the configuration can be specified as a data url. + try { + let dataUrl = parseDataURL(data); + data = dataUrl.content; + } catch (e) { + + } + + + try { + let obj = JSON.parse(data); + validateObject(obj); + return obj; + } catch (e) { + throw new Error('the options does not contain a valid json definition (actual: ' + data + ').'); + } + } + + return {}; +} +'use strict'; + +/** + * In this namespace you will find classes and methods for handling data. + * + * @namespace Monster.Data + * @memberOf Monster + * @author schukai GmbH + */ +const ns = {};'use strict'; + +/** + * @author schukai GmbH + */ + + +import {isArray, isObject} from "../types/is.mjs"; +import {Node} from "../types/node.mjs"; +import {NodeList} from "../types/nodelist.mjs"; +import {assembleParts} from "./buildmap.mjs"; +import {extend} from "./extend.mjs"; + +export {buildTree} + +/** + * @private + * @type {symbol} + */ +const parentSymbol = Symbol('parent'); + +/** + * @private + * @type {symbol} + */ +const rootSymbol = Symbol('root'); + +/** + * @typedef {Object} buildTreeOptions + * @property {array} options.rootReferences=[null, undefined] defines the values for elements without parents + * @property {Monster.Data~exampleFilterCallback} options.filter filtering of the values + * @memberOf Monster.Data + */ + +/** + * With the help of the function `buildTree()`, nodes can be easily created from data objects. + * + * ``` + * <script type="module"> + * import {buildTree} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/buildtree.mjs'; + * buildTree() + * </script> + * ``` + * + * @param {*} subject + * @param {string|Monster.Data~exampleSelectorCallback} selector + * @param {string} idKey + * @param {string} parentIDKey + * @param {buildTreeOptions} [options] + * @return {*} + * @memberOf Monster.Data + * @throws {TypeError} value is neither a string nor a function + * @throws {TypeError} the selector callback must return a map + * @throws {Error} the object has no value for the specified id + * @since 1.26.0 + */ +function buildTree(subject, selector, idKey, parentIDKey, options) { + + const nodes = new Map; + + if (!isObject(options)) { + options = {} + } + + options = extend({}, { + rootReferences: [null, undefined], + filter: undefined + }, options) + + const filter = options?.filter; + let rootReferences = options.rootReferences; + if (!isArray(rootReferences)) { + rootReferences = [rootReferences]; + } + + const childMap = assembleParts(subject, selector, filter, function (o, k, m) { + + const key = o?.[idKey] + let ref = o?.[parentIDKey] + if (rootReferences.indexOf(ref) !== -1) ref = rootSymbol; + + if (key === undefined) { + throw new Error('the object has no value for the specified id') + } + + o[parentSymbol] = ref; + + const node = new Node(o); + this.has(ref) ? this.get(ref).add(node) : this.set(ref, new NodeList().add(node)); + nodes.set(key, node); + + }) + + nodes.forEach(node => { + + let id = node?.['value']?.[idKey]; + + if (childMap.has(id)) { + node.childNodes = childMap.get(id); + childMap.delete(id) + } + }) + + const list = new NodeList; + + childMap.forEach((s) => { + if (s instanceof Set) { + s.forEach((n) => { + list.add(n); + }) + } + }) + + return list; +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from '../types/base.mjs'; +import {getGlobal, getGlobalObject} from "../types/global.mjs"; +import {ID} from '../types/id.mjs'; +import {isArray, isObject, isString} from '../types/is.mjs'; +import { + validateFunction, + validateInteger, + validateObject, + validatePrimitive, + validateString +} from '../types/validate.mjs'; +import {clone} from "../util/clone.mjs"; +import {Pathfinder} from "./pathfinder.mjs"; + +export {Transformer} + +/** + * The transformer class is a swiss army knife for manipulating values. especially in combination with the pipe, processing chains can be built up. + * + * ``` + * <script type="module"> + * import {Transformer} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/transformer.mjs'; + * new Transformer() + * </script> + * ``` + * + * A simple example is the conversion of all characters to lowercase. for this purpose the command `tolower` must be used. + * + * ``` + * let t = new Transformer('tolower').run('ABC'); // ↦ abc + * ``` + * + * **all commands** + * + * in the following table all commands, parameters and existing aliases are described. + * + * | command | parameter | alias | description | + * |:-------------|:---------------------------|:------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + * | to-base64 | | base64, btob | Converts the value to base64 | + * | from-base64 | | atob | Converts the value from base64 | + * | call | function:param1:param2:... | | Calling a callback function. The function can be defined in three places: either globally, in the context `addCallback` or in the passed object | + * | default | value:type | ?? | If the value is undefined the first argument is returned, otherwise the value. The third optional parameter specifies the desired type. If no type is specified, string is used. Valid types are bool, string, int, float, undefined and object. An object default value must be specified as a base64 encoded json string. (since 1.12.0) | + * | debug | | | the passed value is output (console) and returned | + * | empty | | | Return empty String "" | + * | first-key | default | | Can be applied to objects and returns the value of the first key. All keys of the object are fetched and sorted. (since 1.23.0) | + * | fromjson | | | Type conversion from a JSON string (since 1.12.0) | + * | if | statement1:statement2 | ? | Is the ternary operator, the first parameter is the valid statement, the second is the false part. To use the current value in the queue, you can set the value keyword. On the other hand, if you want to have the static string "value", you have to put one backslash \\ in front of it and write value. the follow values are true: 'on', true, 'true'. If you want to have a space, you also have to write \\ in front of the space. | + * | index | key:default | property, key | Fetches a value from an object, an array, a map or a set | + * | last-key | default | | Can be applied to objects and returns the value of the last key. All keys of the object are fetched and sorted. (since 1.23.0) | + * | length | | count | Length of the string or entries of an array or object | + * | nop | | | Do nothing | + * | nth-key | index:default | | Can be applied to objects and returns the value of the nth key. All keys of the object are fetched and sorted. (since 1.23.0) | + * | nth-last-key | index:default | | Can be applied to objects and returns the value of the nth key from behind. All keys of the object are fetched and sorted. (since 1.23.0) | + * | path | path | | The access to an object is done via a Pathfinder object | + * | path-exists | path | | Check if the specified path is available in the value (since 1.24.0) | + * | plaintext | | plain | All HTML tags are removed (*) | + * | prefix | text | | Adds a prefix | + * | rawurlencode | | | URL coding | + * | static | | none | The Arguments value is used and passed to the value. Special characters \ <space> and : can be quotet by a preceding \. | + * | substring | start:length | | Returns a substring | + * | suffix | text | | Adds a suffix | + * | tointeger | | | Type conversion to an integer value | + * | tojson | | | Type conversion to a JSON string (since 1.8.0) | + * | tolower | | strtolower, tolowercase | The input value is converted to lowercase letters | + * | tostring | | | Type conversion to a string. | + * | toupper | | strtoupper, touppercase | The input value is converted to uppercase letters | + * | trim | | | Remove spaces at the beginning and end | + * | ucfirst | | | First character large | + * | ucwords | | | Any word beginning large | + * | undefined | | | Return undefined | + * | uniqid | | | Creates a string with a unique value (**) + * + * (*) for this functionality the extension [jsdom](https://www.npmjs.com/package/jsdom) must be loaded in the nodejs context. + * + * ``` + * // polyfill + * if (typeof window !== "object") { + * const {window} = new JSDOM('', { + * url: 'http://example.com/', + * pretendToBeVisual: true + * }); + * + * [ + * 'self', + * 'document', + * 'Node', + * 'Element', + * 'HTMLElement', + * 'DocumentFragment', + * 'DOMParser', + * 'XMLSerializer', + * 'NodeFilter', + * 'InputEvent', + * 'CustomEvent' + * ].forEach(key => (global[key] = window[key])); + * } + * ``` + * + * (**) for this command the crypt library is necessary in the nodejs context. + * + * ``` + * import * as Crypto from "@peculiar/webcrypto"; + * global['crypto'] = new Crypto.Crypto(); + * ``` + * + * @example + * + * import {Transformer} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/transformer.mjs'; + * + * const transformer = new Transformer("tolower") + * + * console.log(transformer.run("HELLO")) + * // ↦ hello + * + * console.log(transformer.run("WORLD")) + * // ↦ world + * + * @since 1.5.0 + * @copyright schukai GmbH + * @memberOf Monster.Data + */ +class Transformer extends Base { + /** + * + * @param {string} definition + */ + constructor(definition) { + super(); + this.args = disassemble(definition); + this.command = this.args.shift() + this.callbacks = new Map(); + + } + + /** + * + * @param {string} name + * @param {function} callback + * @param {object} context + * @returns {Transformer} + * @throws {TypeError} value is not a string + * @throws {TypeError} value is not a function + */ + setCallback(name, callback, context) { + validateString(name) + validateFunction(callback) + + if (context !== undefined) { + validateObject(context); + } + + this.callbacks.set(name, { + callback: callback, + context: context, + }); + + return this; + } + + /** + * + * @param {*} value + * @returns {*} + * @throws {Error} unknown command + * @throws {TypeError} unsupported type + * @throws {Error} type not supported + */ + run(value) { + return transform.apply(this, [value]) + } +} + +/** + * + * @param {string} command + * @returns {array} + * @private + */ +function disassemble(command) { + + validateString(command); + + let placeholder = new Map; + const regex = /((?<pattern>\\(?<char>.)){1})/mig; + + // The separator for args must be escaped + // undefined string which should not occur normally and is also not a regex + let result = command.matchAll(regex) + + for (let m of result) { + let g = m?.['groups']; + if (!isObject(g)) { + continue; + } + + let p = g?.['pattern']; + let c = g?.['char']; + + if (p && c) { + let r = '__' + new ID().toString() + '__'; + placeholder.set(r, c); + command = command.replace(p, r); + } + + } + let parts = command.split(':'); + + parts = parts.map(function (value) { + let v = value.trim(); + for (let k of placeholder) { + v = v.replace(k[0], k[1]); + } + return v; + + + }); + + return parts +} + +/** + * tries to make a string out of value and if this succeeds to return it back + * + * @param {*} value + * @returns {string} + * @private + */ +function convertToString(value) { + + if (isObject(value) && value.hasOwnProperty('toString')) { + value = value.toString(); + } + + validateString(value) + return value; +} + +/** + * + * @param {*} value + * @returns {*} + * @private + * @throws {Error} unknown command + * @throws {TypeError} unsupported type + * @throws {Error} type not supported + * @throws {Error} missing key parameter + */ +function transform(value) { + + const console = getGlobalObject('console'); + + let args = clone(this.args); + let key, defaultValue; + + switch (this.command) { + + case 'static': + return this.args.join(':'); + + case 'tolower': + case 'strtolower': + case 'tolowercase': + validateString(value) + return value.toLowerCase(); + + case 'toupper': + case 'strtoupper': + case 'touppercase': + validateString(value) + return value.toUpperCase(); + + case 'tostring': + return "" + value; + + case 'tointeger': + let n = parseInt(value); + validateInteger(n); + return n + + case 'tojson': + return JSON.stringify(value); + + case 'fromjson': + return JSON.parse(value); + + case 'trim': + validateString(value) + return value.trim(); + + case 'rawurlencode': + validateString(value) + return encodeURIComponent(value) + .replace(/!/g, '%21') + .replace(/'/g, '%27') + .replace(/\(/g, '%28') + .replace(/\)/g, '%29') + .replace(/\*/g, '%2A'); + + + case 'call': + + /** + * callback-definition + * function callback(value, ...args) { + * return value; + * } + */ + + let callback; + let callbackName = args.shift(); + let context = getGlobal(); + + if (isObject(value) && value.hasOwnProperty(callbackName)) { + callback = value[callbackName]; + } else if (this.callbacks.has(callbackName)) { + let s = this.callbacks.get(callbackName); + callback = s?.['callback']; + context = s?.['context']; + } else if (typeof window === 'object' && window.hasOwnProperty(callbackName)) { + callback = window[callbackName]; + } + validateFunction(callback); + + args.unshift(value); + return callback.call(context, ...args); + + case 'plain': + case 'plaintext': + validateString(value); + let doc = new DOMParser().parseFromString(value, 'text/html'); + return doc.body.textContent || ""; + + case 'if': + case '?': + + validatePrimitive(value); + + let trueStatement = (args.shift() || undefined); + let falseStatement = (args.shift() || undefined); + + if (trueStatement === 'value') { + trueStatement = value; + } + if (trueStatement === '\\value') { + trueStatement = 'value'; + } + if (falseStatement === 'value') { + falseStatement = value; + } + if (falseStatement === '\\value') { + falseStatement = 'value'; + } + + let condition = ((value !== undefined && value !== '' && value !== 'off' && value !== 'false' && value !== false) || value === 'on' || value === 'true' || value === true); + return condition ? trueStatement : falseStatement; + + + case 'ucfirst': + validateString(value); + + let firstchar = value.charAt(0).toUpperCase(); + return firstchar + value.substr(1); + case 'ucwords': + validateString(value); + + return value.replace(/^([a-z\u00E0-\u00FC])|\s+([a-z\u00E0-\u00FC])/g, function (v) { + return v.toUpperCase(); + }); + + case 'count': + case 'length': + + if ((isString(value) || isObject(value) || isArray(value)) && value.hasOwnProperty('length')) { + return value.length; + } + + throw new TypeError("unsupported type " + typeof value); + + case 'to-base64': + case 'btoa': + case 'base64': + return btoa(convertToString(value)); + + case 'atob': + case 'from-base64': + return atob(convertToString(value)); + + case 'empty': + return ''; + + case 'undefined': + return undefined; + + case 'debug': + + if (isObject(console)) { + console.log(value); + } + + return value; + + case 'prefix': + validateString(value); + let prefix = args?.[0]; + return prefix + value; + + case 'suffix': + validateString(value); + let suffix = args?.[0]; + return value + suffix; + + case 'uniqid': + return (new ID()).toString(); + + case 'first-key': + case 'last-key': + case 'nth-last-key': + case 'nth-key': + + if (!isObject(value)) { + throw new Error("type not supported") + } + + const keys = Object.keys(value).sort() + + if (this.command === 'first-key') { + key = 0; + } else if (this.command === 'last-key') { + key = keys.length - 1; + } else { + + key = validateInteger(parseInt(args.shift())); + + if (this.command === 'nth-last-key') { + key = keys.length - key - 1; + } + } + + defaultValue = (args.shift() || ''); + + let useKey = keys?.[key]; + + if (value?.[useKey]) { + return value?.[useKey]; + } + + return defaultValue; + + + case 'key': + case 'property': + case 'index': + + key = args.shift() || undefined; + + if (key === undefined) { + throw new Error("missing key parameter") + } + + defaultValue = (args.shift() || undefined); + + if (value instanceof Map) { + if (!value.has(key)) { + return defaultValue; + } + return value.get(key); + } + + if (isObject(value) || isArray(value)) { + + if (value?.[key]) { + return value?.[key]; + } + + return defaultValue; + } + + throw new Error("type not supported") + + case 'path-exists': + + key = args.shift(); + if (key === undefined) { + throw new Error("missing key parameter") + } + + return new Pathfinder(value).exists(key); + + case 'path': + + key = args.shift(); + if (key === undefined) { + throw new Error("missing key parameter") + } + + let pf = new Pathfinder(value); + + if (!pf.exists(key)) { + return undefined; + } + + return pf.getVia(key); + + + case 'substring': + + validateString(value); + + let start = parseInt(args[0]) || 0; + let end = (parseInt(args[1]) || 0) + start; + + return value.substring(start, end); + + case 'nop': + return value; + + case '??': + case 'default': + if (value !== undefined && value !== null) { + return value; + } + + defaultValue = args.shift(); + let defaultType = args.shift(); + if (defaultType === undefined) { + defaultType = 'string'; + } + + switch (defaultType) { + case 'int': + case 'integer': + return parseInt(defaultValue); + case 'float': + return parseFloat(defaultValue); + case 'undefined': + return undefined + case 'bool': + case 'boolean': + defaultValue = defaultValue.toLowerCase() + return ((defaultValue !== 'undefined' && defaultValue !== '' && defaultValue !== 'off' && defaultValue !== 'false' && defaultValue !== 'false') || defaultValue === 'on' || defaultValue === 'true' || defaultValue === 'true'); + case 'string': + return "" + defaultValue; + case "object": + return JSON.parse(atob(defaultValue)); + } + + throw new Error("type not supported") + + + default: + throw new Error("unknown command " + this.command) + } + + return value; +} + +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from '../types/base.mjs'; +import {isArray, isInteger, isObject, isPrimitive} from '../types/is.mjs'; +import {Stack} from "../types/stack.mjs"; +import {validateInteger, validateString} from '../types/validate.mjs'; + +export {Pathfinder, DELIMITER, WILDCARD} + +/** + * path separator + * + * @private + * @type {string} + */ +const DELIMITER = '.'; + +/** + * @private + * @type {string} + */ +const WILDCARD = '*'; + +/** + * Pathfinder is a class to find a path to an object. + * + * ``` + * <script type="module"> + * import {Pathfinder} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/pathfinder.mjs'; + * console.log(new Pathfinder()) + * </script> + * ``` + * + * With the help of the pathfinder, values can be read and written from an object construct. + * + * ``` + * new Pathfinder({ + * a: { + * b: { + * f: [ + * { + * g: false, + * } + * ], + * } + * } + * }).getVia("a.b.f.0.g"); // ↦ false + * ``` + * + * if a value is not present or has the wrong type, a corresponding exception is thrown. + * + * ``` + * new Pathfinder({}).getVia("a.b.f.0.g"); // ↦ Error + * ``` + * + * The `Pathfinder.exists()` method can be used to check whether access to the path is possible. + * + * ``` + * new Pathfinder({}).exists("a.b.f.0.g"); // ↦ false + * ``` + * + * pathfinder can also be used to build object structures. to do this, the `Pathfinder.setVia()` method must be used. + * + * ``` + * obj = {}; + * new Pathfinder(obj).setVia('a.b.0.c', true); // ↦ {a:{b:[{c:true}]}} + * ``` + * + * @example + * + * import {Pathfinder} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/pathfinder.mjs'; + * + * let value = new Pathfinder({ + * a: { + * b: { + * f: [ + * { + * g: false, + * } + * ], + * } + * } + * }).getVia("a.b.f.0.g"); + * + * console.log(value); + * // ↦ false + * + * try { + * new Pathfinder({}).getVia("a.b.f.0.g"); + * } catch(e) { + * console.log(e.toString()); + * // ↦ Error: the journey is not at its end (b.f.0.g) + * } + * + * @example + * + * import {Pathfinder} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/pathfinder.mjs'; + * + * let p = new Pathfinder({ + * a: { + * x: [ + * {c: 1}, {c: 2} + * ], + * y: true + * }, + * b: { + * x: [ + * {c: 1, d: false}, {c: 2} + * ], + * y: true + * }, + * }); + * + * let r = p.getVia("*.x.*.c"); + * console.log(r); + * + * @since 1.4.0 + * @copyright schukai GmbH + * @memberOf Monster.Data + */ +class Pathfinder extends Base { + + /** + * @param {array|object|Map|Set} value + * @since 1.4.0 + * @throws {Error} the parameter must not be a simple type + **/ + constructor(object) { + super(); + + if (isPrimitive(object)) { + throw new Error('the parameter must not be a simple type'); + } + + this.object = object; + this.wildCard = WILDCARD; + } + + /** + * set wildcard + * + * @param {string} wildcard + * @return {Pathfinder} + * @since 1.7.0 + */ + setWildCard(wildcard) { + validateString(wildcard); + this.wildCard = wildcard; + return this; + } + + /** + * + * @param {string} path + * @since 1.4.0 + * @returns {*} + * @throws {TypeError} unsupported type + * @throws {Error} the journey is not at its end + * @throws {TypeError} value is not a string + * @throws {TypeError} value is not an integer + * @throws {Error} unsupported action for this data type + */ + getVia(path) { + return getValueViaPath.call(this, this.object, validateString(path)); + } + + /** + * + * @param {string} path + * @param {*} value + * @returns {Pathfinder} + * @since 1.4.0 + * @throws {TypeError} unsupported type + * @throws {TypeError} value is not a string + * @throws {TypeError} value is not an integer + * @throws {Error} unsupported action for this data type + */ + setVia(path, value) { + validateString(path); + setValueViaPath.call(this, this.object, path, value); + return this; + } + + /** + * Delete Via Path + * + * @param {string} path + * @returns {Pathfinder} + * @since 1.6.0 + * @throws {TypeError} unsupported type + * @throws {TypeError} value is not a string + * @throws {TypeError} value is not an integer + * @throws {Error} unsupported action for this data type + */ + deleteVia(path) { + validateString(path); + deleteValueViaPath.call(this, this.object, path); + return this; + } + + /** + * + * @param {string} path + * @return {bool} + * @throws {TypeError} unsupported type + * @throws {TypeError} value is not a string + * @throws {TypeError} value is not an integer + * @since 1.4.0 + */ + exists(path) { + validateString(path); + try { + getValueViaPath.call(this, this.object, path, true); + return true; + } catch (e) { + + } + + return false; + } + +} + + +/** + * + * @param {*} subject + * @param {string} path + * @param {string} check + * @return {Map} + * @throws {TypeError} unsupported type + * @throws {Error} the journey is not at its end + * @throws {Error} unsupported action for this data type + * @private + */ +function iterate(subject, path, check) { + + const result = new Map; + + if (isObject(subject) || isArray(subject)) { + for (const [key, value] of Object.entries(subject)) { + result.set(key, getValueViaPath.call(this, value, path, check)) + } + } else { + let key = path.split(DELIMITER).shift(); + result.set(key, getValueViaPath.call(this, subject, path, check)); + } + + return result; + + +} + +/** + * + * @param {*} subject + * @param [string} path + * @param [boolean} check + * @returns {*} + * @throws {TypeError} unsupported type + * @throws {Error} the journey is not at its end + * @throws {Error} unsupported action for this data type + * @private + */ +function getValueViaPath(subject, path, check) { + + if (path === "") { + return subject; + } + + let parts = path.split(DELIMITER) + let current = parts.shift(); + + if (current === this.wildCard) { + return iterate.call(this, subject, parts.join(DELIMITER), check); + } + + if (isObject(subject) || isArray(subject)) { + + let anchor; + if (subject instanceof Map || subject instanceof WeakMap) { + anchor = subject.get(current); + + } else if (subject instanceof Set || subject instanceof WeakSet) { + current = parseInt(current); + validateInteger(current) + anchor = [...subject]?.[current]; + + } else if (typeof WeakRef === 'function' && subject instanceof WeakRef) { + throw Error('unsupported action for this data type'); + + } else if (isArray(subject)) { + current = parseInt(current); + validateInteger(current) + anchor = subject?.[current]; + } else { + anchor = subject?.[current]; + } + + if (isObject(anchor) || isArray(anchor)) { + return getValueViaPath.call(this, anchor, parts.join(DELIMITER), check) + } + + if (parts.length > 0) { + throw Error("the journey is not at its end (" + parts.join(DELIMITER) + ")"); + } + + + if (check === true) { + const descriptor = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(subject), current); + + if (!subject.hasOwnProperty(current) && descriptor === undefined) { + throw Error('unknown value'); + } + + } + + return anchor; + + } + + throw TypeError("unsupported type " + typeof subject) + +} + +/** + * + * @param object + * @param path + * @param value + * @returns {void} + * @throws {TypeError} unsupported type + * @throws {TypeError} unsupported type + * @throws {Error} the journey is not at its end + * @throws {Error} unsupported action for this data type + * @private + */ +function setValueViaPath(object, path, value) { + + validateString(path); + + let parts = path.split(DELIMITER) + let last = parts.pop(); + let subpath = parts.join(DELIMITER); + + let stack = new Stack() + let current = subpath; + while (true) { + + try { + getValueViaPath.call(this, object, current, true) + break; + } catch (e) { + + } + + stack.push(current); + parts.pop(); + current = parts.join(DELIMITER); + + if (current === "") break; + } + + while (!stack.isEmpty()) { + current = stack.pop(); + let obj = {}; + + if (!stack.isEmpty()) { + let n = stack.peek().split(DELIMITER).pop(); + if (isInteger(parseInt(n))) { + obj = []; + } + + } + + setValueViaPath.call(this, object, current, obj); + } + + let anchor = getValueViaPath.call(this, object, subpath); + + if (!isObject(object) && !isArray(object)) { + throw TypeError("unsupported type: " + typeof object); + } + + if (anchor instanceof Map || anchor instanceof WeakMap) { + anchor.set(last, value); + } else if (anchor instanceof Set || anchor instanceof WeakSet) { + anchor.append(value) + + } else if (typeof WeakRef === 'function' && anchor instanceof WeakRef) { + throw Error('unsupported action for this data type'); + + } else if (isArray(anchor)) { + last = parseInt(last); + validateInteger(last) + assignProperty(anchor, last, value); + } else { + assignProperty(anchor, last, value); + } + + +} + +/** + * @private + * @param {object} object + * @param {string} key + * @param {*} value + */ +function assignProperty(object, key, value) { + + if (!object.hasOwnProperty(key)) { + object[key] = value; + return; + } + + if (value === undefined) { + delete object[key]; + } + + object[key] = value; + +} + +/** + * + * @param object + * @param path + * @returns {void} + * @throws {TypeError} unsupported type + * @throws {TypeError} unsupported type + * @throws {Error} the journey is not at its end + * @throws {Error} unsupported action for this data type + * @since 1.6.0 + * @private + */ +function deleteValueViaPath(object, path) { + + const parts = path.split(DELIMITER) + let last = parts.pop(); + const subpath = parts.join(DELIMITER); + + const anchor = getValueViaPath.call(this, object, subpath); + + if (anchor instanceof Map) { + anchor.delete(last); + } else if (anchor instanceof Set || anchor instanceof WeakMap || anchor instanceof WeakSet || (typeof WeakRef === 'function' && anchor instanceof WeakRef)) { + throw Error('unsupported action for this data type'); + + } else if (isArray(anchor)) { + last = parseInt(last); + validateInteger(last) + delete anchor[last]; + } else { + delete anchor[last]; + } + + +} +'use strict'; + +/** + * @author schukai GmbH + */ + + +import {isArray, isObject} from "../types/is.mjs"; +import {typeOf} from "../types/typeof.mjs"; + +export {diff} + +/** + * With the diff function you can perform the change of one object to another. The result shows the changes of the second object to the first object. + * + * The operator `add` means that something has been added to the second object. `delete` means that something has been deleted from the second object compared to the first object. + * + * ``` + * <script type="module"> + * import {Diff} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/diff.mjs'; + * Diff(a, b) + * </script> + * ``` + * + * @example + * + * import {Diff} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/diff.mjs'; + * + * // given are two objects x and y. + * + * let x = { + * a: 1, + * b: "Hello!" + * } + * + * let y = { + * a: 2, + * c: true + * } + * + * // These two objects can be compared with each other. + * + * console.log(Diff(x, y)); + * + * // the result is then the following + * + * // + * // [ + * // { + * // operator: 'update', + * // path: [ 'a' ], + * // first: { value: 1, type: 'number' }, + * // second: { value: 2, type: 'number' } + * // }, + * // { + * // operator: 'delete', + * // path: [ 'b' ], + * // first: { value: 'Hello!', type: 'string' } + * // }, + * // { + * // operator: 'add', + * // path: [ 'c' ], + * // second: { value: true, type: 'boolean' } + * // } + * // ] + * + * @param {*} first + * @param {*} second + * @return {array} + * @since 1.6.0 + * @copyright schukai GmbH + * @memberOf Monster.Data + */ +function diff(first, second) { + return doDiff(first, second) +} + +/** + * @private + * @param a + * @param b + * @param type + * @return {Set<string>|Set<number>} + */ +function getKeys(a, b, type) { + if (isArray(type)) { + const keys = a.length > b.length ? new Array(a.length) : new Array(b.length); + keys.fill(0); + return new Set(keys.map((_, i) => i)); + } + + return new Set(Object.keys(a).concat(Object.keys(b))); +} + +/** + * @private + * @param a + * @param b + * @param path + * @param diff + * @return {array} + */ +function doDiff(a, b, path, diff) { + + let typeA = typeOf(a) + let typeB = typeOf(b) + + const currPath = path || []; + const currDiff = diff || []; + + if (typeA === typeB && (typeA === 'object' || typeA ==='array')) { + + getKeys(a, b, typeA).forEach((v) => { + + if (!(Object.prototype.hasOwnProperty.call(a, v))) { + currDiff.push(buildResult(a[v], b[v], 'add', currPath.concat(v))); + } else if (!(Object.prototype.hasOwnProperty.call(b, v))) { + currDiff.push(buildResult(a[v], b[v], 'delete', currPath.concat(v))); + } else { + doDiff(a[v], b[v], currPath.concat(v), currDiff); + } + }); + + } else { + + const o = getOperator(a, b, typeA, typeB); + if (o !== undefined) { + currDiff.push(buildResult(a, b, o, path)); + } + + } + + return currDiff; + +} + +/** + * + * @param {*} a + * @param {*} b + * @param {string} operator + * @param {array} path + * @return {{path: array, operator: string}} + * @private + */ +function buildResult(a, b, operator, path) { + + const result = { + operator, + path, + }; + + if (operator !== 'add') { + result.first = { + value: a, + type: typeof a + }; + + if (isObject(a)) { + const name = Object.getPrototypeOf(a)?.constructor?.name; + if (name !== undefined) { + result.first.instance = name; + } + } + } + + if (operator === 'add' || operator === 'update') { + result.second = { + value: b, + type: typeof b + }; + + if (isObject(b)) { + const name = Object.getPrototypeOf(b)?.constructor?.name; + if (name !== undefined) { + result.second.instance = name; + } + } + + } + + return result; +} + +/** + * @private + * @param {*} a + * @param {*} b + * @return {boolean} + */ +function isNotEqual(a, b) { + + if (typeof a !== typeof b) { + return true; + } + + if (a instanceof Date && b instanceof Date) { + return a.getTime() !== b.getTime(); + } + + return a !== b; +} + +/** + * @private + * @param {*} a + * @param {*} b + * @return {string|undefined} + */ +function getOperator(a, b) { + + /** + * @type {string|undefined} + */ + let operator; + + /** + * @type {string} + */ + let typeA = typeof a; + + /** + * @type {string} + */ + let typeB = typeof b; + + if (typeA === 'undefined' && typeB !== 'undefined') { + operator = 'add'; + } else if (typeA !== 'undefined' && typeB === 'undefined') { + operator = 'delete'; + } else if (isNotEqual(a, b)) { + operator = 'update'; + } + + return operator; + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../../constants.mjs"; +import {isObject} from "../../types/is.mjs"; +import {Datasource} from "../datasource.mjs"; +import {Pathfinder} from "../pathfinder.mjs"; +import {Pipe} from "../pipe.mjs"; +import {WriteError} from "./restapi/writeerror.mjs"; + +export {RestAPI} + +/** + * The RestAPI is a class that enables a REST API server. + * + * ``` + * <script type="module"> + * import {RestAPI} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/datasource/restapi.mjs'; + * new RestAPI() + * </script> + * ``` + * + * @example + * + * import {RestAPI} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/datasource/restapi.mjs'; + * + * const ds = new RestAPI({ + * url: 'https://httpbin.org/get' + * },{ + * url: 'https://httpbin.org/post' + * }); + * + * ds.set({flag:true}) + * ds.write().then(()=>console.log('done')); + * ds.read().then(()=>console.log('done')); + * + * @since 1.22.0 + * @copyright schukai GmbH + * @memberOf Monster.Data.Datasource + * @summary The LocalStorage class encapsulates the access to data objects. + */ +class RestAPI extends Datasource { + + /** + * + * @param {Object} [readDefinition] An options object containing any custom settings that you want to apply to the read request. + * @param {Object} [writeDefinition] An options object containing any custom settings that you want to apply to the write request. + * @throws {TypeError} value is not a string + */ + constructor(readDefinition, writeDefinition) { + super(); + + const options = {} + + if (isObject(readDefinition)) options.read = readDefinition; + if (isObject(writeDefinition)) options.write = writeDefinition; + + this.setOptions(options); + + } + + /** + * @property {string} url=undefined Defines the resource that you wish to fetch. + * @property {Object} write={} Options + * @property {Object} write.init={} An options object containing any custom settings that you want to apply to the request. The parameters are identical to those of the {@link https://developer.mozilla.org/en-US/docs/Web/API/Request/Request|Request constructor} + * @property {string} write.init.method=POST + * @property {string} write.acceptedStatus=[200,201] + * @property {string} write.url URL + * @property {Object} write.mapping the mapping is applied before writing. + * @property {String} write.mapping.transformer Transformer to select the appropriate entries + * @property {Object} write.report + * @property {String} write.report.path Path to validations + * @property {Monster.Data.Datasource~exampleCallback[]} write.mapping.callback with the help of the callback, the structures can be adjusted before writing. + * @property {Object} read.init={} An options object containing any custom settings that you want to apply to the request. The parameters are identical to those of the {@link https://developer.mozilla.org/en-US/docs/Web/API/Request/Request|Request constructor} + * @property {string} read.init.method=GET + * @property {string} read.acceptedStatus=[200] + * @property {string} read.url URL + * @property {Object} read.mapping the mapping is applied after reading. + * @property {String} read.mapping.transformer Transformer to select the appropriate entries + * @property {Monster.Data.Datasource~exampleCallback[]} read.mapping.callback with the help of the callback, the structures can be adjusted after reading. + */ + get defaults() { + return Object.assign({}, super.defaults, { + write: { + init: { + method: 'POST', + }, + acceptedStatus: [200, 201], + url: undefined, + mapping: { + transformer: undefined, + callbacks: [] + }, + report: { + path: undefined + } + }, + read: { + init: { + method: 'GET' + }, + acceptedStatus: [200], + url: undefined, + mapping: { + transformer: undefined, + callbacks: [] + }, + }, + + }); + } + + /** + * @return {Promise} + * @throws {Error} the options does not contain a valid json definition + * @throws {TypeError} value is not a object + * @throws {Error} the data cannot be read + */ + read() { + const self = this; + let response; + + let init = self.getOption('read.init'); + if (!isObject(init)) init = {}; + + return fetch(self.getOption('read.url'), init).then(resp => { + response = resp; + + const acceptedStatus = self.getOption('read.acceptedStatus', [200]); + + if (acceptedStatus.indexOf(resp.status) === -1) { + throw Error('the data cannot be read (response ' + resp.status + ')') + } + + return resp.text() + }).then(body => { + + let obj; + + try { + obj = JSON.parse(body); + + } catch (e) { + + if (body.length > 100) { + body = body.substring(0, 97) + '...'; + } + + throw new Error('the response does not contain a valid json (actual: ' + body + ').'); + } + + let transformation = self.getOption('read.mapping.transformer'); + if (transformation !== undefined) { + const pipe = new Pipe(transformation); + obj = pipe.run(obj); + } + + self.set(obj); + return response; + }) + } + + /** + * @return {Promise} + * @throws {WriteError} the data cannot be written + */ + write() { + const self = this; + + + let init = self.getOption('write.init'); + if (!isObject(init)) init = {}; + if (typeof init['headers'] !== 'object') { + init['headers'] = { + 'Content-Type': 'application/json' + } + } + + let obj = self.get(); + let transformation = self.getOption('write.mapping.transformer'); + if (transformation !== undefined) { + const pipe = new Pipe(transformation); + obj = pipe.run(obj); + } + + let sheathingObject = self.getOption('write.sheathing.object'); + let sheathingPath = self.getOption('write.sheathing.path'); + let reportPath = self.getOption('write.report.path'); + + if (sheathingObject && sheathingPath) { + const sub = obj; + obj = sheathingObject; + (new Pathfinder(obj)).setVia(sheathingPath, sub); + } + + init['body'] = JSON.stringify(obj); + + return fetch(self.getOption('write.url'), init).then(response => { + + const acceptedStatus = self.getOption('write.acceptedStatus', [200, 2001]); + + if (acceptedStatus.indexOf(response.status) === -1) { + + return response.text().then((body) => { + + let obj, validation; + try { + obj = JSON.parse(body); + validation = new Pathfinder(obj).getVia(reportPath) + + } catch (e) { + + if (body.length > 100) { + body = body.substring(0, 97) + '...'; + } + + throw new Error('the response does not contain a valid json (actual: ' + body + ').'); + } + + throw new WriteError('the data cannot be written (response ' + response.status + ')', response, validation) + + }) + + + } + + return response; + }); + } + + + /** + * @return {RestAPI} + */ + getClone() { + const self = this; + return new RestAPI(self[internalSymbol].getRealSubject()['options'].read, self[internalSymbol].getRealSubject()['options'].write); + } + +} + + +/** + * This callback can be passed to a datasource and is used to adapt data structures. + * + * @callback Monster.Data.Datasource~exampleCallback + * @param {*} value Value + * @param {string} key Key + * @memberOf Monster.Data + * @see Monster.Data.Datasource + */ +'use strict'; + +/** + * Namespace for datasources + * + * @namespace Monster.Data.Datasource + * @memberOf Monster.Data + * @author schukai GmbH + */ +const ns = {};'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../../../constants.mjs"; +import {getGlobalObject} from "../../../types/global.mjs"; +import {Datasource} from "../../datasource.mjs"; +import {Storage, storageObjectSymbol} from "../storage.mjs"; + +export {SessionStorage} + +/** + * The SessionStorage class provides a data source that uses the SessionStorage API on the client. + * + * ``` + * <script type="module"> + * import {SessionStorage} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/datasource/storage/sessionstorage.mjs'; + * new SessionStorage() + * </script> + * ``` + * + * @since 1.22.0 + * @copyright schukai GmbH + * @memberOf Monster.Data.Datasource.Storage + * @summary The LocalStorage class encapsulates the access to data objects. + */ +class SessionStorage extends Storage { + + /** + * @throws {Error} this method must be implemented by derived classes. + * @return {external:sessionStorage} + * @private + */ + [storageObjectSymbol]() { + return getGlobalObject('sessionStorage'); + } + + /** + * Create Clone + * + * @return {SessionStorage} + */ + getClone() { + const self = this; + return new SessionStorage(self[internalSymbol].getRealSubject()['options'].key); + } + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../../../constants.mjs"; +import {getGlobalObject} from "../../../types/global.mjs"; +import {Datasource} from "../../datasource.mjs"; +import {Storage, storageObjectSymbol} from "../storage.mjs"; + +export {LocalStorage} + +/** + * The LocalStorage Datasource provides a data store in the browser localStorage. + * + * ``` + * <script type="module"> + * import {LocalStorage} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/datasource/storage/localstorage.mjs'; + * new LocalStorage() + * </script> + * ``` + * + * @since 1.22.0 + * @copyright schukai GmbH + * @memberOf Monster.Data.Datasource.Storage + * @summary The LocalStorage class encapsulates the access to data objects. + */ +class LocalStorage extends Storage { + + /** + * @throws {Error} this method must be implemented by derived classes. + * @return {external:localStorage} + * @private + */ + [storageObjectSymbol]() { + return getGlobalObject('localStorage'); + } + + /** + * Create clone + * @return {LocalStorage} + */ + getClone() { + const self = this; + return new LocalStorage(self[internalSymbol].getRealSubject()['options'].key); + } + + +} +'use strict'; + +/** + * Namespace for storages + * + * @namespace Monster.Data.Datasource.Storage + * @memberOf Monster.Data.Datasource + * @author schukai GmbH + */ +const ns = {}; +'use strict'; + +/** + * Namespace for storages + * + * @namespace Monster.Data.Datasource.RestAPI + * @memberOf Monster.Data.Datasource + * @author schukai GmbH + */ +const ns = {}; +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../../../constants.mjs"; + +export {WriteError} + +/** + * Error message for API requests with extension of request and validation. + * + * @since 1.24.0 + * @copyright schukai GmbH + * @memberOf Monster.Data.Datasource.RestAPI + * @summary the error is thrown by the rest api in case of error + */ +class WriteError extends Error { + /** + * + * @param {string} message + * @param {Response} response + */ + constructor(message, response, validation) { + super(message); + this[internalSymbol] = { + response: response, + validation: validation + }; + } + + /** + * @return {Response} + */ + getResponse() { + return this[internalSymbol]['response'] + } + + /** + * @return {Object} + */ + getValidation() { + return this[internalSymbol]['validation'] + } +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../../constants.mjs"; +import {validateString} from "../../types/validate.mjs"; +import {Datasource} from "../datasource.mjs"; + +export {Storage, storageObjectSymbol} + +/** + * @private + * @type {symbol} + */ +const storageObjectSymbol = Symbol('storageObject'); + +/** + * The class represents a record. + * + * ``` + * <script type="module"> + * import {Storage} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/datasource/storage.mjs'; + * new Storage() + * </script> + * ``` + * + * @example + * + * import {Storage} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/datasource/storage.mjs'; + * + * new Datasource(); + * + * @since 1.22.0 + * @copyright schukai GmbH + * @memberOf Monster.Data.Datasource + * @summary The Storage class encapsulates the access to data objects over WebStorageAPI. + */ +class Storage extends Datasource { + + /** + * + * @param {string} key LocalStorage Key + * @throws {TypeError} value is not a string + */ + constructor(key) { + super(); + this.setOption('key', validateString(key)); + } + + /** + * @property {string} key=undefined LocalStorage Key + */ + get defaults() { + return Object.assign({}, super.defaults, { + key: undefined, + }); + } + + /** + * @throws {Error} this method must be implemented by derived classes. + * @return {external:Storage} + * @private + */ + [storageObjectSymbol]() { + throw new Error("this method must be implemented by derived classes") + } + + /** + * @return {Promise} + * @throws {Error} the options does not contain a valid json definition + * @throws {TypeError} value is not a object + * @throws {Error} the data cannot be read + */ + read() { + const self = this; + + const storage = self[storageObjectSymbol](); + + return new Promise(function (resolve) { + const data = JSON.parse(storage.getItem(self.getOption('key'))); + self.set(data??{}); + resolve(); + }) + + } + + /** + * @return {Storage} + * @throws {Error} the data cannot be written + */ + write() { + const self = this; + + const storage = self[storageObjectSymbol](); + + return new Promise(function (resolve) { + + const data = self.get(); + if (data === undefined) { + storage.removeItem(self.getOption('key')); + } else { + storage.setItem(self.getOption('key'), JSON.stringify(data)); + } + + resolve(); + }) + } + + /** + * @return {Storage} + */ + getClone() { + const self=this; + return new Storage(self[internalSymbol].getRealSubject()['options'].key); + } + +} +'use strict'; + +/** + * @author schukai GmbH + */ + + +import {getGlobal} from '../types/global.mjs'; + +export {random} + +/** + * this function uses crypt and returns a random number. + * + * ``` + * <script type="module"> + * import {random} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/math/random.mjs'; + * random(1,10) + * // ↦ 5 + * </script> + * ``` + * + * @param {number} min starting value of the definition set (default is 0) + * @param {number} max end value of the definition set (default is 1000000000) + * @returns {number} + * @memberOf Monster.Math + * @throws {Error} missing crypt + * @throws {Error} we cannot generate numbers larger than 53 bits. + * @throws {Error} the distance is too small to create a random number. + + * @since 1.0.0 + * @copyright schukai GmbH + */ + function random(min, max) { + + if (min === undefined) { + min = 0; + } + if (max === undefined) { + max = MAX; + } + + if (max < min) { + throw new Error("max must be greater than min"); + } + + return Math.round(create(min, max)); + +} + +/** + * @private + * @type {number} + */ +var MAX = 1000000000; + + +Math.log2 = Math.log2 || function (n) { + return Math.log(n) / Math.log(2); +}; + +/** + * + * @param {number} min + * @param {number} max + * @returns {number} + * @private + * @throws {Error} missing crypt + * @throws {Error} we cannot generate numbers larger than 53 bits. + * @throws {Error} the distance is too small to create a random number. + */ +function create(min, max) { + let crypt; + let globalReference = getGlobal(); + + crypt = globalReference?.['crypto'] || globalReference?.['msCrypto'] || globalReference?.['crypto'] || undefined; + + if (typeof crypt === "undefined") { + throw new Error("missing crypt") + } + + let rval = 0; + const range = max - min; + if (range < 2) { + throw new Error('the distance is too small to create a random number.') + } + + const bitsNeeded = Math.ceil(Math.log2(range)); + if (bitsNeeded > 53) { + throw new Error("we cannot generate numbers larger than 53 bits."); + } + const bytesNeeded = Math.ceil(bitsNeeded / 8); + const mask = Math.pow(2, bitsNeeded) - 1; + + const byteArray = new Uint8Array(bytesNeeded); + crypt.getRandomValues(byteArray); + + let p = (bytesNeeded - 1) * 8; + for (var i = 0; i < bytesNeeded; i++) { + rval += byteArray[i] * Math.pow(2, p); + p -= 8; + } + + rval = rval & mask; + + if (rval >= range) { + return create(min, max); + } + + if (rval < min) { + rval += min; + } + + return rval; + +} +'use strict'; + +/** + * Namespace for math. + * + * @namespace Monster.Math + * @memberOf Monster + * @author schukai GmbH + */ +const ns = {};'use strict'; + +/** + * @author schukai GmbH + */ + +import {isObject} from "../types/is.mjs"; +import {AbstractConstraint} from "./abstract.mjs"; + +export {IsObject} + +/** + * Constraints are used to define conditions that must be met by the value of a variable. + * + * The uniform API of the constraints allows chains to be formed. + * + * ``` + * <script type="module"> + * import {IsObject} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/isobject.mjs'; + * console.log(new IsObject()) + * </script> + * ``` + * + * @example + * + * import {IsObject} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/isobject.mjs'; + * + * new IsObject() + * .isValid({}) + * .then(()=>console.log(true)); + * // ↦ true + * + * + * new IsObject() + * .isValid(99) + * .catch(e=>console.log(e)); + * // ↦ 99 + * + * @since 1.3.0 + * @copyright schukai GmbH + * @memberOf Monster.Constraints + * @summary A constraint to check if a value is an object + */ +class IsObject extends AbstractConstraint { + + /** + * this method return a promise containing the result of the check. + * + * @param {*} value + * @returns {Promise} + */ + isValid(value) { + if (isObject(value)) { + return Promise.resolve(value); + } + + return Promise.reject(value); + } + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {AbstractConstraint} from "./abstract.mjs"; + +export {Invalid} + +/** + * Constraints are used to define conditions that must be met by the value of a variable. + * + * The uniform API of the constraints allows chains to be formed. + * + * The invalid constraint allows an always invalid query to be performed. this constraint is mainly intended for testing. + * + * ``` + * <script type="module"> + * import {Invalid} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/invalid.mjs'; + * new Invalid(); + * </script> + * ``` + * + * @example + * + * import {Invalid} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/invalid.mjs'; + * + * new Invalid().isValid() + * .then(()=>console.log(true)) + * .catch(()=>console.log(false)); + * // ↦ false + * + * @since 1.3.0 + * @copyright schukai GmbH + * @memberOf Monster.Constraints + * @summary A constraint that always invalid + */ +class Invalid extends AbstractConstraint { + + /** + * this method return a rejected promise + * + * @param {*} value + * @returns {Promise} + */ + isValid(value) { + return Promise.reject(value); + } + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {AbstractOperator} from "./abstractoperator.mjs"; + +export {AndOperator} + +/** + * Constraints are used to define conditions that must be met by the value of a variable. + * + * The uniform API of the constraints allows chains to be formed. + * + * The AndOperator is used to link several contraints. The constraint is fulfilled if all constraints of the operators are fulfilled. + * + * ``` + * <script type="module"> + * import {AndOperator} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/andoperator.mjs'; + * new AndOperator(); + * </script> + * ``` + * + * @example + * + * import {Valid} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/valid.mjs'; + * import {Invalid} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/invalid.mjs'; + * import {AndOperator} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/andoperator.mjs'; + * + * new AndOperator( + * new Valid(), new Valid()).isValid() + * .then(()=>console.log(true)) + * .catch(()=>console.log(false)); + * // ↦ true + * + * new AndOperator( + * new Invalid(), new Valid()).isValid() + * .then(()=>console.log(true)) + * .catch(()=>console.log(false)); + * // ↦ false + * + * @since 1.3.0 + * @copyright schukai GmbH + * @memberOf Monster.Constraints + * @summary A and operator constraint + */ +class AndOperator extends AbstractOperator { + + /** + * this method return a promise containing the result of the check. + * + * @param {*} value + * @returns {Promise} + */ + isValid(value) { + return Promise.all([this.operantA.isValid(value), this.operantB.isValid(value)]); + } + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {AbstractOperator} from "./abstractoperator.mjs"; + +export {OrOperator} + +/** + * Constraints are used to define conditions that must be met by the value of a variable. + * + * The uniform API of the constraints allows chains to be formed. + * + * The OrOperator is used to link several constraints. The constraint is fulfilled if one of the constraints is fulfilled. + * + * ``` + * <script type="module"> + * import {OrOperator} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraint/oroperator.mjs'; + * new OrOperator(); + * </script> + * ``` + * + * @example + * + * import {Valid} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/valid.mjs'; + * import {Invalid} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/invalid.mjs'; + * import {OrOperator} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/oroperator.mjs'; + * + * new OrOperator( + * new Valid(), new Invalid()).isValid() + * .then(()=>console.log(true)) + * .catch(()=>console.log(false)); + * // ↦ true + * + * new OrOperator( + * new Invalid(), new Invalid()).isValid() + * .then(()=>console.log(true)) + * .catch(()=>console.log(false)); + * // ↦ false + * + * @since 1.3.0 + * @copyright schukai GmbH + * @memberOf Monster.Constraints + * @summary A or operator + */ +class OrOperator extends AbstractOperator { + + /** + * this method return a promise containing the result of the check. + * + * @param {*} value + * @returns {Promise} + */ + isValid(value) { + var self = this; + + return new Promise(function (resolve, reject) { + let a, b; + + self.operantA.isValid(value) + .then(function () { + resolve(); + }).catch(function () { + a = false; + /** b has already been evaluated and was not true */ + if (b === false) { + reject(); + } + }); + + self.operantB.isValid(value) + .then(function () { + resolve(); + }).catch(function () { + b = false; + /** b has already been evaluated and was not true */ + if (a === false) { + reject(); + } + }); + }); + } + + +} +'use strict'; + +/** + * @author schukai GmbH + */ + + +import {Base} from '../types/base.mjs'; + +export {AbstractConstraint} + +/** + * Constraints are used to define conditions that must be met by the value of a variable. + * + * The uniform API of the constraints allows chains to be formed. + * + * The abstract constraint defines the api for all constraints. mainly the method isValid() is defined. + * + * derived classes must implement the method isValid(). + * + * @since 1.3.0 + * @copyright schukai GmbH + * @memberOf Monster.Constraints + * @summary The abstract constraint + */ +class AbstractConstraint extends Base { + + /** + * + */ + constructor() { + super(); + } + + /** + * this method must return a promise containing the result of the check. + * + * @param {*} value + * @returns {Promise} + */ + isValid(value) { + return Promise.reject(value); + } +} +'use strict'; + +/** + * Constraints are used to define conditions that must be met by the value of a variable so that the value can be transferred to the system. + * + * @namespace Monster.Constraints + * @memberOf Monster + * @author schukai GmbH + */ +const ns = {};'use strict'; + +/** + * @author schukai GmbH + */ + +import {isArray} from "../types/is.mjs"; +import {AbstractConstraint} from "./abstract.mjs"; + +export {IsArray} + +/** + * Constraints are used to define conditions that must be met by the value of a variable. + * + * The uniform API of the constraints allows chains to be formed. + * + * ``` + * <script type="module"> + * import {IsArray} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/isarray.mjs'; + * console.log(new IsArray()) + * </script> + * ``` + * + * @example + * + * import {IsArray} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/isarray.mjs'; + * + * new IsArray() + * .isValid([]) + * .then(()=>console.log(true)); + * // ↦ true + * + * new IsArray() + * .isValid(99) + * .catch(e=>console.log(e)); + * // ↦ 99 + * + * @since 1.3.0 + * @copyright schukai GmbH + * @memberOf Monster.Constraints + * @summary A constraint to check if a value is an array + */ +class IsArray extends AbstractConstraint { + + /** + * this method return a promise containing the result of the check. + * + * @param {*} value + * @returns {Promise} + */ + isValid(value) { + if (isArray(value)) { + return Promise.resolve(value); + } + + return Promise.reject(value); + } + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {AbstractConstraint} from "./abstract.mjs"; + +export {AbstractOperator} + +/** + * Constraints are used to define conditions that must be met by the value of a variable. + * + * The uniform API of the constraints allows chains to be formed. + * + * Operators allow you to link constraints together. for example, you can check whether a value is an object or an array. each operator has two operands that are linked together. + * + * @since 1.3.0 + * @copyright schukai GmbH + * @memberOf Monster.Constraints + * @summary The abstract operator constraint + */ +class AbstractOperator extends AbstractConstraint { + + /** + * + * @param {AbstractConstraint} operantA + * @param {AbstractConstraint} operantB + * @throws {TypeError} "parameters must be from type AbstractConstraint" + */ + constructor(operantA, operantB) { + super(); + + if (!(operantA instanceof AbstractConstraint) || !(operantB instanceof AbstractConstraint)) { + throw new TypeError("parameters must be from type AbstractConstraint") + } + + this.operantA = operantA; + this.operantB = operantB; + + } + + +} + +'use strict'; + +/** + * @author schukai GmbH + */ + +import {AbstractConstraint} from "./abstract.mjs"; + +export {Valid} + +/** + * Constraints are used to define conditions that must be met by the value of a variable. + * + * The uniform API of the constraints allows chains to be formed. + * + * The valid constraint allows an always valid query to be performed. this constraint is mainly intended for testing. + * + * ``` + * <script type="module"> + * import {Valid} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/valid.mjs'; + * new Valid(); + * </script> + * ``` + * + * @example + * + * import {Valid} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/valid.mjs'; + * + * new Valid().isValid() + * .then(()=>console.log(true)) + * .catch(()=>console.log(false)); + * // ↦ true + * + * @since 1.3.0 + * @copyright schukai GmbH + * @memberOf Monster.Constraints + * @summary A constraint that always valid + */ +class Valid extends AbstractConstraint { + + /** + * this method return a promise containing the result of the check. + * + * @param {*} value + * @returns {Promise} + */ + isValid(value) { + return Promise.resolve(value); + } + +} +/** + * @license + * Copyright 2021 schukai GmbH + * SPDX-License-Identifier: AGPL-3.0-only or COMMERCIAL + * @author schukai GmbH + */ + + +/** + * Main namespace for Monster. + * + * @namespace Monster + * @author schukai GmbH + */ +'use strict'; + +import './constants.mjs'; +// find packages/monster/source/ -type f -name "*.mjs" -not -name "*namespace*" -not -iname "monster.mjs" +import './constraints/isobject.mjs'; +import './constraints/valid.mjs'; +import './constraints/invalid.mjs'; +import './constraints/abstractoperator.mjs'; +import './constraints/oroperator.mjs'; +import './constraints/andoperator.mjs'; +import './constraints/abstract.mjs'; +import './constraints/isarray.mjs'; +import './logging/logger.mjs'; +import './logging/handler.mjs'; +import './logging/logentry.mjs'; +import './logging/handler/console.mjs'; +import './text/formatter.mjs'; +import './dom/resource/script.mjs'; +import './dom/resource/data.mjs'; +import './dom/resource/link/stylesheet.mjs'; +import './dom/resource/link.mjs'; +import './dom/resource.mjs'; +import './dom/updater.mjs'; +import './dom/attributes.mjs'; +import './dom/template.mjs'; +import './dom/util.mjs'; +import './dom/ready.mjs'; +import './dom/resourcemanager.mjs'; +import './dom/locale.mjs'; +import './dom/customcontrol.mjs'; +import './dom/constants.mjs'; +import './dom/assembler.mjs'; +import './dom/theme.mjs'; +import './dom/worker/factory.mjs'; +import './dom/focusmanager.mjs'; +import './dom/events.mjs'; +import './dom/customelement.mjs'; +import './i18n/formatter.mjs'; +import './i18n/providers/fetch.mjs'; +import './i18n/translations.mjs'; +import './i18n/locale.mjs'; +import './i18n/provider.mjs'; +import './types/queue.mjs'; +import './types/binary.mjs'; +import './types/regex.mjs'; +import './types/observer.mjs'; +import './types/observerlist.mjs'; +import './types/basewithoptions.mjs'; +import './types/is.mjs'; +import './types/proxyobserver.mjs'; +import './types/uniquequeue.mjs'; +import './types/node.mjs'; +import './types/tokenlist.mjs'; +import './types/typeof.mjs'; +import './types/uuid.mjs'; +import './types/mediatype.mjs'; +import './types/dataurl.mjs'; +import './types/base.mjs'; +import './types/version.mjs'; +import './types/nodelist.mjs'; +import './types/id.mjs'; +import './types/randomid.mjs'; +import './types/noderecursiveiterator.mjs'; +import './types/validate.mjs'; +import './types/stack.mjs'; +import './util/deadmansswitch.mjs'; +import './util/comparator.mjs'; +import './util/trimspaces.mjs'; +import './util/clone.mjs'; +import './util/freeze.mjs'; +import './util/processing.mjs'; +import './constants.mjs'; +import './data/pathfinder.mjs'; +import './data/pipe.mjs'; +import './data/extend.mjs'; +import './data/diff.mjs'; +import './data/buildmap.mjs'; +import './data/datasource.mjs'; +import './data/buildtree.mjs'; +import './data/transformer.mjs'; +import './data/datasource/storage.mjs'; +import './data/datasource/restapi.mjs'; +import './data/datasource/storage/sessionstorage.mjs'; +import './data/datasource/storage/localstorage.mjs'; +import './data/datasource/restapi/writeerror.mjs'; +import './math/random.mjs'; + +export {Monster} + +/** + * This class has no other purpose than to exist. + * + * @since 2.0.0 + * @copyright schukai GmbH + * @memberOf Monster + */ +class Monster { + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Handler} from '../logging/handler.mjs'; +import {LogEntry} from '../logging/logentry.mjs'; + +import {Base} from '../types/base.mjs'; +import {validateInteger, validateObject, validateString} from '../types/validate.mjs'; + +export {Logger, ALL, TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF}; + +/** + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF + * @type {number} + * @memberOf Monster.Logging + */ +const ALL = 255; +/** + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF + * @type {number} + * @memberOf Monster.Logging + */ +const TRACE = 64; +/** + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF + * @type {number} + * @memberOf Monster.Logging + */ +const DEBUG = 32; +/** + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF + * @type {number} + * @memberOf Monster.Logging + */ +const INFO = 16; +/** + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF + * @type {number} + * @memberOf Monster.Logging + */ +const WARN = 8; +/** + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF + * @type {number} + * @memberOf Monster.Logging + */ +const ERROR = 4; +/** + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF + * @type {number} + * @memberOf Monster.Logging + */ +const FATAL = 2; +/** + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF + * @type {number} + * @memberOf Monster.Logging + */ +const OFF = 0; + +/** + * The logger is a class that takes care of logging. + * + * ``` + * <script type="module"> + * import {ID} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/logging/logger.mjs'; + * new Logger() + * </script> + * ``` + * + * @since 1.5.0 + * @copyright schukai GmbH + * @memberOf Monster.Logging + */ +class Logger extends Base { + + /** + * + */ + constructor() { + super(); + this.handler = new Set; + } + + /** + * + * @param {Handler} handler + * @returns {Logger} + * @throws {Error} the handler must be an instance of Handler + */ + addHandler(handler) { + validateObject(handler) + if (!(handler instanceof Handler)) { + throw new Error("the handler must be an instance of Handler") + } + + this.handler.add(handler) + return this; + } + + /** + * + * @param {Handler} handler + * @returns {Logger} + * @throws {Error} the handler must be an instance of Handler + */ + removeHandler(handler) { + validateObject(handler) + if (!(handler instanceof Handler)) { + throw new Error("the handler must be an instance of Handler") + } + + this.handler.delete(handler); + return this; + } + + /** + * log Trace message + * + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF (ALL = 0xff;OFF = 0x00; + * + * @param {*} arguments + * @returns {Logger} + * @since 1.5.0 + */ + logTrace() { + triggerLog.apply(this, [TRACE, ...arguments]); + return this; + }; + + /** + * log Debug message + * + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF (ALL = 0xff;OFF = 0x00; + * + * @param {*} arguments + * @returns {Logger} + * @since 1.5.0 + */ + logDebug() { + triggerLog.apply(this, [DEBUG, ...arguments]); + return this; + }; + + /** + * log Info message + * + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF (ALL = 0xff;OFF = 0x00; + * + * + * @param {*} arguments + * @returns {Logger} + * @since 1.5.0 + */ + logInfo() { + triggerLog.apply(this, [INFO, ...arguments]); + return this; + }; + + /** + * log Warn message + * + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF (ALL = 0xff;OFF = 0x00; + * + * @param {*} arguments + * @returns {Logger} + * @since 1.5.0 + */ + logWarn() { + triggerLog.apply(this, [WARN, ...arguments]); + return this; + }; + + /** + * log Error message + * + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF (ALL = 0xff;OFF = 0x00; + * + * @param {*} arguments + * @returns {Logger} + * @since 1.5.0 + */ + logError() { + triggerLog.apply(this, [ERROR, ...arguments]); + return this; + }; + + /** + * log Fatal message + * + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF (ALL = 0xff;OFF = 0x00; + * + * @param {*} arguments + * @returns {Logger} + * @since 1.5.0 + */ + logFatal() { + triggerLog.apply(this, [FATAL, ...arguments]); + return this; + }; + + + /** + * Labels + * + * @param {integer} level + * @returns {string} + */ + getLabel(level) { + validateInteger(level); + + if (level === ALL) return 'ALL'; + if (level === TRACE) return 'TRACE'; + if (level === DEBUG) return 'DEBUG'; + if (level === INFO) return 'INFO'; + if (level === WARN) return 'WARN'; + if (level === ERROR) return 'ERROR'; + if (level === FATAL) return 'FATAL'; + if (level === OFF) return 'OFF'; + + return 'unknown'; + }; + + /** + * Level + * + * @param {string} label + * @returns {integer} + */ + getLevel(label) { + validateString(label); + + if (label === 'ALL') return ALL; + if (label === 'TRACE') return TRACE; + if (label === 'DEBUG') return DEBUG; + if (label === 'INFO') return INFO; + if (label === 'WARN') return WARN; + if (label === 'ERROR') return ERROR; + if (label === 'FATAL') return FATAL; + if (label === 'OFF') return OFF; + + return 0; + }; + + +} + + +/** + * Log triggern + * + * @param {integer} loglevel + * @param {*} args + * @returns {Logger} + * @private + */ +function triggerLog(loglevel, ...args) { + var logger = this; + + for (let handler of logger.handler) { + handler.log(new LogEntry(loglevel, args)) + } + + return logger; + +} +'use strict'; + +/** + * @author schukai GmbH + */ + + +import {Base} from '../types/base.mjs'; +import {validateInteger} from '../types/validate.mjs'; + +export {LogEntry} + +/** + * A log entry for the logger + * + * ``` + * <script type="module"> + * import {ID} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/logging/logentry.mjs'; + * console.log(new LogEntry()) + * </script> + * ``` + * + * @since 1.5.0 + * @copyright schukai GmbH + * @memberOf Monster.Logging + */ + class LogEntry extends Base { + /** + * + * @param {Integer} loglevel + * @param {...*} args + */ + constructor(loglevel, ...args) { + super(); + validateInteger(loglevel); + + this.loglevel = loglevel + this.arguments = args + } + + /** + * + * @returns {integerr} + */ + getLogLevel() { + return this.loglevel + } + + /** + * + * @returns {array} + */ + getArguments() { + return this.arguments + } + +} +'use strict'; + +/** + * Namespace for logging. + * + * @namespace Monster.Logging + * @memberOf Monster + * @author schukai GmbH + */ +const ns = {};'use strict'; + +/** + * @namespace Monster.Logging.Handler + * @memberOf Monster.Logging + * @author schukai GmbH + */ +const ns = {}; +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from '../../types/base.mjs'; +import {getGlobalObject} from "../../types/global.mjs"; +import {Handler} from '../handler.mjs'; +import {LogEntry} from "../logentry.mjs"; + +export {ConsoleHandler} + +/** + * You can create an object of the class simply by using the namespace `new Monster.Logging.Handler.ConsoleHandler()`. + * + * ``` + * <script type="module"> + * import {ConsoleHandler} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/logging/handler/console.mjs'; + * console.log(new ConsoleHandler()) + * </script> + * ``` + * + * @since 1.5.0 + * @copyright schukai GmbH + * @memberOf Monster.Logging.Handler + */ + class ConsoleHandler extends Handler { + constructor() { + super(); + } + + + /** + * This is the central log function. this method must be + * overwritten by derived handlers with their own logic. + * + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF (ALL = 0xff;OFF = 0x00; + * + * @param {LogEntry} entry + * @returns {boolean} + */ + log(entry) { + if (super.log(entry)) { + let console = getGlobalObject('console'); + if (!console) return false; + console.log(entry.toString()); + return true; + } + + return false; + } + +} + + +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from '../types/base.mjs'; +import {validateInstance, validateInteger} from "../types/validate.mjs"; +import {LogEntry} from "./logentry.mjs"; +import {ALL, DEBUG, ERROR, FATAL, INFO, OFF, TRACE, WARN} from "./logger.mjs"; + +export {Handler} + +/** + * The log handler is the interface between the log entries and the log listeners. + * + * ``` + * <script type="module"> + * import {ID} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/logging/handler.mjs'; + * console.log(new Handler()) + * </script> + * ``` + * + * @since 1.5.0 + * @copyright schukai GmbH + * @memberOf Monster.Logging + */ + class Handler extends Base { + constructor() { + super(); + + /** + * Loglevel + * + * @type {integer} + */ + this.loglevel = OFF; + } + + /** + * This is the central log function. this method must be + * overwritten by derived handlers with their own logic. + * + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF (ALL = 0xff;OFF = 0x00; + * + * @param {LogEntry} entry + * @returns {boolean} + */ + log(entry) { + validateInstance(entry, LogEntry); + + if (this.loglevel < entry.getLogLevel()) { + return false; + } + + return true; + } + + /** + * set loglevel + * + * @param {integer} loglevel + * @returns {Handler} + * @since 1.5.0 + */ + setLogLevel(loglevel) { + validateInteger(loglevel) + this.loglevel = loglevel; + return this; + } + + /** + * get loglevel + * + * @returns {integer} + * @since 1.5.0 + */ + getLogLevel() { + return this.loglevel; + } + + /** + * Set log level to All + * + * @returns {Handler} + * @since 1.5.0 + */ + setAll() { + this.setLogLevel(ALL); + return this; + }; + + /** + * Set log level to Trace + * + * @returns {Handler} + * @since 1.5.0 + */ + setTrace() { + this.setLogLevel(TRACE); + return this; + }; + + /** + * Set log level to Debug + * + * @returns {Handler} + * @since 1.5.0 + */ + setDebug() { + this.setLogLevel(DEBUG); + return this; + }; + + /** + * Set log level to Info + * + * @returns {Handler} + * @since 1.5.0 + */ + setInfo() { + this.setLogLevel(INFO); + return this; + }; + + /** + * Set log level to Warn + * + * @returns {undefined} + * @since 1.5.0 + */ + setWarn() { + this.setLogLevel(WARN); + return this; + }; + + /** + * Set log level to Error + * + * @returns {Handler} + * @since 1.5.0 + */ + setError() { + this.setLogLevel(ERROR); + return this; + }; + + /** + * Set log level to Fatal + * + * @returns {Handler} + * @since 1.5.0 + */ + setFatal() { + this.setLogLevel(FATAL); + return this; + }; + + + /** + * Set log level to Off + * + * @returns {Handler} + * @since 1.5.0 + */ + setOff() { + this.setLogLevel(OFF); + return this; + }; + + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../constants.mjs"; +import {extend} from "../data/extend.mjs"; +import {Pipe} from "../data/pipe.mjs"; + +import {BaseWithOptions} from "../types/basewithoptions.mjs"; +import {isObject, isString} from "../types/is.mjs"; +import {validateArray, validateString} from "../types/validate.mjs"; +import {getMonsterVersion} from "../types/version.mjs"; + +export {Formatter} + +/** + * @private + * @type {symbol} + */ +const internalObjectSymbol = Symbol('internalObject'); + +/** + * @private + * @type {symbol} + */ +const watchdogSymbol = Symbol('watchdog'); + +/** + * @private + * @type {symbol} + */ +const markerOpenIndexSymbol = Symbol('markerOpenIndex'); + +/** + * @private + * @type {symbol} + */ +const markerCloseIndexSymbol = Symbol('markercloseIndex'); + +/** + * @private + * @type {symbol} + */ +const workingDataSymbol = Symbol('workingData'); + + +/** + * Messages can be formatted with the formatter. To do this, an object with the values must be passed to the formatter. The message can then contain placeholders. + * + * Look at the example below. The placeholders use the logic of Pipe. + * + * ``` + * <script type="module"> + * import {Formatter} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/text/formatter.mjs'; + * new Formatter() + * </script> + * ``` + * + * ## Marker in marker + * + * Markers can be nested. Here, the inner marker is resolved first `${subkey} ↦ 1 = ${mykey2}` and then the outer marker `${mykey2}`. + * + * ``` + * const text = '${mykey${subkey}}'; + * let obj = { + * mykey2: "1", + * subkey: "2" + * }; + * + * new Formatter(obj).format(text); + * // ↦ 1 + * ``` + * + * ## Callbacks + * + * The values in a formatter can be adjusted via the commands of the `Transformer` or the`Pipe`. + * There is also the possibility to use callbacks. + * + * const formatter = new Formatter({x: '1'}, { + * callbacks: { + * quote: (value) => { + * return '"' + value + '"' + * } + * } + * }); + * + * formatter.format('${x | call:quote}')) + * // ↦ "1" + * + * ## Marker with parameter + * + * A string can also bring its own values. These must then be separated from the key by a separator `::`. + * The values themselves must be specified in key/value pairs. The key must be separated from the value by a separator `=`. + * + * When using a pipe, you must pay attention to the separators. + * + * @example + * + * import {Formatter} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/text/formatter.mjs'; + * + * new Formatter({ + * a: { + * b: { + * c: "Hello" + * }, + * d: "world", + * } + * }).format("${a.b.c} ${a.d | ucfirst}!"); // with pipe + * + * // ↦ Hello World! + * + * @since 1.12.0 + * @copyright schukai GmbH + * @memberOf Monster.Text + */ + class Formatter extends BaseWithOptions { + + /** + * Default values for the markers are `${` and `}` + * + * @param {object} object + * @throws {TypeError} value is not a object + */ + constructor(object, options) { + super(options); + this[internalObjectSymbol] = object || {} + this[markerOpenIndexSymbol] = 0; + this[markerCloseIndexSymbol] = 0; + } + + /** + * @property {object} marker + * @property {array} marker.open=["${"] + * @property {array} marker.close=["${"] + * @property {object} parameter + * @property {string} parameter.delimiter="::" + * @property {string} parameter.assignment="=" + * @property {object} callbacks={} + */ + get defaults() { + return extend({}, super.defaults, { + marker: { + open: ['${'], + close: ['}'] + }, + parameter: { + delimiter: '::', + assignment: '=' + }, + callbacks: {}, + }) + } + + + /** + * Set new Parameter Character + * + * Default values for the chars are `::` and `=` + * + * ``` + * formatter.setParameterChars('#'); + * formatter.setParameterChars('[',']'); + * formatter.setParameterChars('i18n{','}'); + * ``` + * + * @param {string} delimiter + * @param {string} assignment + * @return {Formatter} + * @since 1.24.0 + * @throws {TypeError} value is not a string + */ + setParameterChars(delimiter, assignment) { + + if (delimiter !== undefined) { + this[internalSymbol]['parameter']['delimiter'] = validateString(delimiter); + } + + if (assignment !== undefined) { + this[internalSymbol]['parameter']['assignment'] = validateString(assignment); + } + + return this; + } + + /** + * Set new Marker + * + * Default values for the markers are `${` and `}` + * + * ``` + * formatter.setMarker('#'); // open and close are both # + * formatter.setMarker('[',']'); + * formatter.setMarker('i18n{','}'); + * ``` + * + * @param {array|string} open + * @param {array|string|undefined} close + * @return {Formatter} + * @since 1.12.0 + * @throws {TypeError} value is not a string + */ + setMarker(open, close) { + + if (close === undefined) { + close = open; + } + + if (isString(open)) open = [open]; + if (isString(close)) close = [close]; + + this[internalSymbol]['marker']['open'] = validateArray(open); + this[internalSymbol]['marker']['close'] = validateArray(close); + return this; + } + + /** + * + * @param {string} text + * @return {string} + * @throws {TypeError} value is not a string + * @throws {Error} too deep nesting + */ + format(text) { + this[watchdogSymbol] = 0; + this[markerOpenIndexSymbol] = 0; + this[markerCloseIndexSymbol] = 0; + this[workingDataSymbol] = {}; + return format.call(this, text); + } + +} + +/** + * @private + * @return {string} + */ +function format(text) { + const self = this; + + self[watchdogSymbol]++; + if (this[watchdogSymbol] > 20) { + throw new Error('too deep nesting') + } + + let openMarker = self[internalSymbol]['marker']['open']?.[this[markerOpenIndexSymbol]]; + let closeMarker = self[internalSymbol]['marker']['close']?.[this[markerCloseIndexSymbol]]; + + // contains no placeholders + if (text.indexOf(openMarker) === -1 || text.indexOf(closeMarker) === -1) { + return text; + } + + let result = tokenize.call(this, validateString(text), openMarker, closeMarker) + + if (self[internalSymbol]['marker']['open']?.[this[markerOpenIndexSymbol] + 1]) { + this[markerOpenIndexSymbol]++; + } + + if (self[internalSymbol]['marker']['close']?.[this[markerCloseIndexSymbol] + 1]) { + this[markerCloseIndexSymbol]++; + } + + result = format.call(self, result) + + return result; +} + +/** + * @private + * @since 1.12.0 + * @param text + * @return {string} + */ +function tokenize(text, openMarker, closeMarker) { + const self = this; + + let formatted = []; + + const parameterAssignment = self[internalSymbol]['parameter']['assignment'] + const parameterDelimiter = self[internalSymbol]['parameter']['delimiter'] + const callbacks = self[internalSymbol]['callbacks']; + + while (true) { + + let startIndex = text.indexOf(openMarker); + // no marker + if (startIndex === -1) { + formatted.push(text); + break; + } else if (startIndex > 0) { + formatted.push(text.substring(0, startIndex)) + text = text.substring(startIndex) + } + + let endIndex = text.substring(openMarker.length).indexOf(closeMarker); + if (endIndex !== -1) endIndex += openMarker.length; + let insideStartIndex = text.substring(openMarker.length).indexOf(openMarker); + if (insideStartIndex !== -1) { + insideStartIndex += openMarker.length; + if (insideStartIndex < endIndex) { + let result = tokenize.call(self, text.substring(insideStartIndex), openMarker, closeMarker); + text = text.substring(0, insideStartIndex) + result + endIndex = text.substring(openMarker.length).indexOf(closeMarker); + if (endIndex !== -1) endIndex += openMarker.length; + } + } + + if (endIndex === -1) { + throw new Error("syntax error in formatter template") + return; + } + + let key = text.substring(openMarker.length, endIndex); + let parts = key.split(parameterDelimiter); + let currentPipe = parts.shift(); + + self[workingDataSymbol] = extend({}, self[internalObjectSymbol], self[workingDataSymbol]); + + for (const kv of parts) { + const [k, v] = kv.split(parameterAssignment); + self[workingDataSymbol][k] = v; + } + + const t1 = key.split('|').shift().trim(); // pipe symbol + const t2 = t1.split('::').shift().trim(); // key value delimiter + const t3 = t2.split('.').shift().trim(); // path delimiter + let prefix = self[workingDataSymbol]?.[t3] ? 'path:' : 'static:'; + + let command = ""; + if (prefix && key.indexOf(prefix) !== 0 + && key.indexOf('path:') !== 0 + && key.indexOf('static:') !== 0) { + command = prefix; + } + + command += currentPipe; + + const pipe = new Pipe(command); + + if (isObject(callbacks)) { + for (const [name, callback] of Object.entries(callbacks)) { + pipe.setCallback(name, callback); + } + } + + formatted.push(validateString(pipe.run(self[workingDataSymbol]))); + + text = text.substring(endIndex + closeMarker.length); + + } + + return formatted.join(''); +} +'use strict'; + +/** + * Namespace for texts. + * + * @namespace Monster.Text + * @memberOf Monster + * @author schukai GmbH + */ +const ns = {}; +'use strict'; + +/** + * @author schukai GmbH + */ + +import {extend} from "../../../data/extend.mjs"; +import {Link} from "../link.mjs"; + +export {Stylesheet} + +/** + * This class is used by the resource manager to embed external resources. + * + * ``` + * <script type="module"> + * import {Style} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/resource/link/stylesheet.mjs'; + * new Stylesheet() + * </script> + * ``` + * + * @since 1.25.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM.Resource + * @summary A Resource class + * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link + */ +class Stylesheet extends Link { + + /** + * @property {string} rel {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-rel} + */ + get defaults() { + return extend({}, super.defaults, { + rel: 'stylesheet' + }) + } + +} + +'use strict'; + +/** + * In this namespace you will find classes and methods for links + * + * @namespace Monster.DOM.Resource.Link + * @memberOf Monster + * @author schukai GmbH + */ +const ns = {};'use strict'; + +/** + * In this namespace you will find classes and methods for handling resources. + * + * @namespace Monster.DOM.Resource + * @memberOf Monster + * @author schukai GmbH + */ +const ns = {}; +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalStateSymbol} from "../../constants.mjs"; +import {extend} from "../../data/extend.mjs"; +import {getGlobalFunction} from "../../types/global.mjs"; +import { + ATTRIBUTE_CLASS, + ATTRIBUTE_ERRORMESSAGE, + ATTRIBUTE_ID, + ATTRIBUTE_SRC, + ATTRIBUTE_TITLE, + ATTRIBUTE_TYPE, + TAG_SCRIPT +} from "../constants.mjs"; +import {KEY_DOCUMENT, KEY_QUERY, referenceSymbol, Resource} from "../resource.mjs"; + +export {Data} + +/** + * This class is used by the resource manager to embed data. + * + * ``` + * <script type="module"> + * import {Data} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/resource/data.mjs'; + * new Data() + * </script> + * ``` + * + * @since 1.25.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM.Resource + * @summary A Data Resource class + */ +class Data extends Resource { + + /** + * @property {string} mode=cors https://developer.mozilla.org/en-US/docs/Web/API/fetch + * @property {string} credentials=same-origin https://developer.mozilla.org/en-US/docs/Web/API/fetch + * @property {string} type=application/json {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-type} + */ + get defaults() { + return extend({}, super.defaults, { + mode: 'cors', + credentials: 'same-origin', + type: 'application/json', + }) + } + + /** + * + * @return {Monster.DOM.Resource.Data} + */ + create() { + createElement.call(this); + return this; + } + + /** + * This method appends the HTMLElement to the specified document + * + * throws {Error} target not found + * @return {Monster.DOM.Resource} + */ + connect() { + + if (!(this[referenceSymbol] instanceof HTMLElement)) { + this.create(); + } + + appendToDocument.call(this); + return this; + } + + /** + * @return {string} + */ + static getURLAttribute() { + return ATTRIBUTE_SRC + } + +} + +/** + * @private + * @return {Monster.DOM.Resource.Data} + */ +function createElement() { + const self = this; + + const document = self.getOption(KEY_DOCUMENT); + self[referenceSymbol] = document.createElement(TAG_SCRIPT); + + for (let key of [ATTRIBUTE_TYPE, ATTRIBUTE_ID, ATTRIBUTE_CLASS, ATTRIBUTE_TITLE]) { + if (self.getOption(key) !== undefined) { + self[referenceSymbol][key] = self.getOption(key); + } + } + + return self; +} + + +/** + * @private + * @return {Promise} + * throws {Error} target not found + */ +function appendToDocument() { + const self = this; + + const targetNode = document.querySelector(self.getOption(KEY_QUERY, 'head')) + if (!(targetNode instanceof HTMLElement)) { + throw new Error('target not found') + } + + targetNode.appendChild(self[referenceSymbol]); + + getGlobalFunction('fetch')(self.getOption(ATTRIBUTE_SRC), { + method: 'GET', // *GET, POST, PUT, DELETE, etc. + mode: self.getOption('mode', 'cors'), // no-cors, *cors, same-origin + cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached + credentials: self.getOption('credentials', 'same-origin'), // include, *same-origin, omit + headers: { + 'Accept': self.getOption('type', 'application/json') + }, + redirect: 'follow', // manual, *follow, error + referrerPolicy: 'no-referrer', // no-referrer, + }).then(response => { + + return response.text() + + + }).then(text => { + + const textNode = document.createTextNode(text); + self[referenceSymbol].appendChild(textNode); + + self[internalStateSymbol].getSubject()['loaded'] = true; + + + }).catch(e => { + self[internalStateSymbol].setSubject({ + loaded: true, + error: e.toString(), + }) + + targetNode.setAttribute(ATTRIBUTE_ERRORMESSAGE, e.toString()); + }) + + return self; +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {extend} from "../../data/extend.mjs"; +import { + ATTRIBUTE_CLASS, + ATTRIBUTE_HREF, + ATTRIBUTE_ID, + ATTRIBUTE_NONCE, ATTRIBUTE_SRC, + ATTRIBUTE_TITLE, ATTRIBUTE_TYPE, + TAG_LINK +} from "../constants.mjs"; +import {KEY_DOCUMENT, referenceSymbol, Resource} from "../resource.mjs"; + +export {Link} + +/** + * This class is used by the resource manager to embed external resources. + * + * ``` + * <script type="module"> + * import {Link} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/resource/link.mjs'; + * new Link() + * </script> + * ``` + * + * @since 1.25.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM.Resource + * @summary A Resource class + * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link + */ +class Link extends Resource { + + /** + * @property {string} as {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-as} + * @property {string} crossOrigin=anonymous {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-crossorigin} + * @property {boolean} disabled + * @property {string} href {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-href} + * @property {string} hreflang {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-hreflang} + * @property {string} imagesizes {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-imagesizes} + * @property {string} imagesrcset {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-imagesrcset} + * @property {string} integrity {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-integrity} + * @property {string} media {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-media} + * @property {string} prefetch {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-prefetch} + * @property {string} referrerpolicy {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-referrerpolicy} + * @property {string} rel {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-rel} + * @property {string} type {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-type} + * @property {string} sizes {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-sizes} + * @property {string} nonce {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-nonce} + */ + get defaults() { + return extend({}, super.defaults, { + as: undefined, + crossOrigin: 'anonymous', + disabled: undefined, + href: undefined, + hreflang: undefined, + imagesizes: undefined, + imagesrcset: undefined, + integrity: undefined, + media: undefined, + prefetch: undefined, + referrerpolicy: undefined, + rel: undefined, + sizes: undefined, + type: undefined, + nonce: undefined + }) + } + + /** + * + * @return {Monster.DOM.Resource.Link} + */ + create() { + createElement.call(this); + return this; + } + + /** + * @return {string} + */ + static getURLAttribute() { + return ATTRIBUTE_HREF + } + +} + +/** + * @private + * @return {Monster.DOM.Resource.Link} + */ +function createElement() { + const self = this; + + const document = self.getOption(KEY_DOCUMENT); + self[referenceSymbol] = document.createElement(TAG_LINK); + + for (let key of ['as','crossOrigin','disabled','href','hreflang','imagesizes','imagesrcset','integrity','media','prefetch','referrerpolicy','sizes','rel','type',ATTRIBUTE_HREF,ATTRIBUTE_ID,ATTRIBUTE_CLASS,ATTRIBUTE_TITLE,ATTRIBUTE_NONCE]) { + if (self.getOption(key) !== undefined) { + self[referenceSymbol][key] = self.getOption(key); + } + } + + return self; +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {extend} from "../../data/extend.mjs"; +import { + ATTRIBUTE_CLASS, + ATTRIBUTE_ID, + ATTRIBUTE_NONCE, + ATTRIBUTE_SRC, + ATTRIBUTE_TITLE, + ATTRIBUTE_TYPE, + TAG_SCRIPT +} from "../constants.mjs"; +import {KEY_DOCUMENT, referenceSymbol, Resource} from "../resource.mjs"; + +export {Script} + +/** + * This class is used by the resource manager to embed scripts. + * + * ``` + * <script type="module"> + * import {Script} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/resource/script.mjs'; + * new Script() + * </script> + * ``` + * + * @since 1.25.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM.Resource + * @summary A Resource class + */ +class Script extends Resource { + + /** + * @property {boolean} async=true {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-async} + * @property {string} crossOrigin=anonymous {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-crossorigin} + * @property {boolean} defer=false {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-defer} + * @property {string} integrity {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-integrity} + * @property {boolean} nomodule {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-nomodule} + * @property {string} nonce {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-nonce} + * @property {string} referrerpolicy {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-referrerpolicy} + * @property {string} type {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-type} + */ + get defaults() { + return extend({}, super.defaults, { + async: true, + crossOrigin: 'anonymous', + defer: false, + integrity: undefined, + nomodule: false, + nonce: undefined, + referrerpolicy: undefined, + type: 'text/javascript', + }) + } + + /** + * + * @return {Monster.DOM.Resource.Script} + */ + create() { + createElement.call(this); + return this; + } + + /** + * @return {string} + */ + static getURLAttribute() { + return ATTRIBUTE_SRC + } + +} + +/** + * @private + * @return {Monster.DOM.Resource.Script} + */ +function createElement() { + const self = this; + + const document = self.getOption(KEY_DOCUMENT); + self[referenceSymbol] = document.createElement(TAG_SCRIPT); + + for (let key of ['crossOrigin', 'defer', 'async', 'integrity', 'nomodule', ATTRIBUTE_NONCE, 'referrerpolicy', ATTRIBUTE_TYPE, ATTRIBUTE_SRC, ATTRIBUTE_ID, ATTRIBUTE_CLASS, ATTRIBUTE_TITLE]) { + if (self.getOption(key) !== undefined) { + self[referenceSymbol][key] = self.getOption(key); + } + } + + + return self; +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../constants.mjs"; +import {diff} from "../data/diff.mjs"; +import {Pathfinder} from "../data/pathfinder.mjs"; +import {Pipe} from "../data/pipe.mjs"; +import { + ATTRIBUTE_ERRORMESSAGE, + ATTRIBUTE_UPDATER_ATTRIBUTES, + ATTRIBUTE_UPDATER_BIND, + ATTRIBUTE_UPDATER_INSERT, + ATTRIBUTE_UPDATER_INSERT_REFERENCE, + ATTRIBUTE_UPDATER_REMOVE, + ATTRIBUTE_UPDATER_REPLACE, + ATTRIBUTE_UPDATER_SELECT_THIS +} from "../dom/constants.mjs"; + +import {Base} from "../types/base.mjs"; +import {isArray, isInstance, isIterable} from "../types/is.mjs"; +import {Observer} from "../types/observer.mjs"; +import {ProxyObserver} from "../types/proxyobserver.mjs"; +import {validateArray, validateInstance} from "../types/validate.mjs"; +import {clone} from "../util/clone.mjs"; +import {trimSpaces} from "../util/trimspaces.mjs"; +import {findTargetElementFromEvent} from "./events.mjs"; +import {findDocumentTemplate} from "./template.mjs"; +import {getDocument} from "./util.mjs"; + +export {Updater} + +/** + * The updater class connects an object with the dom. In this way, structures and contents in the DOM can be programmatically adapted via attributes. + * + * For example, to include a string from an object, the attribute `data-monster-replace` can be used. + * a further explanation can be found under {@tutorial dom-based-templating-implementation}. + * + * Changes to attributes are made only when the direct values are changed. If you want to assign changes to other values + * as well, you have to insert the attribute `data-monster-select-this`. This should be done with care, as it can reduce performance. + * + * ``` + * <script type="module"> + * import {Updater} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/updater.mjs'; + * new Updater() + * </script> + * ``` + * + * @example + * + * import {Updater} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/updater.mjs'; + * + * // First we prepare the html document. + * // This is done here via script, but can also be inserted into the document as pure html. + * // To do this, simply insert the tag <h1 data-monster-replace="path:headline"></h1>. + * const body = document.querySelector('body'); + * const headline = document.createElement('h1'); + * headline.setAttribute('data-monster-replace','path:headline') + * body.appendChild(headline); + * + * // the data structure + * let obj = { + * headline: "Hello World", + * }; + * + * // Now comes the real magic. we pass the updater the parent HTMLElement + * // and the desired data structure. + * const updater = new Updater(body, obj); + * updater.run(); + * + * // Now you can change the data structure and the HTML will follow these changes. + * const subject = updater.getSubject(); + * subject['headline'] = "Hello World!" + * + * @since 1.8.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @throws {Error} the value is not iterable + * @throws {Error} pipes are not allowed when cloning a node. + * @throws {Error} no template was found with the specified key. + * @throws {Error} the maximum depth for the recursion is reached. + * @throws {TypeError} value is not a object + * @throws {TypeError} value is not an instance of HTMLElement + * @summary The updater class connects an object with the dom + */ +class Updater extends Base { + + /** + * @since 1.8.0 + * @param {HTMLElement} element + * @param {object|ProxyObserver|undefined} subject + * @throws {TypeError} value is not a object + * @throws {TypeError} value is not an instance of HTMLElement + * @see {@link Monster.DOM.findDocumentTemplate} + */ + constructor(element, subject) { + super(); + + /** + * @type {HTMLElement} + */ + if (subject === undefined) subject = {} + if (!isInstance(subject, ProxyObserver)) { + subject = new ProxyObserver(subject); + } + + this[internalSymbol] = { + element: validateInstance(element, HTMLElement), + last: {}, + callbacks: new Map(), + eventTypes: ['keyup', 'click', 'change', 'drop', 'touchend', 'input'], + subject: subject + } + + this[internalSymbol].callbacks.set('checkstate', getCheckStateCallback.call(this)); + + this[internalSymbol].subject.attachObserver(new Observer(() => { + + const s = this[internalSymbol].subject.getRealSubject(); + + const diffResult = diff(this[internalSymbol].last, s) + this[internalSymbol].last = clone(s); + + for (const [, change] of Object.entries(diffResult)) { + removeElement.call(this, change); + insertElement.call(this, change); + updateContent.call(this, change); + updateAttributes.call(this, change); + } + })); + + } + + /** + * Defaults: 'keyup', 'click', 'change', 'drop', 'touchend' + * + * @see {@link https://developer.mozilla.org/de/docs/Web/Events} + * @since 1.9.0 + * @param {Array} types + * @return {Updater} + */ + setEventTypes(types) { + this[internalSymbol].eventTypes = validateArray(types); + return this; + } + + /** + * With this method, the eventlisteners are hooked in and the magic begins. + * + * ``` + * updater.run().then(() => { + * updater.enableEventProcessing(); + * }); + * ``` + * + * @since 1.9.0 + * @return {Updater} + * @throws {Error} the bind argument must start as a value with a path + */ + enableEventProcessing() { + this.disableEventProcessing(); + + for (const type of this[internalSymbol].eventTypes) { + // @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener + this[internalSymbol].element.addEventListener(type, getControlEventHandler.call(this), { + capture: true, + passive: true + }); + } + + return this; + + } + + /** + * This method turns off the magic or who loves it more profane it removes the eventListener. + * + * @since 1.9.0 + * @return {Updater} + */ + disableEventProcessing() { + + for (const type of this[internalSymbol].eventTypes) { + this[internalSymbol].element.removeEventListener(type, getControlEventHandler.call(this)); + } + + return this; + + } + + /** + * The run method must be called for the update to start working. + * The method ensures that changes are detected. + * + * ``` + * updater.run().then(() => { + * updater.enableEventProcessing(); + * }); + * ``` + * + * @summary Let the magic begin + * @return {Promise} + */ + run() { + // the key __init__has no further meaning and is only + // used to create the diff for empty objects. + this[internalSymbol].last = {'__init__': true}; + return this[internalSymbol].subject.notifyObservers(); + } + + /** + * Gets the values of bound elements and changes them in subject + * + * @since 1.27.0 + * @return {Monster.DOM.Updater} + */ + retrieve() { + retrieveFromBindings.call(this); + return this; + } + + /** + * If you have passed a ProxyObserver in the constructor, you will get the object that the ProxyObserver manages here. + * However, if you passed a simple object, here you will get a proxy for that object. + * + * For changes the ProxyObserver must be used. + * + * @since 1.8.0 + * @return {Proxy} + */ + getSubject() { + return this[internalSymbol].subject.getSubject(); + } + + /** + * This method can be used to register commands that can be called via call: instruction. + * This can be used to provide a pipe with its own functionality. + * + * @param {string} name + * @param {function} callback + * @returns {Transformer} + * @throws {TypeError} value is not a string + * @throws {TypeError} value is not a function + */ + setCallback(name, callback) { + this[internalSymbol].callbacks.set(name, callback); + return this; + } + +} + +/** + * @private + * @since 1.9.0 + * @return {function + * @this Updater + */ +function getCheckStateCallback() { + const self = this; + + return function (current) { + + // this is a reference to the current object (therefore no array function here) + if (this instanceof HTMLInputElement) { + if (['radio', 'checkbox'].indexOf(this.type) !== -1) { + return (this.value + "" === current + "") ? 'true' : undefined + } + } else if (this instanceof HTMLOptionElement) { + + if (isArray(current) && current.indexOf(this.value) !== -1) { + return 'true' + } + + return undefined; + } + } +} + +/** + * @private + */ +const symbol = Symbol('EventHandler'); + +/** + * @private + * @return {function} + * @this Updater + * @throws {Error} the bind argument must start as a value with a path + */ +function getControlEventHandler() { + + const self = this; + + if (self[symbol]) { + return self[symbol]; + } + + /** + * @throws {Error} the bind argument must start as a value with a path. + * @throws {Error} unsupported object + * @param {Event} event + */ + self[symbol] = (event) => { + const element = findTargetElementFromEvent(event, ATTRIBUTE_UPDATER_BIND); + + if (element === undefined) { + return; + } + + retrieveAndSetValue.call(self, element); + + } + + return self[symbol]; + + +} + +/** + * @throws {Error} the bind argument must start as a value with a path + * @param {HTMLElement} element + * @return void + * @memberOf Monster.DOM + * @private + */ +function retrieveAndSetValue(element) { + + const self = this; + + const pathfinder = new Pathfinder(self[internalSymbol].subject.getSubject()); + + let path = element.getAttribute(ATTRIBUTE_UPDATER_BIND); + + if (path.indexOf('path:') !== 0) { + throw new Error('the bind argument must start as a value with a path'); + } + + path = path.substr(5); + + let value; + + if (element instanceof HTMLInputElement) { + switch (element.type) { + + case 'checkbox': + value = element.checked ? element.value : undefined; + break; + default: + value = element.value; + break; + + + } + } else if (element instanceof HTMLTextAreaElement) { + value = element.value; + + } else if (element instanceof HTMLSelectElement) { + + switch (element.type) { + case 'select-one': + value = element.value; + break; + case 'select-multiple': + value = element.value; + + let options = element?.selectedOptions; + if (options === undefined) options = element.querySelectorAll(":scope option:checked"); + value = Array.from(options).map(({value}) => value); + + break; + } + + + // values from customelements + } else if ((element?.constructor?.prototype && !!Object.getOwnPropertyDescriptor(element.constructor.prototype, 'value')?.['get']) || element.hasOwnProperty('value')) { + value = element?.['value']; + } else { + throw new Error("unsupported object"); + } + + const copy = clone(self[internalSymbol].subject.getRealSubject()); + const pf = new Pathfinder(copy); + pf.setVia(path, value); + + const diffResult = diff(copy, self[internalSymbol].subject.getRealSubject()); + + if (diffResult.length > 0) { + pathfinder.setVia(path, value); + } +} + +/** + * @since 1.27.0 + * @return void + * @private + */ +function retrieveFromBindings() { + const self = this; + + if (self[internalSymbol].element.matches('[' + ATTRIBUTE_UPDATER_BIND + ']')) { + retrieveAndSetValue.call(self, element) + } + + for (const [, element] of self[internalSymbol].element.querySelectorAll('[' + ATTRIBUTE_UPDATER_BIND + ']').entries()) { + retrieveAndSetValue.call(self, element) + } + +} + +/** + * @private + * @since 1.8.0 + * @param {object} change + * @return {void} + */ +function removeElement(change) { + const self = this; + + for (const [, element] of self[internalSymbol].element.querySelectorAll(':scope [' + ATTRIBUTE_UPDATER_REMOVE + ']').entries()) { + element.parentNode.removeChild(element); + } +} + +/** + * @private + * @since 1.8.0 + * @param {object} change + * @return {void} + * @throws {Error} the value is not iterable + * @throws {Error} pipes are not allowed when cloning a node. + * @throws {Error} no template was found with the specified key. + * @throws {Error} the maximum depth for the recursion is reached. + * @this Updater + */ +function insertElement(change) { + const self = this; + const subject = self[internalSymbol].subject.getRealSubject(); + const document = getDocument(); + + let mem = new WeakSet; + let wd = 0; + + const container = self[internalSymbol].element; + + while (true) { + let found = false; + wd++; + + let p = clone(change?.['path']); + if (!isArray(p)) return self; + + while (p.length > 0) { + const current = p.join('.'); + + let iterator = new Set; + const query = '[' + ATTRIBUTE_UPDATER_INSERT + '*="path:' + current + '"]'; + + const e = container.querySelectorAll(query); + + if (e.length > 0) { + iterator = new Set( + [...e] + ) + } + + if (container.matches(query)) { + iterator.add(container); + } + + for (const [, containerElement] of iterator.entries()) { + + if (mem.has(containerElement)) continue; + mem.add(containerElement) + + found = true; + + const attributes = containerElement.getAttribute(ATTRIBUTE_UPDATER_INSERT); + let def = trimSpaces(attributes); + let i = def.indexOf(' '); + let key = trimSpaces(def.substr(0, i)); + let refPrefix = key + '-'; + let cmd = trimSpaces(def.substr(i)); + + // this case is actually excluded by the query but is nevertheless checked again here + if (cmd.indexOf('|') > 0) { + throw new Error("pipes are not allowed when cloning a node."); + } + + let pipe = new Pipe(cmd); + self[internalSymbol].callbacks.forEach((f, n) => { + pipe.setCallback(n, f); + }) + + let value + try { + containerElement.removeAttribute(ATTRIBUTE_ERRORMESSAGE); + value = pipe.run(subject) + } catch (e) { + containerElement.setAttribute(ATTRIBUTE_ERRORMESSAGE, e.message); + } + + let dataPath = cmd.split(':').pop(); + + let insertPoint; + if (containerElement.hasChildNodes()) { + insertPoint = containerElement.lastChild; + } + + if (!isIterable(value)) { + throw new Error('the value is not iterable'); + } + + let available = new Set; + + for (const [i, obj] of Object.entries(value)) { + let ref = refPrefix + i; + let currentPath = dataPath + "." + i; + + available.add(ref); + let refElement = containerElement.querySelector('[' + ATTRIBUTE_UPDATER_INSERT_REFERENCE + '="' + ref + '"]'); + + if (refElement instanceof HTMLElement) { + insertPoint = refElement; + continue; + } + + appendNewDocumentFragment(containerElement, key, ref, currentPath); + } + + let nodes = containerElement.querySelectorAll('[' + ATTRIBUTE_UPDATER_INSERT_REFERENCE + '*="' + refPrefix + '"]'); + for (const [, node] of Object.entries(nodes)) { + if (!available.has(node.getAttribute(ATTRIBUTE_UPDATER_INSERT_REFERENCE))) { + try { + containerElement.removeChild(node); + } catch (e) { + containerElement.setAttribute(ATTRIBUTE_ERRORMESSAGE, (containerElement.getAttribute(ATTRIBUTE_ERRORMESSAGE) + ", " + e.message).trim()); + } + + } + } + } + + p.pop(); + } + + if (found === false) break; + if (wd++ > 200) { + throw new Error('the maximum depth for the recursion is reached.'); + } + + } + + +} + +/** + * + * @private + * @since 1.8.0 + * @param {HTMLElement} container + * @param {string} key + * @param {string} ref + * @param {string} path + * @throws {Error} no template was found with the specified key. + */ +function appendNewDocumentFragment(container, key, ref, path) { + + let template = findDocumentTemplate(key, container); + + let nodes = template.createDocumentFragment(); + for (const [, node] of Object.entries(nodes.childNodes)) { + if (node instanceof HTMLElement) { + + applyRecursive(node, key, path); + node.setAttribute(ATTRIBUTE_UPDATER_INSERT_REFERENCE, ref); + } + + container.appendChild(node); + } +} + +/** + * @private + * @since 1.10.0 + * @param {HTMLElement} node + * @param {string} key + * @param {string} path + * @return {void} + */ +function applyRecursive(node, key, path) { + + if (node instanceof HTMLElement) { + + if (node.hasAttribute(ATTRIBUTE_UPDATER_REPLACE)) { + let value = node.getAttribute(ATTRIBUTE_UPDATER_REPLACE); + node.setAttribute(ATTRIBUTE_UPDATER_REPLACE, value.replaceAll("path:" + key, "path:" + path)); + } + + if (node.hasAttribute(ATTRIBUTE_UPDATER_ATTRIBUTES)) { + let value = node.getAttribute(ATTRIBUTE_UPDATER_ATTRIBUTES); + node.setAttribute(ATTRIBUTE_UPDATER_ATTRIBUTES, value.replaceAll("path:" + key, "path:" + path)); + } + + for (const [, child] of Object.entries(node.childNodes)) { + applyRecursive(child, key, path); + } + } +} + +/** + * @private + * @since 1.8.0 + * @param {object} change + * @return {void} + * @this Updater + */ +function updateContent(change) { + const self = this; + const subject = self[internalSymbol].subject.getRealSubject(); + + let p = clone(change?.['path']); + runUpdateContent.call(this, this[internalSymbol].element, p, subject); + + const slots = this[internalSymbol].element.querySelectorAll('slot'); + if (slots.length > 0) { + for (const [, slot] of Object.entries(slots)) { + for (const [, element] of Object.entries(slot.assignedNodes())) { + runUpdateContent.call(this, element, p, subject); + } + } + } + + +} + +/** + * @private + * @since 1.8.0 + * @param {HTMLElement} container + * @param {array} parts + * @param {object} subject + * @return {void} + */ +function runUpdateContent(container, parts, subject) { + if (!isArray(parts)) return; + if (!(container instanceof HTMLElement)) return; + parts = clone(parts); + + let mem = new WeakSet; + + while (parts.length > 0) { + const current = parts.join('.'); + parts.pop(); + + // Unfortunately, static data is always changed as well, since it is not possible to react to changes here. + const query = '[' + ATTRIBUTE_UPDATER_REPLACE + '^="path:' + current + '"], [' + ATTRIBUTE_UPDATER_REPLACE + '^="static:"]'; + const e = container.querySelectorAll('' + query); + + const iterator = new Set([ + ...e + ]) + + if (container.matches(query)) { + iterator.add(container); + } + + /** + * @type {HTMLElement} + */ + for (const [element] of iterator.entries()) { + + if (mem.has(element)) return; + mem.add(element) + + const attributes = element.getAttribute(ATTRIBUTE_UPDATER_REPLACE) + let cmd = trimSpaces(attributes); + + let pipe = new Pipe(cmd); + this[internalSymbol].callbacks.forEach((f, n) => { + pipe.setCallback(n, f); + }) + + let value + try { + element.removeAttribute(ATTRIBUTE_ERRORMESSAGE); + value = pipe.run(subject) + } catch (e) { + element.setAttribute(ATTRIBUTE_ERRORMESSAGE, e.message); + } + + if (value instanceof HTMLElement) { + while (element.firstChild) { + element.removeChild(element.firstChild); + } + + try { + element.appendChild(value); + } catch (e) { + element.setAttribute(ATTRIBUTE_ERRORMESSAGE, (element.getAttribute(ATTRIBUTE_ERRORMESSAGE) + ", " + e.message).trim()); + } + + } else { + element.innerHTML = value; + } + + } + + + } + +} + +/** + * @private + * @since 1.8.0 + * @param {string} path + * @param {object} change + * @return {void} + */ +function updateAttributes(change) { + const subject = this[internalSymbol].subject.getRealSubject(); + let p = clone(change?.['path']); + runUpdateAttributes.call(this, this[internalSymbol].element, p, subject); +} + +/** + * @private + * @param {HTMLElement} container + * @param {array} parts + * @param {object} subject + * @return {void} + * @this Updater + */ +function runUpdateAttributes(container, parts, subject) { + + const self = this; + + if (!isArray(parts)) return; + parts = clone(parts); + + let mem = new WeakSet; + + while (parts.length > 0) { + const current = parts.join('.'); + parts.pop(); + + let iterator = new Set; + + const query = '[' + ATTRIBUTE_UPDATER_SELECT_THIS + '], [' + ATTRIBUTE_UPDATER_ATTRIBUTES + '*="path:' + current + '"], [' + ATTRIBUTE_UPDATER_ATTRIBUTES + '^="static:"]'; + + const e = container.querySelectorAll(query); + + if (e.length > 0) { + iterator = new Set( + [...e] + ) + } + + if (container.matches(query)) { + iterator.add(container); + } + + for (const [element] of iterator.entries()) { + + if (mem.has(element)) return; + mem.add(element) + + const attributes = element.getAttribute(ATTRIBUTE_UPDATER_ATTRIBUTES) + + for (let [, def] of Object.entries(attributes.split(','))) { + def = trimSpaces(def); + let i = def.indexOf(' '); + let name = trimSpaces(def.substr(0, i)); + let cmd = trimSpaces(def.substr(i)); + + let pipe = new Pipe(cmd); + + self[internalSymbol].callbacks.forEach((f, n) => { + pipe.setCallback(n, f, element); + }) + + let value + try { + element.removeAttribute(ATTRIBUTE_ERRORMESSAGE); + value = pipe.run(subject) + } catch (e) { + element.setAttribute(ATTRIBUTE_ERRORMESSAGE, e.message); + } + + + if (value === undefined) { + element.removeAttribute(name) + + } else if (element.getAttribute(name) !== value) { + element.setAttribute(name, value); + } + + handleInputControlAttributeUpdate.call(this, element, name, value); + + } + } + } + +} + +/** + * @private + * @param {HTMLElement|*} element + * @param {string} name + * @param {string|number|undefined} value + * @return {void} + * @this Updater + */ + +function handleInputControlAttributeUpdate(element, name, value) { + const self = this; + + if (element instanceof HTMLSelectElement) { + + + switch (element.type) { + case 'select-multiple': + + for (const [index, opt] of Object.entries(element.options)) { + if (value.indexOf(opt.value) !== -1) { + opt.selected = true; + } else { + opt.selected = false; + } + } + + break; + case 'select-one': + // Only one value may be selected + + for (const [index, opt] of Object.entries(element.options)) { + if (opt.value === value) { + element.selectedIndex = index; + break; + } + } + + break; + } + + + } else if (element instanceof HTMLInputElement) { + switch (element.type) { + + case 'radio': + if (name === 'checked') { + + if (value !== undefined) { + element.checked = true; + } else { + element.checked = false; + } + } + + break; + + case 'checkbox': + + if (name === 'checked') { + + if (value !== undefined) { + element.checked = true; + } else { + element.checked = false; + } + } + + break; + case 'text': + default: + if (name === 'value') { + element.value = (value === undefined ? "" : value); + } + + break; + + + } + } else if (element instanceof HTMLTextAreaElement) { + if (name === 'value') { + element.value = (value === undefined ? "" : value); + } + } + +} +'use strict'; + +import {extend} from "../data/extend.mjs"; +/** + * @author schukai GmbH + */ + +import {ATTRIBUTE_VALUE} from "./constants.mjs"; +import {CustomElement, attributeObserverSymbol} from "./customelement.mjs"; + +export {CustomControl} + +/** + * @private + * @type {symbol} + */ +const attachedInternalSymbol = Symbol('attachedInternal'); + +/** + * To define a new HTML control we need the power of CustomElement + * + * IMPORTANT: after defining a `CustomElement`, the `registerCustomElement` method must be called + * with the new class name. only then will the tag defined via the `getTag` method be made known to the DOM. + * + * <img src="./images/customcontrol-class.png"> + * + * This control uses `attachInternals()` to integrate the control into a form. + * If the target environment does not support this method, the [polyfill](https://www.npmjs.com/package/element-internals-polyfill ) can be used. + * + * You can create the object via the function `document.createElement()`. + * + * ``` + * <script type="module"> + * import {CustomControl} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source//monster.mjs'; + * document.createElement('monster-') + * </script> + * ``` + * + * @startuml customcontrol-class.png + * skinparam monochrome true + * skinparam shadowing false + * HTMLElement <|-- CustomElement + * CustomElement <|-- CustomControl + * @enduml + * + * @summary A base class for customcontrols based on CustomElement + * @see {@link https://www.npmjs.com/package/element-internals-polyfill} + * @see {@link https://github.com/WICG/webcomponents} + * @see {@link https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements} + * @since 1.14.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + */ +class CustomControl extends CustomElement { + + /** + * IMPORTANT: CustomControls instances are not created via the constructor, but either via a tag in the HTML or via <code>document.createElement()</code>. + * + * @throws {Error} the ElementInternals is not supported and a polyfill is necessary + * @summary create new Instance + */ + constructor() { + super(); + + if (typeof this['attachInternals'] === 'function') { + /** + * currently only supported by chrome + * @property {Object} + * @private + */ + this[attachedInternalSymbol] = this.attachInternals(); + } + + initObserver.call(this); + + } + + /** + * This method determines which attributes are to be monitored by `attributeChangedCallback()`. + * + * @return {string[]} + * @since 1.15.0 + */ + static get observedAttributes() { + const list = super.observedAttributes; + list.push(ATTRIBUTE_VALUE); + return list; + } + + /** + * + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/attachInternals} + * @since 1.14.0 + * @return {boolean} + */ + static get formAssociated() { + return true; + } + + /** + * Derived classes can override and extend this method as follows. + * + * ``` + * get defaults() { + * return extends{}, super.defaults, { + * myValue:true + * }); + * } + * ``` + * + * @see {@link https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements-face-example} + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/attachInternals} + * @return {object} + * @since 1.14.0 + */ + get defaults() { + return extend({}, super.defaults); + } + + /** + * Must be overridden by a derived class and return the value of the control. + * + * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals) + * + * @since 1.14.0 + * @throws {Error} the value getter must be overwritten by the derived class + */ + get value() { + throw Error('the value getter must be overwritten by the derived class'); + } + + /** + * Must be overridden by a derived class and return the value of the control. + * + * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals) + * + * @param {*} value + * @since 1.14.0 + * @throws {Error} the value setter must be overwritten by the derived class + */ + set value(value) { + throw Error('the value setter must be overwritten by the derived class'); + } + + /** + * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals) + * + * @return {NodeList} + * @since 1.14.0 + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/labels} + * @throws {Error} the ElementInternals is not supported and a polyfill is necessary + */ + get labels() { + return getInternal.call(this)?.labels; + } + + /** + * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals) + * + * @return {string|null} + */ + get name() { + return this.getAttribute('name'); + } + + /** + * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals) + * + * @return {string} + */ + get type() { + return this.constructor.getTag(); + } + + /** + * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals) + * + * @return {ValidityState} + * @throws {Error} the ElementInternals is not supported and a polyfill is necessary + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/ValidityState} + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/validity} + */ + get validity() { + return getInternal.call(this)?.validity; + } + + /** + * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals) + * + * @return {string} + * @since 1.14.0 + * @see https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/validationMessage + * @throws {Error} the ElementInternals is not supported and a polyfill is necessary + */ + get validationMessage() { + return getInternal.call(this)?.validationMessage; + } + + /** + * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals) + * + * @return {boolean} + * @since 1.14.0 + * @see https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/willValidate + * @throws {Error} the ElementInternals is not supported and a polyfill is necessary + */ + get willValidate() { + return getInternal.call(this)?.willValidate; + } + + /** + * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals) + * + * @return {CustomStateSet} + * @since 1.14.0 + * @see https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/states + * @throws {Error} the ElementInternals is not supported and a polyfill is necessary + */ + get states() { + return getInternal.call(this)?.states; + } + + /** + * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals) + * + * @return {HTMLFontElement|null} + * @since 1.14.0 + * @see https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/form + * @throws {Error} the ElementInternals is not supported and a polyfill is necessary + */ + get form() { + return getInternal.call(this)?.form; + } + + /** + * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals) + * + * ``` + * // Use the control's name as the base name for submitted data + * const n = this.getAttribute('name'); + * const entries = new FormData(); + * entries.append(n + '-first-name', this.firstName_); + * entries.append(n + '-last-name', this.lastName_); + * this.setFormValue(entries); + * ``` + * + * @param {File|string|FormData} value + * @param {File|string|FormData} state + * @since 1.14.0 + * @return {undefined} + * @throws {DOMException} NotSupportedError + * @throws {Error} the ElementInternals is not supported and a polyfill is necessary + * @see https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/setFormValue + */ + setFormValue(value, state) { + getInternal.call(this).setFormValue(value, state); + } + + /** + * + * @param {object} flags + * @param {string|undefined} message + * @param {HTMLElement} anchor + * @see https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/setValidity + * @since 1.14.0 + * @return {undefined} + * @throws {DOMException} NotSupportedError + * @throws {Error} the ElementInternals is not supported and a polyfill is necessary + */ + setValidity(flags, message, anchor) { + getInternal.call(this).setValidity(flags, message, anchor); + } + + /** + * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals) + * + * @see https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/checkValidity + * @since 1.14.0 + * @return {boolean} + * @throws {DOMException} NotSupportedError + * @throws {Error} the ElementInternals is not supported and a polyfill is necessary + */ + checkValidity() { + return getInternal.call(this)?.checkValidity(); + } + + /** + * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals) + * + * @return {boolean} + * @since 1.14.0 + * @see https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/reportValidity + * @throws {Error} the ElementInternals is not supported and a polyfill is necessary + * @throws {DOMException} NotSupportedError + */ + reportValidity() { + return getInternal.call(this)?.reportValidity(); + } + +} + +/** + * @private + * @return {object} + * @throws {Error} the ElementInternals is not supported and a polyfill is necessary + * @this CustomControl + */ +function getInternal() { + const self = this; + + if (!(attachedInternalSymbol in this)) { + throw new Error('ElementInternals is not supported and a polyfill is necessary'); + } + + return this[attachedInternalSymbol]; +} + +/** + * @private + * @return {object} + * @this CustomControl + */ +function initObserver() { + const self = this; + + // value + self[attributeObserverSymbol]['value'] = () => { + self.setOption('value', self.getAttribute('value')); + } + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {parseLocale} from "../i18n/locale.mjs"; + +import {getDocument} from "./util.mjs"; + +export {getLocaleOfDocument} + +/** + * @private + * @type {string} + */ +const DEFAULT_LANGUAGE = 'en'; + +/** + * With this function you can read the language version set by the document. + * For this the attribute `lang` in the html tag is read. If no attribute is set, `en` is used as default. + * + * ```html + * <html lang="en"> + * ``` + * + * You can call the function via the monster namespace `new Monster.DOM.getLocaleOfDocument()`. + * + * ``` + * <script type="module"> + * import {getLocaleOfDocument} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/locale.mjs'; + * new getLocaleOfDocument() + * </script> + * ``` + * + * @since 1.13.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @throws {TypeError} value is not a string + * @throws {Error} unsupported locale + * @summary Tries to determine the locale used + */ +function getLocaleOfDocument() { + + const document = getDocument(); + + let html = document.querySelector('html') + if (html instanceof HTMLElement && html.hasAttribute('lang')) { + let locale = html.getAttribute('lang'); + if (locale) { + return new parseLocale(locale) + } + } + + return parseLocale(DEFAULT_LANGUAGE); +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from '../types/base.mjs'; +import {getGlobalObject} from '../types/global.mjs'; +import {validateString} from "../types/validate.mjs"; +import {ATTRIBUTE_THEME_NAME, DEFAULT_THEME} from "./constants.mjs"; + +export {Theme, getDocumentTheme} + +/** + * The Theme class provides the functionality for the theme. + * + * ``` + * <script type="module"> + * import {Theme} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/theme.mjs'; + * console.log(new Theme()) + * </script> + * ``` + * + * @example + * + * import {getDocumentTheme} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/theme.mjs'; + * + * const theme = getDocumentTheme(); + * console.log(theme.getName()); + * // ↦ monster + * + * @since 1.7.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @summary A theme class + */ +class Theme extends Base { + + /** + * + * @param name + * @throws {TypeError} value is not a string + */ + constructor(name) { + super(); + validateString(name); + this.name = name; + } + + /** + * + * @returns {string} + */ + getName() { + return this.name; + } + +} + +/** + * The theming used in the document can be defined via the html-tag. + * The theming is specified via the attribute `data-monster-theme-name`. + * + * As name for a theme all characters are valid, which are also allowed for a HTMLElement-ID. + * + * ``` + * <html data-monster-theme-name="my-theme"> + * ``` + * + * the default theme name is `monster`. + * + * @return {Theme} + * @memberOf Monster.DOM + * @since 1.7.0 + */ +function getDocumentTheme() { + let document = getGlobalObject('document'); + let name = DEFAULT_THEME; + + let element = document.querySelector('html'); + if (element instanceof HTMLElement) { + let theme = element.getAttribute(ATTRIBUTE_THEME_NAME); + if (theme) { + name = theme; + } + } + + return new Theme(name); + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalStateSymbol, internalSymbol,} from "../constants.mjs"; +import {extend} from "../data/extend.mjs"; +import {BaseWithOptions} from "../types/basewithoptions.mjs"; +import {getGlobalObject} from "../types/global.mjs"; +import {ID} from "../types/id.mjs"; +import {isString} from "../types/is.mjs"; +import {Observer} from "../types/observer.mjs"; +import {ProxyObserver} from "../types/proxyobserver.mjs"; +import {ATTRIBUTE_CLASS, ATTRIBUTE_ID, ATTRIBUTE_TITLE} from "./constants.mjs"; + +export {Resource, KEY_DOCUMENT, KEY_QUERY, referenceSymbol} + +/** + * @private + * @type {string} + */ +const KEY_DOCUMENT = 'document'; + +/** + * @private + * @type {string} + */ +const KEY_QUERY = 'query'; + +/** + * @private + * @type {string} + */ +const KEY_TIMEOUT = 'timeout'; + +/** + * @private + * @type {symbol} + */ +const referenceSymbol = Symbol('reference'); + +/** + * This class is the base class for all resources to be loaded. + * + * ``` + * <script type="module"> + * import {Resource} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/resource.mjs'; + * new Resource() + * </script> + * ``` + * + * @since 1.25.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @summary A Resource class + */ +class Resource extends BaseWithOptions { + + /** + * + * @param {Object|undefined} options + */ + constructor(options) { + super(options); + + let uri = this.getOption(this.constructor.getURLAttribute()); + + if (uri === undefined) { + throw new Error('missing source') + } else if (uri instanceof URL) { + uri = uri.toString(); + } else if (!isString(uri)) { + throw new Error('unsupported url type') + } + + this[internalSymbol][this.constructor.getURLAttribute()] = uri; + this[internalStateSymbol] = new ProxyObserver({ + loaded: false, + error: undefined, + }) + + this[referenceSymbol] = undefined; + + } + + /** + * @return {boolean} + */ + isConnected() { + + if (this[referenceSymbol] instanceof HTMLElement) { + return this[referenceSymbol].isConnected; + } + + return false; + } + + /** + * This method is overridden by the special classes and creates the DOM object. + * This method is also called implicitly, if not yet done explicitly, by calling `connect()`. + * + * @throws {Error} this method must be implemented by derived classes + * @return {Monster.DOM.Resource} + */ + create() { + throw new Error("this method must be implemented by derived classes"); + } + + /** + * This method appends the HTMLElement to the specified document. + * If the element has not yet been created, `create()` is called implicitly. + * + * throws {Error} target not found + * @return {Monster.DOM.Resource} + */ + connect() { + + if (!(this[referenceSymbol] instanceof HTMLElement)) { + this.create(); + } + + appendToDocument.call(this); + return this; + } + + /** + * @property {Document} document the document object into which the node is to be appended + * @property {string} src/href url to the corresponding resource + * @property {string} query defines the location where the resource is to be hooked into the dom. + * @property {string} id element attribute id + * @property {string} title element attribute title + * @property {string} class element attribute class + * @property {int} timeout timeout + */ + get defaults() { + return extend({}, super.defaults, { + [this.constructor.getURLAttribute()]: undefined, + [KEY_DOCUMENT]: getGlobalObject('document'), + [KEY_QUERY]: 'head', + [KEY_TIMEOUT]: 10000, + [ATTRIBUTE_ID]: (new ID('resource')).toString(), + [ATTRIBUTE_CLASS]: undefined, + [ATTRIBUTE_TITLE]: undefined + }) + } + + /** + * With `available()` you can check if a resource is available. + * This is the case when the tag is included and the resource is loaded. + * + * @return {Promise} + */ + available() { + const self = this; + if (!(self[referenceSymbol] instanceof HTMLElement)) { + return Promise.reject('no element') + } + + if (!self.isConnected()) { + return Promise.reject('element not connected') + } + + if (self[internalStateSymbol].getSubject()['loaded'] === true) { + + if (self[internalStateSymbol].getSubject()['error'] !== undefined) { + return Promise.reject(self[internalStateSymbol].getSubject()['error']); + } + + return Promise.resolve(); + + } + + return new Promise(function (resolve, reject) { + + const timeout = setTimeout(() => { + reject('timeout'); + }, self.getOption('timeout')) + + const observer = new Observer(() => { + clearTimeout(timeout); + self[internalStateSymbol].detachObserver(observer); + resolve(); + }) + + self[internalStateSymbol].attachObserver(observer); + + }); + + }; + + /** + * @return {string} + */ + static getURLAttribute() { + throw new Error("this method must be implemented by derived classes"); + } + +} + + +/** + * @private + * @return {Promise} + * throws {Error} target not found + */ +function appendToDocument() { + const self = this; + + const targetNode = document.querySelector(self.getOption(KEY_QUERY, 'head')) + if (!(targetNode instanceof HTMLElement)) { + throw new Error('target not found') + } + + addEvents.call(self); + targetNode.appendChild(self[referenceSymbol]); + + return self; +} + +/** + * @private + * @return {addEvents} + */ +function addEvents() { + const self = this; + + const onError = () => { + + self[referenceSymbol].removeEventListener('error', onError); + self[referenceSymbol].removeEventListener('load', onLoad); + + self[internalStateSymbol].setSubject({ + loaded: true, + error: self[referenceSymbol][self.constructor.getURLAttribute()] + ' is not available', + }) + + return; + } + + const onLoad = () => { + self[referenceSymbol].removeEventListener('error', onError); + self[referenceSymbol].removeEventListener('load', onLoad); + self[internalStateSymbol].getSubject()['loaded'] = true; + return; + } + + self[referenceSymbol].addEventListener('load', onLoad, false); + self[referenceSymbol].addEventListener('error', onError, false); + + return self; + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {extend} from "../data/extend.mjs"; +import {BaseWithOptions} from "../types/basewithoptions.mjs"; +import {getGlobalObject} from "../types/global.mjs"; +import {isArray} from "../types/is.mjs"; +import {ATTRIBUTE_HREF, ATTRIBUTE_SRC} from "./constants.mjs"; +import {Resource} from "./resource.mjs"; +import {Data} from "./resource/data.mjs"; +import {Stylesheet} from "./resource/link/stylesheet.mjs"; +import {Script} from "./resource/script.mjs"; + +export {ResourceManager} + +/** + * The ResourceManager is a singleton that manages all resources. + * + * ``` + * <script type="module"> + * import {Resource} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/resourcemanager.mjs'; + * new ResourceManager() + * </script> + * ``` + * + * @since 1.25.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @summary A Resource class + */ + class ResourceManager extends BaseWithOptions { + + /** + * + * @param {Object} options + * throw {Error} unsupported document type + */ + constructor(options) { + super(options); + + if (!(this.getOption('document') instanceof Document)) { + throw new Error('unsupported document type') + } + + + } + + /** + * @property {string} baseurl + */ + getBaseURL() { + this.getOption('document')?.baseURL; + } + + /** + * + * @property {HTMLDocument} document=document Document + * @property {Object} resources + * @property {Array} resources.scripts=[] array with {@link Monster.DOM.Resource.Script} objects + * @property {Array} resources.stylesheets=[] array with {@link Monster.DOM.Resource.Link.Stylesheet} objects + * @property {Array} resources.data=[] array with {@link Monster.DOM.Resource.Data} objects + */ + get defaults() { + return Object.assign({}, super.defaults, { + document: getGlobalObject('document'), + resources: { + scripts: [], + stylesheets: [], + data: [] + } + }) + } + + /** + * Append Tags to DOM + * + * @return {Monster.DOM.ResourceManager} + * @throws {Error} unsupported resource definition + */ + connect() { + runResourceMethod.call(this, 'connect'); + return this; + } + + /** + * Check if available + * + * @return {Promise} + * @throws {Error} unsupported resource definition + */ + available() { + return Promise.all(runResourceMethod.call(this, 'available')); + } + + /** + * Add a script + * + * @param {string|URL} url + * @param [Object|undefined} options + * @return {Monster.DOM.ResourceManager} + * @see Monster.DOM.Resource.Script + */ + addScript(url, options) { + return addResource.call(this, 'scripts', url, options); + } + + + /** + * Add Stylesheet + * + * @param {string|URL} url + * @param [Object|undefined} options + * @return {Monster.DOM.ResourceManager} + * @see Monster.DOM.Resource.Link.Stylesheet + */ + addStylesheet(url, options) { + return addResource.call(this, 'stylesheets', url, options); + } + + /** + * Add Data Tag + * + * @param {string|URL} url + * @param [Object|undefined} options + * @return {Monster.DOM.ResourceManager} + * @see Monster.DOM.Resource.Data + */ + addData(url, options) { + return addResource.call(this, 'data', url, options); + } + + +} + +/** + * @private + * @param {string} method + * @return {Array} + */ +function runResourceMethod(method) { + const self = this; + + const result = []; + + for (const type of ['scripts', 'stylesheets', 'data']) { + const resources = self.getOption('resources.' + type); + if (!isArray(resources)) { + continue; + } + + for (const resource of resources) { + if (!(resource instanceof Resource)) { + throw new Error('unsupported resource definition') + } + + result.push(resource[method]()); + } + + } + + return result; +} + +/** + * + * @param {string} type + * @param {string|URL} url + * @param [Object|undefined} options + * @return {Monster.DOM.ResourceManager} + * @private + */ +function addResource(type, url, options) { + const self = this; + + if (url instanceof URL) { + url = url.toString(); + } + + options = options || {} + + let resource; + switch (type) { + case 'scripts': + resource = new Script(extend({}, options, {[ATTRIBUTE_SRC]: url})) + break; + case 'stylesheets': + resource = new Stylesheet(extend({}, options, {[ATTRIBUTE_HREF]: url})) + break; + case 'data': + resource = new Data(extend({}, options, {[ATTRIBUTE_SRC]: url})) + break; + default: + throw new Error('unsupported type ' + type) + } + + (self.getOption('resources')?.[type]).push(resource); + return self; +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {getGlobal} from "../types/global.mjs"; +import {validateString} from "../types/validate.mjs"; + +export {getDocument, getWindow, getDocumentFragmentFromString} + +/** + * this method fetches the document object + * + * ``` + * <script type="module"> + * import {getDocument} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/util.mjs'; + * console.log(getDocument()) + * </script> + * ``` + * + * in nodejs this functionality can be performed with [jsdom](https://www.npmjs.com/package/jsdom). + * + * ``` + * import {JSDOM} from "jsdom" + * if (typeof window !== "object") { + * const {window} = new JSDOM('', { + * url: 'http://example.com/', + * pretendToBeVisual: true + * }); + * + * [ + * 'self', + * 'document', + * 'Document', + * 'Node', + * 'Element', + * 'HTMLElement', + * 'DocumentFragment', + * 'DOMParser', + * 'XMLSerializer', + * 'NodeFilter', + * 'InputEvent', + * 'CustomEvent' + * ].forEach(key => (getGlobal()[key] = window[key])); + * } + * ``` + * + * @returns {object} + * @since 1.6.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @throws {Error} not supported environment + */ +function getDocument() { + let document = getGlobal()?.['document']; + if (typeof document !== 'object') { + throw new Error("not supported environment") + } + + return document; +} + +/** + * this method fetches the window object + * + * ``` + * <script type="module"> + * import {getWindow} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/util.mjs'; + * console.log(getWindow(null)) + * </script> + * ``` + * + * in nodejs this functionality can be performed with [jsdom](https://www.npmjs.com/package/jsdom). + * + * ``` + * import {JSDOM} from "jsdom" + * if (typeof window !== "object") { + * const {window} = new JSDOM('', { + * url: 'http://example.com/', + * pretendToBeVisual: true + * }); + * + * getGlobal()['window']=window; + * + * [ + * 'self', + * 'document', + * 'Document', + * 'Node', + * 'Element', + * 'HTMLElement', + * 'DocumentFragment', + * 'DOMParser', + * 'XMLSerializer', + * 'NodeFilter', + * 'InputEvent', + * 'CustomEvent' + * ].forEach(key => (getGlobal()[key] = window[key])); + * } + * ``` + * + * @returns {object} + * @since 1.6.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @throws {Error} not supported environment + */ +function getWindow() { + let window = getGlobal()?.['window']; + if (typeof window !== 'object') { + throw new Error("not supported environment") + } + + return window; +} + + +/** + * this method fetches the document object + * + * ``` + * <script type="module"> + * import {getDocumentFragmentFromString} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/util.mjs'; + * console.log(getDocumentFragmentFromString('<div></div>')) + * </script> + * ``` + * + * in nodejs this functionality can be performed with [jsdom](https://www.npmjs.com/package/jsdom). + * + * ``` + * import {JSDOM} from "jsdom" + * if (typeof window !== "object") { + * const {window} = new JSDOM('', { + * url: 'http://example.com/', + * pretendToBeVisual: true + * }); + * + * [ + * 'self', + * 'document', + * 'Document', + * 'Node', + * 'Element', + * 'HTMLElement', + * 'DocumentFragment', + * 'DOMParser', + * 'XMLSerializer', + * 'NodeFilter', + * 'InputEvent', + * 'CustomEvent' + * ].forEach(key => (getGlobal()[key] = window[key])); + * } + * ``` + * + * @returns {DocumentFragment} + * @since 1.6.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @throws {Error} not supported environment + * @throws {TypeError} value is not a string + */ +function getDocumentFragmentFromString(html) { + validateString(html); + + const document = getDocument(); + const template = document.createElement('template'); + template.innerHTML = html; + + return template.content; +} +'use strict'; + + +/** + * @author schukai GmbH + */ + +export { + DEFAULT_THEME, + ATTRIBUTE_PREFIX, + ATTRIBUTE_OPTIONS, + ATTRIBUTE_OPTIONS_SELECTOR, + ATTRIBUTE_THEME_PREFIX, + ATTRIBUTE_THEME_NAME, + ATTRIBUTE_UPDATER_ATTRIBUTES, + ATTRIBUTE_UPDATER_SELECT_THIS, + ATTRIBUTE_UPDATER_REPLACE, + ATTRIBUTE_UPDATER_INSERT, + ATTRIBUTE_UPDATER_INSERT_REFERENCE, + ATTRIBUTE_UPDATER_REMOVE, + ATTRIBUTE_UPDATER_BIND, + ATTRIBUTE_TEMPLATE_PREFIX, + ATTRIBUTE_ROLE, + ATTRIBUTE_DISABLED, + ATTRIBUTE_VALUE, + ATTRIBUTE_OBJECTLINK, + ATTRIBUTE_ERRORMESSAGE, + TAG_SCRIPT, + TAG_STYLE, + TAG_LINK, + ATTRIBUTE_ID, + ATTRIBUTE_CLASS, + ATTRIBUTE_TITLE, + ATTRIBUTE_SRC, + ATTRIBUTE_HREF, + ATTRIBUTE_TYPE, + ATTRIBUTE_NONCE, + ATTRIBUTE_TRANSLATE, + ATTRIBUTE_TABINDEX, + ATTRIBUTE_SPELLCHECK, + ATTRIBUTE_SLOT, + ATTRIBUTE_PART, + ATTRIBUTE_LANG, + ATTRIBUTE_ITEMTYPE, + ATTRIBUTE_ITEMSCOPE, + ATTRIBUTE_ITEMREF, + ATTRIBUTE_ITEMID, + ATTRIBUTE_ITEMPROP, + ATTRIBUTE_IS, + ATTRIBUTE_INPUTMODE, + ATTRIBUTE_ACCESSKEY, + ATTRIBUTE_AUTOCAPITALIZE, + ATTRIBUTE_AUTOFOCUS, + ATTRIBUTE_CONTENTEDITABLE, + ATTRIBUTE_DIR, + ATTRIBUTE_DRAGGABLE, + ATTRIBUTE_ENTERKEYHINT, + ATTRIBUTE_EXPORTPARTS, + ATTRIBUTE_HIDDEN, + objectUpdaterLinkSymbol, + +} + +/** + * default theme + * @memberOf Monster.DOM + * @type {string} + */ +const DEFAULT_THEME = 'monster'; + +/** + * @memberOf Monster.DOM + * @since 1.8.0 + * @type {string} + */ +const ATTRIBUTE_PREFIX = 'data-monster-'; + +/** + * This is the name of the attribute to pass options to a control + * + * @memberOf Monster.DOM + * @since 1.8.0 + * @type {string} + */ +const ATTRIBUTE_OPTIONS = ATTRIBUTE_PREFIX + 'options'; + +/** + * This is the name of the attribute to pass options to a control + * + * @memberOf Monster.DOM + * @since 1.30.0 + * @type {string} + */ +const ATTRIBUTE_OPTIONS_SELECTOR = ATTRIBUTE_PREFIX + 'options-selector'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.8.0 + */ +const ATTRIBUTE_THEME_PREFIX = ATTRIBUTE_PREFIX + 'theme-'; + +/** + * @memberOf Monster.DOM + * @type {string} + */ +const ATTRIBUTE_THEME_NAME = ATTRIBUTE_THEME_PREFIX + 'name'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.8.0 + */ +const ATTRIBUTE_UPDATER_ATTRIBUTES = ATTRIBUTE_PREFIX + 'attributes'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.27.1 + */ +const ATTRIBUTE_UPDATER_SELECT_THIS = ATTRIBUTE_PREFIX + 'select-this'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.8.0 + */ +const ATTRIBUTE_UPDATER_REPLACE = ATTRIBUTE_PREFIX + 'replace'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.8.0 + */ +const ATTRIBUTE_UPDATER_INSERT = ATTRIBUTE_PREFIX + 'insert'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.8.0 + */ +const ATTRIBUTE_UPDATER_INSERT_REFERENCE = ATTRIBUTE_PREFIX + 'insert-reference'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.8.0 + */ +const ATTRIBUTE_UPDATER_REMOVE = ATTRIBUTE_PREFIX + 'remove'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.9.0 + */ +const ATTRIBUTE_UPDATER_BIND = ATTRIBUTE_PREFIX + 'bind'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.27.0 + */ +const ATTRIBUTE_TEMPLATE_PREFIX = ATTRIBUTE_PREFIX + 'template-prefix'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.14.0 + */ +const ATTRIBUTE_ROLE = ATTRIBUTE_PREFIX + 'role'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.24.0 + */ +const ATTRIBUTE_DISABLED = 'disabled'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.24.0 + */ +const ATTRIBUTE_VALUE = 'value'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.9.0 + */ +const ATTRIBUTE_OBJECTLINK = ATTRIBUTE_PREFIX + 'objectlink'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.24.0 + */ +const ATTRIBUTE_ERRORMESSAGE = ATTRIBUTE_PREFIX + 'error'; + +/** + * @memberOf Monster.DOM + * @type {symbol} + * @since 1.24.0 + */ +const objectUpdaterLinkSymbol = Symbol('monsterUpdater'); + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const TAG_SCRIPT = 'script'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const TAG_STYLE = 'style'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const TAG_LINK = 'link'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ + +const ATTRIBUTE_ID = 'id'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ + +const ATTRIBUTE_CLASS = 'class'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_TITLE = 'title'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_SRC = 'src'; +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_HREF = 'href'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_TYPE = 'type'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_NONCE = 'nonce'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_TRANSLATE = 'translate'; + + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_TABINDEX = 'tabindex'; + + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_SPELLCHECK = 'spellcheck'; + + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_SLOT = 'slot'; + + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_PART = 'part'; + + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_LANG = 'lang'; + + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_ITEMTYPE = 'itemtype'; + + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_ITEMSCOPE = 'itemscope'; + + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_ITEMREF = 'itemref'; + + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_ITEMID = 'itemid'; + + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_ITEMPROP = 'itemprop'; + + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_IS = 'is'; + + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_INPUTMODE = 'inputmode'; + + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_ACCESSKEY = 'accesskey'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_AUTOCAPITALIZE = 'autocapitalize'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_AUTOFOCUS = 'autofocus'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_CONTENTEDITABLE = 'contenteditable'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_DIR = 'dir'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_DRAGGABLE = 'draggable'; + + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_ENTERKEYHINT = 'enterkeyhint'; +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_EXPORTPARTS = 'exportparts'; +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_HIDDEN = 'hidden'; +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../constants.mjs"; +import {extend} from "../data/extend.mjs"; +import {Pathfinder} from "../data/pathfinder.mjs"; + +import {parseDataURL} from "../types/dataurl.mjs"; +import {getGlobalObject} from "../types/global.mjs"; +import {isArray, isFunction, isObject, isString} from "../types/is.mjs"; +import {Observer} from "../types/observer.mjs"; +import {ProxyObserver} from "../types/proxyobserver.mjs"; +import {validateFunction, validateInstance, validateObject, validateString} from "../types/validate.mjs"; +import {clone} from "../util/clone.mjs"; +import {addAttributeToken, addToObjectLink, getLinkedObjects, hasObjectLink} from "./attributes.mjs"; +import { + ATTRIBUTE_DISABLED, + ATTRIBUTE_ERRORMESSAGE, + ATTRIBUTE_OPTIONS, + ATTRIBUTE_OPTIONS_SELECTOR, + objectUpdaterLinkSymbol +} from "./constants.mjs"; +import {findDocumentTemplate, Template} from "./template.mjs"; +import {Updater} from "./updater.mjs"; + +export {CustomElement, initMethodSymbol, assembleMethodSymbol, attributeObserverSymbol, registerCustomElement, assignUpdaterToElement} + +/** + * @memberOf Monster.DOM + * @type {symbol} + */ +const initMethodSymbol = Symbol('initMethodSymbol'); + +/** + * @memberOf Monster.DOM + * @type {symbol} + */ +const assembleMethodSymbol = Symbol('assembleMethodSymbol'); + +/** + * this symbol holds the attribute observer callbacks. The key is the attribute name. + * @memberOf Monster.DOM + * @type {symbol} + */ +const attributeObserverSymbol = Symbol('attributeObserver'); + + +/** + * HTMLElement + * @external HTMLElement + * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement + * + * @startuml customelement-sequencediagram.png + * skinparam monochrome true + * skinparam shadowing false + * + * autonumber + * + * Script -> DOM: element = document.createElement('my-element') + * DOM -> CustomElement: constructor() + * CustomElement -> CustomElement: [initMethodSymbol]() + * + * CustomElement --> DOM: Element + * DOM --> Script : element + * + * + * Script -> DOM: document.querySelector('body').append(element) + * + * DOM -> CustomElement : connectedCallback() + * + * note right CustomElement: is only called at\nthe first connection + * CustomElement -> CustomElement : [assembleMethodSymbol]() + * + * ... ... + * + * autonumber + * + * Script -> DOM: document.querySelector('monster-confirm-button').parentNode.removeChild(element) + * DOM -> CustomElement: disconnectedCallback() + * + * + * @enduml + * + * @startuml customelement-class.png + * skinparam monochrome true + * skinparam shadowing false + * HTMLElement <|-- CustomElement + * @enduml + */ + + +/** + * To define a new HTML element we need the power of CustomElement + * + * IMPORTANT: after defining a `CustomElement`, the `registerCustomElement` method must be called + * with the new class name. only then will the tag defined via the `getTag` method be made known to the DOM. + * + * <img src="./images/customelement-class.png"> + * + * You can create the object via the function `document.createElement()`. + * + * ``` + * <script type="module"> + * import {Monster} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source//monster.mjs'; + * document.createElement('monster-') + * </script> + * ``` + * + * ## Interaction + * + * <img src="./images/customelement-sequencediagram.png"> + * + * ## Styling + * + * For optimal display of custom-elements the pseudo-class :defined can be used. + * + * To prevent the custom elements from being displayed and flickering until the control is registered, it is recommended to create a css directive. + * + * In the simplest case, you can simply hide the control. + * + * ``` + * <style> + * + * my-custom-element:not(:defined) { + * display: none; + * } + * + * my-custom-element:defined { + * display: flex; + * } + * + * </style> + * ``` + * + * Alternatively you can also display a loader + * + * ``` + * my-custom-element:not(:defined) { + * display: flex; + * box-shadow: 0 4px 10px 0 rgba(33, 33, 33, 0.15); + * border-radius: 4px; + * height: 200px; + * position: relative; + * overflow: hidden; + * } + * + * my-custom-element:not(:defined)::before { + * content: ''; + * display: block; + * position: absolute; + * left: -150px; + * top: 0; + * height: 100%; + * width: 150px; + * background: linear-gradient(to right, transparent 0%, #E8E8E8 50%, transparent 100%); + * animation: load 1s cubic-bezier(0.4, 0.0, 0.2, 1) infinite; + * } + * + * @keyframes load { + * from { + * left: -150px; + * } + * to { + * left: 100%; + * } + * } + * + * my-custom-element:defined { + * display: flex; + * } + * ``` + * + * @example + * + * // In the example the the user can use his own template by creating a template in the DOM with the ID `my-custom-element`. + * // You can also specify a theme (for example `mytheme`), then it will search for the ID `my-custom-element-mytheme` and + * // if not available for the ID `my-custom-element`. + * + * class MyCustomElement extends CustomElement { + * + * static getTag() { + * return "my-custom-element" + * } + * + * } + * + * // ↦ <my-custom-element></my-custom-element> + * + * @see https://github.com/WICG/webcomponents + * @see https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements + * @since 1.7.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @extends external:HTMLElement + * @summary A base class for HTML5 customcontrols + */ +class CustomElement extends HTMLElement { + + /** + * A new object is created. First the `initOptions` method is called. Here the + * options can be defined in derived classes. Subsequently, the shadowRoot is initialized. + * + * @throws {Error} the options attribute does not contain a valid json definition. + * @since 1.7.0 + */ + constructor() { + super(); + this[internalSymbol] = new ProxyObserver({'options': extend({}, this.defaults)}); + this[attributeObserverSymbol] = {}; + initOptionObserver.call(this); + this[initMethodSymbol](); + } + + /** + * This method determines which attributes are to be monitored by `attributeChangedCallback()`. + * + * @return {string[]} + * @since 1.15.0 + */ + static get observedAttributes() { + return [ATTRIBUTE_OPTIONS, ATTRIBUTE_DISABLED]; + } + + /** + * Derived classes can override and extend this method as follows. + * + * ``` + * get defaults() { + * return Object.assign({}, super.defaults, { + * myValue:true + * }); + * } + * ``` + * + * To set the options via the html tag the attribute data-monster-options must be set. + * As value a JSON object with the desired values must be defined. + * + * Since 1.18.0 the JSON can be specified as a DataURI. + * + * ``` + * new Monster.Types.DataUrl(btoa(JSON.stringify({ + * shadowMode: 'open', + * delegatesFocus: true, + * templates: { + * main: undefined + * } + * })),'application/json',true).toString() + * ``` + * + * The attribute data-monster-options-selector can be used to access a script tag that contains additional configuration. + * + * As value a selector must be specified, which belongs to a script tag and contains the configuration as json. + * + * ``` + * <script id="id-for-this-config" type="application/json"> + * { + * "config-key": "config-value" + * } + * </script> + * ``` + * + * The individual configuration values can be found in the table. + * + * @property {boolean} disabled=false Object The Boolean disabled attribute, when present, makes the element not mutable, focusable, or even submitted with the form. + * @property {string} shadowMode=open `open` Elements of the shadow root are accessible from JavaScript outside the root, for example using. `close` Denies access to the node(s) of a closed shadow root from JavaScript outside it + * @property {Boolean} delegatesFocus=true A boolean that, when set to true, specifies behavior that mitigates custom element issues around focusability. When a non-focusable part of the shadow DOM is clicked, the first focusable part is given focus, and the shadow host is given any available :focus styling. + * @property {Object} templates Templates + * @property {string} templates.main=undefined Main template + * + * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/attachShadow + * @since 1.8.0 + */ + get defaults() { + return { + ATTRIBUTE_DISABLED: this.getAttribute(ATTRIBUTE_DISABLED), + shadowMode: 'open', + delegatesFocus: true, + templates: { + main: undefined + } + }; + } + + /** + * There is no check on the name by this class. the developer is responsible for assigning an appropriate tag. + * if the name is not valid, registerCustomElement() will issue an error + * + * @link https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name + * @return {string} + * @throws {Error} the method getTag must be overwritten by the derived class. + * @since 1.7.0 + */ + static getTag() { + throw new Error("the method getTag must be overwritten by the derived class."); + } + + /** + * At this point a `CSSStyleSheet` object can be returned. If the environment does not + * support a constructor, then an object can also be built using the following detour. + * + * If `undefined` is returned then the shadowRoot does not get a stylesheet. + * + * ``` + * const doc = document.implementation.createHTMLDocument('title'); + * + * let style = doc.createElement("style"); + * style.innerHTML="p{color:red;}"; + * + * // WebKit Hack + * style.appendChild(document.createTextNode("")); + * // Add the <style> element to the page + * doc.head.appendChild(style); + * return doc.styleSheets[0]; + * ; + * ``` + * + * @return {CSSStyleSheet|CSSStyleSheet[]|string|undefined} + */ + static getCSSStyleSheet() { + return undefined; + } + + /** + * attach a new observer + * + * @param {Observer} observer + * @returns {CustomElement} + */ + attachObserver(observer) { + this[internalSymbol].attachObserver(observer) + return this; + } + + /** + * detach a observer + * + * @param {Observer} observer + * @returns {CustomElement} + */ + detachObserver(observer) { + this[internalSymbol].detachObserver(observer) + return this; + } + + /** + * @param {Observer} observer + * @returns {ProxyObserver} + */ + containsObserver(observer) { + return this[internalSymbol].containsObserver(observer) + } + + /** + * nested options can be specified by path `a.b.c` + * + * @param {string} path + * @param {*} defaultValue + * @return {*} + * @since 1.10.0 + */ + getOption(path, defaultValue) { + let value; + + try { + value = new Pathfinder(this[internalSymbol].getRealSubject()['options']).getVia(path); + } catch (e) { + + } + + if (value === undefined) return defaultValue; + return value; + } + + /** + * Set option and inform elements + * + * @param {string} path + * @param {*} value + * @return {CustomElement} + * @since 1.14.0 + */ + setOption(path, value) { + new Pathfinder(this[internalSymbol].getSubject()['options']).setVia(path, value); + return this; + } + + /** + * @since 1.15.0 + * @param {string|object} options + * @return {CustomElement} + */ + setOptions(options) { + + if (isString(options)) { + options = parseOptionsJSON.call(this, options) + } + + const self = this; + extend(self[internalSymbol].getSubject()['options'], self.defaults, options); + + return self; + } + + /** + * Is called once via the constructor + * + * @return {CustomElement} + * @since 1.8.0 + */ + [initMethodSymbol]() { + return this; + } + + /** + * Is called once when the object is included in the DOM for the first time. + * + * @return {CustomElement} + * @since 1.8.0 + */ + [assembleMethodSymbol]() { + + const self = this; + let elements, nodeList; + + const AttributeOptions = getOptionsFromAttributes.call(self); + if (isObject(AttributeOptions) && Object.keys(AttributeOptions).length > 0) { + self.setOptions(AttributeOptions); + } + + const ScriptOptions = getOptionsFromScriptTag.call(self); + if (isObject(ScriptOptions) && Object.keys(ScriptOptions).length > 0) { + self.setOptions(ScriptOptions); + } + + + if (self.getOption('shadowMode', false) !== false) { + try { + initShadowRoot.call(self); + elements = self.shadowRoot.childNodes; + + } catch (e) { + + } + + try { + initCSSStylesheet.call(this); + } catch (e) { + addAttributeToken(self, ATTRIBUTE_ERRORMESSAGE, e.toString()); + } + } + + if (!(elements instanceof NodeList)) { + if (!(elements instanceof NodeList)) { + initHtmlContent.call(this); + elements = this.childNodes; + } + } + + try { + nodeList = new Set([ + ...elements, + ...getSlottedElements.call(self) + ]) + } catch (e) { + nodeList = elements + } + + assignUpdaterToElement.call(self, nodeList, clone(self[internalSymbol].getRealSubject()['options'])); + return self; + } + + /** + * Called every time the element is inserted into the DOM. Useful for running setup code, such as + * fetching resources or rendering. Generally, you should try to delay work until this time. + * + * @return {void} + * @since 1.7.0 + */ + connectedCallback() { + let self = this; + if (!hasObjectLink(self, objectUpdaterLinkSymbol)) { + self[assembleMethodSymbol]() + } + } + + /** + * Called every time the element is removed from the DOM. Useful for running clean up code. + * + * @return {void} + * @since 1.7.0 + */ + disconnectedCallback() { + + } + + /** + * The custom element has been moved into a new document (e.g. someone called document.adoptNode(el)). + * + * @return {void} + * @since 1.7.0 + */ + adoptedCallback() { + + } + + /** + * Called when an observed attribute has been added, removed, updated, or replaced. Also called for initial + * values when an element is created by the parser, or upgraded. Note: only attributes listed in the observedAttributes + * property will receive this callback. + * + * @param {string} attrName + * @param {string} oldVal + * @param {string} newVal + * @return {void} + * @since 1.15.0 + */ + attributeChangedCallback(attrName, oldVal, newVal) { + const self = this; + + const callback = self[attributeObserverSymbol]?.[attrName]; + + if (isFunction(callback)) { + callback.call(self, newVal, oldVal); + } + + } + + /** + * + * @param {Node} node + * @return {boolean} + * @throws {TypeError} value is not an instance of + * @since 1.19.0 + */ + hasNode(node) { + const self = this; + + if (containChildNode.call(self, validateInstance(node, Node))) { + return true; + } + + if (!(self.shadowRoot instanceof ShadowRoot)) { + return false; + } + + return containChildNode.call(self.shadowRoot, node); + + } + +} + +/** + * @private + * @param {String|undefined} query + * @param {String|undefined|null} name name of the slot (if the parameter is undefined, all slots are searched, if the parameter has the value null, all slots without a name are searched. if a string is specified, the slots with this name are searched.) + * @return {*} + * @this CustomElement + * @since 1.23.0 + * @throws {Error} query must be a string + */ +function getSlottedElements(query, name) { + const self = this; + const result = new Set; + + if (!(self.shadowRoot instanceof ShadowRoot)) { + return result; + } + + let selector = 'slot'; + if (name !== undefined) { + if (name === null) { + selector += ':not([name])'; + } else { + selector += '[name=' + validateString(name) + ']'; + } + + } + + const slots = self.shadowRoot.querySelectorAll(selector); + + for (const [, slot] of Object.entries(slots)) { + slot.assignedElements().forEach(function (node) { + + if (!(node instanceof HTMLElement)) return; + + if (isString(query)) { + node.querySelectorAll(query).forEach(function (n) { + result.add(n); + }); + + if (node.matches(query)) { + result.add(node); + } + + } else if (query !== undefined) { + throw new Error('query must be a string') + } else { + result.add(node); + } + }) + } + + return result; +} + +/** + * @this CustomElement + * @private + * @param {Node} node + * @return {boolean} + */ +function containChildNode(node) { + const self = this; + + if (self.contains(node)) { + return true; + } + + for (const [, e] of Object.entries(self.childNodes)) { + if (e.contains(node)) { + return true; + } + + containChildNode.call(e, node); + } + + + return false; +} + +/** + * @since 1.15.0 + * @private + * @this CustomElement + */ +function initOptionObserver() { + const self = this; + + let lastDisabledValue = undefined; + self.attachObserver(new Observer(function () { + const flag = self.getOption('disabled'); + + if (flag === lastDisabledValue) { + return; + } + + lastDisabledValue = flag; + + if (!(self.shadowRoot instanceof ShadowRoot)) { + return; + } + + const query = 'button, command, fieldset, keygen, optgroup, option, select, textarea, input, [data-monster-objectlink]'; + const elements = self.shadowRoot.querySelectorAll(query); + + let nodeList; + try { + nodeList = new Set([ + ...elements, + ...getSlottedElements.call(self, query) + ]) + } catch (e) { + nodeList = elements + } + + for (const element of [...nodeList]) { + if (flag === true) { + element.setAttribute(ATTRIBUTE_DISABLED, ''); + } else { + element.removeAttribute(ATTRIBUTE_DISABLED); + } + } + + })); + + self.attachObserver(new Observer(function () { + + // not initialised + if (!hasObjectLink(self, objectUpdaterLinkSymbol)) { + return; + } + // inform every element + const updaters = getLinkedObjects(self, objectUpdaterLinkSymbol); + + for (const list of updaters) { + for (const updater of list) { + let d = clone(self[internalSymbol].getRealSubject()['options']); + Object.assign(updater.getSubject(), d); + } + } + + })); + + // disabled + self[attributeObserverSymbol][ATTRIBUTE_DISABLED] = () => { + if (self.hasAttribute(ATTRIBUTE_DISABLED)) { + self.setOption(ATTRIBUTE_DISABLED, true); + } else { + self.setOption(ATTRIBUTE_DISABLED, undefined); + } + } + + // data-monster-options + self[attributeObserverSymbol][ATTRIBUTE_OPTIONS] = () => { + const options = getOptionsFromAttributes.call(self); + if (isObject(options) && Object.keys(options).length > 0) { + self.setOptions(options); + } + } + + // data-monster-options-selector + self[attributeObserverSymbol][ATTRIBUTE_OPTIONS_SELECTOR] = () => { + const options = getOptionsFromScriptTag.call(self); + if (isObject(options) && Object.keys(options).length > 0) { + self.setOptions(options); + } + } + + +} + +/** + * @private + * @return {object} + * @throws {TypeError} value is not a object + */ +function getOptionsFromScriptTag() { + const self = this; + + if (!self.hasAttribute(ATTRIBUTE_OPTIONS_SELECTOR)) { + return {}; + } + + const node = document.querySelector(self.getAttribute(ATTRIBUTE_OPTIONS_SELECTOR)); + if (!(node instanceof HTMLScriptElement)) { + addAttributeToken(self, ATTRIBUTE_ERRORMESSAGE, 'the selector ' + ATTRIBUTE_OPTIONS_SELECTOR + ' for options was specified (' + self.getAttribute(ATTRIBUTE_OPTIONS_SELECTOR) + ') but not found.'); + return {}; + } + + let obj = {}; + + try { + obj = parseOptionsJSON.call(this, node.textContent.trim()) + } catch (e) { + addAttributeToken(self, ATTRIBUTE_ERRORMESSAGE, 'when analyzing the configuration from the script tag there was an error. ' + e); + } + + return obj; + +} + +/** + * @private + * @return {object} + */ +function getOptionsFromAttributes() { + const self = this; + + if (this.hasAttribute(ATTRIBUTE_OPTIONS)) { + try { + return parseOptionsJSON.call(self, this.getAttribute(ATTRIBUTE_OPTIONS)) + } catch (e) { + addAttributeToken(self, ATTRIBUTE_ERRORMESSAGE, 'the options attribute ' + ATTRIBUTE_OPTIONS + ' does not contain a valid json definition (actual: ' + this.getAttribute(ATTRIBUTE_OPTIONS) + ').' + e); + } + } + + return {}; +} + +/** + * @private + * @param data + * @return {Object} + */ +function parseOptionsJSON(data) { + + const self = this, obj = {}; + + if (!isString(data)) { + return obj; + } + + // the configuration can be specified as a data url. + try { + let dataUrl = parseDataURL(data); + data = dataUrl.content; + } catch (e) { + + } + + try { + let obj = JSON.parse(data); + return validateObject(obj); + } catch (e) { + throw e; + } + + + return obj; +} + +/** + * @private + * @return {initHtmlContent} + */ +function initHtmlContent() { + + try { + let template = findDocumentTemplate(this.constructor.getTag()); + this.appendChild(template.createDocumentFragment()); + } catch (e) { + + let html = this.getOption('templates.main', ''); + if (isString(html) && html.length > 0) { + this.innerHTML = html; + } + + } + + return this; + +} + +/** + * @private + * @return {CustomElement} + * @memberOf Monster.DOM + * @this CustomElement + * @since 1.16.0 + * @throws {TypeError} value is not an instance of + */ +function initCSSStylesheet() { + const self = this; + + if (!(this.shadowRoot instanceof ShadowRoot)) { + return self; + } + + const styleSheet = this.constructor.getCSSStyleSheet(); + + if (styleSheet instanceof CSSStyleSheet) { + if (styleSheet.cssRules.length > 0) { + this.shadowRoot.adoptedStyleSheets = [styleSheet]; + } + } else if (isArray(styleSheet)) { + const assign = []; + for (let s of styleSheet) { + + if (isString(s)) { + let trimedStyleSheet = s.trim() + if (trimedStyleSheet !== '') { + const style = document.createElement('style') + style.innerHTML = trimedStyleSheet; + self.shadowRoot.prepend(style); + } + continue; + } + + validateInstance(s, CSSStyleSheet); + + if (s.cssRules.length > 0) { + assign.push(s); + } + + } + + if (assign.length > 0) { + this.shadowRoot.adoptedStyleSheets = assign; + } + + } else if (isString(styleSheet)) { + + let trimedStyleSheet = styleSheet.trim() + if (trimedStyleSheet !== '') { + const style = document.createElement('style') + style.innerHTML = styleSheet; + self.shadowRoot.prepend(style); + } + + } + + return self; + +} + +/** + * @private + * @return {CustomElement} + * @throws {Error} html is not set. + * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/attachShadow + * @memberOf Monster.DOM + * @since 1.8.0 + */ +function initShadowRoot() { + + let template, html; + + try { + template = findDocumentTemplate(this.constructor.getTag()); + } catch (e) { + + html = this.getOption('templates.main', ''); + if (!isString(html) || html === undefined || html === "") { + throw new Error("html is not set."); + } + + } + + this.attachShadow({ + mode: this.getOption('shadowMode', 'open'), + delegatesFocus: this.getOption('delegatesFocus', true) + }); + + if (template instanceof Template) { + this.shadowRoot.appendChild(template.createDocumentFragment()); + return this; + } + + this.shadowRoot.innerHTML = html; + return this; +} + +/** + * This method registers a new element. The string returned by `CustomElement.getTag()` is used as the tag. + * + * @param {CustomElement} element + * @return {void} + * @since 1.7.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @throws {DOMException} Failed to execute 'define' on 'CustomElementRegistry': is not a valid custom element name + */ +function registerCustomElement(element) { + validateFunction(element); + getGlobalObject('customElements').define(element.getTag(), element); +} + + +/** + * + * @param element + * @param object + * @return {Promise[]} + * @since 1.23.0 + * @memberOf Monster.DOM + */ +function assignUpdaterToElement(elements, object) { + + const updaters = new Set; + + if (elements instanceof NodeList) { + elements = new Set([ + ...elements + ]) + } + + let result = []; + + elements.forEach((element) => { + if (!(element instanceof HTMLElement)) return; + if ((element instanceof HTMLTemplateElement)) return; + + const u = new Updater(element, object) + updaters.add(u); + + result.push(u.run().then(() => { + return u.enableEventProcessing(); + })); + + }); + + if (updaters.size > 0) { + addToObjectLink(this, objectUpdaterLinkSymbol, updaters); + } + + return result; +} +'use strict'; + +/** + * In this namespace you will find classes and methods for handling the DOM. + * + * @namespace Monster.DOM + * @memberOf Monster + * @author schukai GmbH + */ +const ns = {};'use strict'; + +/** + * @author schukai GmbH + */ + + +import {getGlobalFunction} from "../types/global.mjs"; +import {TokenList} from "../types/tokenlist.mjs"; +import {validateInstance, validateString, validateSymbol} from "../types/validate.mjs"; +import {ATTRIBUTE_OBJECTLINK} from "./constants.mjs"; + +export { + findClosestObjectLink, + addToObjectLink, + removeObjectLink, + hasObjectLink, + getLinkedObjects, + toggleAttributeToken, + addAttributeToken, + removeAttributeToken, + containsAttributeToken, + replaceAttributeToken, + clearAttributeTokens, + findClosestByAttribute, + findClosestByClass +} + +/** + * Get the closest object link of a node + * + * if a node is specified without a object link, a recursive search upwards is performed until the corresponding + * object link is found, or undefined is returned. + * + * ``` + * <script type="module"> + * import {getUpdaterFromNode} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/updater.mjs'; + * console.log(findClosestObjectLink()) + * </script> + * ``` + * + * @param {HTMLElement} element + * @return {HTMLElement|undefined} + * @since 1.10.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @throws {TypeError} value is not an instance of HTMLElement + */ +function findClosestObjectLink(element) { + return findClosestByAttribute(element, ATTRIBUTE_OBJECTLINK); +} + +/** + * Adds a class attribute to an element. + * + * ``` + * <script type="module"> + * import {addToObjectLink} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/attributes.mjs'; + * addToObjectLink(); + * </script> + * ``` + * + * @since 1.9.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @param {HTMLElement} element + * @param {Symbol} symbol + * @param {Object} object + * @return {boolean} + */ +function addToObjectLink(element, symbol, object) { + + validateInstance(element, HTMLElement); + validateSymbol(symbol) + + if (element?.[symbol] === undefined) { + element[symbol] = new Set; + } + + addAttributeToken(element, ATTRIBUTE_OBJECTLINK, symbol.toString()); + element[symbol].add(object); + return element; + +} + +/** + * Removes an object from an element + * + * ``` + * <script type="module"> + * import {removeObjectLink} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/attributes.mjs'; + * removeObjectLink(); + * </script> + * ``` + * + * @since 1.9.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @param {HTMLElement} element + * @param {Symbol} symbol + * @return {boolean} + */ +function removeObjectLink(element, symbol) { + + validateInstance(element, HTMLElement); + validateSymbol(symbol) + + if (element?.[symbol] === undefined) { + return element + } + + removeAttributeToken(element, ATTRIBUTE_OBJECTLINK, symbol.toString()); + delete element[symbol]; + return element; + +} + + +/** + * Checks if an element has an object link + * + * ``` + * <script type="module"> + * import {hasObjectLink} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/attributes.mjs'; + * hasObjectLink(); + * </script> + * ``` + * + * @since 1.9.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @param {HTMLElement} element + * @param {Symbol} symbol + * @return {boolean} + */ +function hasObjectLink(element, symbol) { + + validateInstance(element, HTMLElement); + validateSymbol(symbol) + + if (element?.[symbol] === undefined) { + return false + } + + return containsAttributeToken(element, ATTRIBUTE_OBJECTLINK, symbol.toString()); + +} + +/** + * The ObjectLink can be used to attach objects to HTMLElements. The elements are kept in a set under a unique + * symbol and can be read via an iterator {@see {@link getLinkedObjects}}. + * + * In addition, elements with an objectLink receive the attribute `data-monster-objectlink`. + * + * With the method {@see {@link addToObjectLink}} the objects can be added. + * + * ``` + * <script type="module"> + * import {getLinkedObjects} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/attributes.mjs'; + * getLinkedObjects(); + * </script> + * ``` + * + * @since 1.9.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @param {HTMLElement} element + * @param {Symbol} symbol + * @return {Iterator} + * @throws {Error} there is no object link for symbol + */ +function getLinkedObjects(element, symbol) { + + validateInstance(element, HTMLElement); + validateSymbol(symbol) + + if (element?.[symbol] === undefined) { + throw new Error('there is no object link for ' + symbol.toString()); + } + + return element?.[symbol][Symbol.iterator](); + +} + + +/** + * With this method tokens in an attribute can be switched on or off. For example, classes can be switched on and off in the elements class attribute. + * + * Tokens are always separated by a space. + * + * ``` + * <script type="module"> + * import {toggleAttributeToken} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/attributes.mjs'; + * toggleAttributeToken(); + * </script> + * ``` + * + * @since 1.9.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @param {HTMLElement} element + * @param {string} key + * @param {string} token + * @return {HTMLElement} + */ +function toggleAttributeToken(element, key, token) { + validateInstance(element, HTMLElement); + validateString(token) + validateString(key) + + if (!element.hasAttribute(key)) { + element.setAttribute(key, token); + return element; + } + + element.setAttribute(key, new TokenList(element.getAttribute(key)).toggle(token).toString()); + + return element +} + +/** + * This method can be used to add a token to an attribute. Tokens are always separated by a space. + * + * ``` + * <script type="module"> + * import {addAttributeToken} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/attributes.mjs'; + * addAttributeToken(); + * </script> + * ``` + * + * @since 1.9.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @param {HTMLElement} element + * @param {string} key + * @param {string} token + * @return {HTMLElement} + */ +function addAttributeToken(element, key, token) { + validateInstance(element, HTMLElement); + validateString(token) + validateString(key) + + if (!element.hasAttribute(key)) { + element.setAttribute(key, token); + return element; + } + + element.setAttribute(key, new TokenList(element.getAttribute(key)).add(token).toString()); + + return element +} + +/** + * This function can be used to remove tokens from an attribute. + * + * Tokens are always separated by a space. + * + * ``` + * <script type="module"> + * import {removeAttributeToken} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/attributes.mjs'; + * removeAttributeToken(); + * </script> + * ``` + * + * @since 1.9.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @param {HTMLElement} element + * @param {string} key + * @param {string} token + * @return {HTMLElement} + */ +function removeAttributeToken(element, key, token) { + validateInstance(element, HTMLElement); + validateString(token) + validateString(key) + + if (!element.hasAttribute(key)) { + return element; + } + + element.setAttribute(key, new TokenList(element.getAttribute(key)).remove(token).toString()); + + return element +} + +/** + * This method can be used to determine whether an attribute has a token. + * + * Tokens are always separated by a space. + * + * ``` + * <script type="module"> + * import {containsAttributeToken} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/attributes.mjs'; + * containsAttributeToken(); + * </script> + * ``` + * + * @since 1.9.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @param {HTMLElement} element + * @param {string} key + * @param {string} token + * @return {boolean} + */ +function containsAttributeToken(element, key, token) { + validateInstance(element, HTMLElement); + validateString(token) + validateString(key) + + if (!element.hasAttribute(key)) { + return false; + } + + return new TokenList(element.getAttribute(key)).contains(token); + +} + +/** + * Tokens are always separated by a space. + * + * ``` + * <script type="module"> + * import {replaceAttributeToken} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/attributes.mjs'; + * replaceAttributeToken(); + * </script> + * ``` + * + * @since 1.9.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @param {HTMLElement} element + * @param {string} key + * @param {string} from + * @param {string} to + * @return {HTMLElement} + */ +function replaceAttributeToken(element, key, from, to) { + validateInstance(element, HTMLElement); + validateString(from) + validateString(to) + validateString(key) + + if (!element.hasAttribute(key)) { + return element; + } + + element.setAttribute(key, new TokenList(element.getAttribute(key)).replace(from, to).toString()); + + return element +} + +/** + * Tokens are always separated by a space. + * + * ``` + * <script type="module"> + * import {clearAttributeTokens} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/attributes.mjs'; + * clearAttributeTokens(); + * </script> + * ``` + * + * @since 1.9.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @param {HTMLElement} element + * @param {string} key + * @return {HTMLElement} + */ +function clearAttributeTokens(element, key) { + validateInstance(element, HTMLElement); + validateString(key) + + if (!element.hasAttribute(key)) { + return element; + } + + element.setAttribute(key, ""); + + return element +} + +/** + * This function searches, starting from an `HTMLElemement`, for the next element that has a certain attribute. + * + * ```html + * <div data-my-attribute="2" id="2"> + * <div id="1"></div> + * </div> + * ``` + * + * ```javascript + * // if no value is specified (undefined), then only the attribute is checked. + * findClosestByAttribute(document.getElementById('1'),'data-my-attribute'); // ↦ node with id 2 + * findClosestByAttribute(document.getElementById('2'),'data-my-attribute'); // ↦ node with id 2 + * + * // if a value is specified, for example an empty string, then the name and the value are checked. + * findClosestByAttribute(document.getElementById('1'),'data-my-attribute', ''); // ↦ undefined + * findClosestByAttribute(document.getElementById('1'),'data-my-attribute', '2'); // ↦ node with id 2 + * ``` + * + * ``` + * <script type="module"> + * import {findClosestByAttribute} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/attributes.mjs'; + * findClosestByAttribute(); + * </script> + * ``` + * + * @since 1.14.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @param {HTMLElement} element + * @param {string} key + * @param {string|undefined} value + * @return {HTMLElement|undefined} + * @summary find closest node + */ +function findClosestByAttribute(element, key, value) { + validateInstance(element, getGlobalFunction('HTMLElement')); + + if (element.hasAttribute(key)) { + if (value === undefined) { + return element; + } + + if (element.getAttribute(key) === value) { + return element; + } + + } + + let selector = validateString(key); + if (value !== undefined) selector += "=" + validateString(value); + let result = element.closest('[' + selector + ']'); + if (result instanceof HTMLElement) { + return result; + } + return undefined; +} + +/** + * This function searches, starting from an `HTMLElemement`, for the next element that has a certain attribute. + * + * ```html + * <div class="myclass" id="2"> + * <div id="1"></div> + * </div> + * ``` + * + * ```javascript + * // if no value is specified (undefined), then only the attribute is checked. + * findClosestByClass(document.getElementById('1'),'myclass'); // ↦ node with id 2 + * findClosestByClass(document.getElementById('2'),'myclass'); // ↦ node with id 2 + * ``` + * + * ``` + * <script type="module"> + * import {findClosestByClass} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/attributes.mjs'; + * findClosestByClass(); + * </script> + * ``` + * + * @since 1.27.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @param {HTMLElement} element + * @param {string} className + * @return {HTMLElement|undefined} + * @summary find closest node + */ +function findClosestByClass(element, className) { + validateInstance(element, getGlobalFunction('HTMLElement')); + + if (element?.classList?.contains(validateString(className))) { + return element; + } + + let result = element.closest('.' + className); + if (result instanceof HTMLElement) { + return result; + } + + return undefined; +} +'use strict'; + + +/** + * @author schukai GmbH + */ + +import {isArray, isObject} from "../types/is.mjs"; +import {validateInstance, validateString} from "../types/validate.mjs"; +import {getDocument} from "./util.mjs"; + +export {fireEvent, fireCustomEvent, findTargetElementFromEvent} + +/** + * The function sends an event + * + * ``` + * <script type="module"> + * import {fireEvent} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/events.mjs'; + * fireEvent() + * </script> + * ``` + * + * @param {HTMLElement|HTMLCollection|NodeList} element + * @param {string} type + * @return {void} + * @since 1.10.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @throws {TypeError} value is not an instance of HTMLElement or HTMLCollection + * @summary Construct and send and event + */ +function fireEvent(element, type) { + + const document = getDocument(); + + if (element instanceof HTMLElement) { + + if (type === 'click') { + element.click(); + return; + } + + let event = new Event(validateString(type), { + bubbles: true, + cancelable: true, + }); + + element.dispatchEvent(event); + + } else if (element instanceof HTMLCollection || element instanceof NodeList) { + for (let e of element) { + fireEvent(e, type); + } + } else { + throw new TypeError('value is not an instance of HTMLElement or HTMLCollection') + } + +} + +/** + * You can call the function via the monster namespace `new Monster.DOM.fireCustomEvent()`. + * + * ``` + * <script type="module"> + * import {fireCustomEvent} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/events.mjs'; + * fireCustomEvent() + * </script> + * ``` + * + * @param {HTMLElement|HTMLCollection|NodeList} element + * @param {string} type + * @return {void} + * @since 1.29.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @throws {TypeError} value is not an instance of HTMLElement or HTMLCollection + * @summary Construct and send and event + */ +function fireCustomEvent(element, type, detail) { + + const document = getDocument(); + + if (element instanceof HTMLElement) { + + if (!isObject(detail)) { + detail = {detail}; + } + + let event = new CustomEvent(validateString(type), { + bubbles: true, + cancelable: true, + detail + }); + + element.dispatchEvent(event); + + } else if (element instanceof HTMLCollection || element instanceof NodeList) { + for (let e of element) { + fireCustomEvent(e, type, detail); + } + } else { + throw new TypeError('value is not an instance of HTMLElement or HTMLCollection') + } + +} + +/** + * This function gets the path `Event.composedPath()` from an event and tries to find the next element + * up the tree `element.closest()` with the attribute and value. If no value, or a value that is undefined or null, + * is specified, only the attribute is searched. + * + * ``` + * <script type="module"> + * import {findTargetElementFromEvent} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/events.mjs'; + * findTargetElementFromEvent() + * </script> + * ``` + * + * @since 1.14.0 + * @param {Event} event + * @param {string} attributeName + * @param {string|null|undefined} attributeValue + * @throws {Error} unsupported event + * @memberOf Monster.DOM + * @throws {TypeError} value is not a string + * @throws {TypeError} value is not an instance of HTMLElement + * @summary Help function to find the appropriate control + */ +function findTargetElementFromEvent(event, attributeName, attributeValue) { + validateInstance(event, Event); + + if (typeof event.composedPath !== 'function') { + throw new Error('unsupported event'); + } + + const path = event.composedPath(); + + // closest cannot be used here, because closest is not correct for slotted elements + if (isArray(path)) { + for (let i = 0; i < path.length; i++) { + const o = path[i]; + + if (o instanceof HTMLElement && + o.hasAttribute(attributeName) + && (attributeValue === undefined || o.getAttribute(attributeName) === attributeValue)) { + return o; + } + } + } + + return undefined; + +} +'use strict'; + + +/** + * @author schukai GmbH + */ + + +import {internalSymbol} from "../../constants.mjs"; +import {Base} from "../../types/base.mjs"; +import {getGlobal, getGlobalFunction} from "../../types/global.mjs"; +import {isFunction} from "../../types/is.mjs"; +import {validateInstance, validateString} from "../../types/validate.mjs"; + +export {Factory} + +/** + * A factory for creating worker instances. + * + * ``` + * <script type="module"> + * import {Factory} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/worker/factory.mjs'; + * console.log(new Factory()) + * </script> + * ``` + * + * @since 1.25.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM.Worker + * @summary A small factory to create worker + */ +class Factory extends Base { + + + /** + * + */ + constructor() { + super(); + this[internalSymbol] = { + worker: new WeakMap + } + } + + /** + * Creates a worker from a URL + * + * @param {string|URL} url + * @param {function} messageHandler + * @param {function} errorHandler + * @return {Worker} + */ + createFromURL = function (url, messageHandler, errorHandler) { + + if (url instanceof URL) { + url = url.toString(); + } + + const workerClass = getGlobalFunction('Worker'); + var worker = new workerClass(validateString(url)); + + if (isFunction(messageHandler)) { + worker.onmessage = (event) => { + messageHandler.call(worker, event); + } + } + + if (isFunction(errorHandler)) { + worker.onerror = (event) => { + errorHandler.call(worker, event); + } + } + + return worker; + }; + + /** + * Creates a worker from a script + * + * @param {string} content + * @param {function} messageHandler + * @param {function} errorHandler + * @return {Worker} + * @see https://developer.mozilla.org/de/docs/Web/API/URL/createObjectURL + */ + createFromScript = function (content, messageHandler, errorHandler) { + const blobFunction = new getGlobalFunction('Blob') + const blob = new blobFunction([validateString(content)], {type: 'script/javascript'}); + + const url = getGlobalFunction('URL').createObjectURL(blob); + const worker = this.createFromURL(url, messageHandler, errorHandler); + + this[internalSymbol]['worker'].set(worker, url); + + return worker; + + }; + + /** + * Terminate the worker and call revokeObjectURL if necessary. + * + * @param worker + * @return {Monster.DOM.Worker.Factory} + */ + terminate(worker) { + + const workerClass = getGlobalFunction('Worker'); + validateInstance(worker, workerClass); + + worker.terminate(); + + if (this[internalSymbol]['worker'].has(worker)) { + const url = this[internalSymbol]['worker'].get(worker); + URL.revokeObjectURL(url); + } + + return this; + } + + +} +'use strict'; + +/** + * In this namespace you will find classes and methods for handling the DOM. + * + * @namespace Monster.DOM.Worker + * @memberOf Monster + * @author schukai GmbH + */ +const ns = {};'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from '../types/base.mjs'; +import {getGlobalFunction, getGlobalObject} from '../types/global.mjs'; +import {validateInstance, validateString} from "../types/validate.mjs"; +import {ATTRIBUTE_TEMPLATE_PREFIX} from "./constants.mjs"; +import {getDocumentTheme} from "./theme.mjs"; + +export {Template} + +/** + * The template class provides methods for creating templates. + * + * ``` + * <script type="module"> + * import {Template} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/template.mjs'; + * new Template() + * </script> + * ``` + * + * @since 1.6.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @summary A template class + */ +class Template extends Base { + /** + * + * @param {HTMLTemplateElement} template + * @throws {TypeError} value is not an instance of + * @throws {TypeError} value is not a function + * @throws {Error} the function is not defined + */ + constructor(template) { + super(); + const HTMLTemplateElement = getGlobalFunction('HTMLTemplateElement'); + validateInstance(template, HTMLTemplateElement); + this.template = template; + } + + /** + * + * @returns {HTMLTemplateElement} + */ + getTemplateElement() { + return this.template; + } + + /** + * + * @return {DocumentFragment} + * @throws {TypeError} value is not an instance of + */ + createDocumentFragment() { + return this.template.content.cloneNode(true); + } + +} + +/** + * This method loads a template with the given ID and returns it. + * + * To do this, it first reads the theme of the document and looks for the `data-monster-theme-name` attribute in the HTML tag. + * + * ``` + * <html data-monster-theme-name="my-theme"> + * ``` + * + * If no theme was specified, the default theme is `monster`. + * + * Now it is looked if there is a template with the given ID and theme `id-theme` and if yes it is returned. + * If there is no template a search for a template with the given ID `id` is done. If this is also not found, an error is thrown. + * + * You can call the method via the monster namespace `Monster.DOM.findDocumentTemplate()`. + * + * ``` + * <script type="module"> + * import {findTemplate} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/template.mjs'; + * findDocumentTemplate() + * </script> + * ``` + * + * @example + * + * import { findDocumentTemplate } from "https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/template.mjs"; + * + * const template = document.createElement("template"); + * template.id = "myTemplate"; + * template.innerHTML = "<p>my default template</p>"; + * document.body.appendChild(template); + * + * const themedTemplate = document.createElement("template"); + * themedTemplate.id = "myTemplate-myTheme"; + * themedTemplate.innerHTML = "<p>my themed template</p>"; + * document.body.appendChild(themedTemplate); + * + * // loads the temple and since no theme is set the default template + * const template1 = findDocumentTemplate("myTemplate"); + * console.log(template1.createDocumentFragment()); + * // ↦ '<p>my default template</p>' + * + * // now we set our own theme + * document + * .querySelector("html") + * .setAttribute("data-monster-theme-name", "myTheme"); + * + * // now we don't get the default template, + * // but the template with the theme in the id + * const template2 = findDocumentTemplate("myTemplate"); + * console.log(template2.createDocumentFragment()); + * // ↦ '<p>my themed template</p>' + * + * @param {string} id + * @param {Node} currentNode + * @return {Monster.DOM.Template} + * @since 1.7.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @throws {Error} template id not found. + * @throws {TypeError} value is not a string + */ +export function findDocumentTemplate(id, currentNode) { + validateString(id); + + const document = getGlobalObject('document'); + const HTMLTemplateElement = getGlobalFunction('HTMLTemplateElement'); + const DocumentFragment = getGlobalFunction('DocumentFragment'); + const Document = getGlobalFunction('Document'); + + + let prefixID; + + if (!(currentNode instanceof Document || currentNode instanceof DocumentFragment)) { + + if (currentNode instanceof Node) { + + if (currentNode.hasAttribute(ATTRIBUTE_TEMPLATE_PREFIX)) { + prefixID = currentNode.getAttribute(ATTRIBUTE_TEMPLATE_PREFIX) + } + + currentNode = currentNode.getRootNode(); + + if (!(currentNode instanceof Document || currentNode instanceof DocumentFragment)) { + currentNode = currentNode.ownerDocument; + } + + } + + if (!(currentNode instanceof Document || currentNode instanceof DocumentFragment)) { + currentNode = document; + } + } + + let template; + let theme = getDocumentTheme() + + if (prefixID) { + let themedPrefixID = prefixID + '-' + id + '-' + theme.getName(); + + // current + themedPrefixID + template = currentNode.getElementById(themedPrefixID); + if (template instanceof HTMLTemplateElement) { + return new Template(template); + } + + // document + themedPrefixID + template = document.getElementById(themedPrefixID); + if (template instanceof HTMLTemplateElement) { + return new Template(template); + } + } + + let themedID = id + '-' + theme.getName(); + + // current + themedID + template = currentNode.getElementById(themedID); + if (template instanceof HTMLTemplateElement) { + return new Template(template); + } + + // document + themedID + template = document.getElementById(themedID); + if (template instanceof HTMLTemplateElement) { + return new Template(template); + } + + // current + ID + template = currentNode.getElementById(id); + if (template instanceof HTMLTemplateElement) { + return new Template(template); + } + + // document + ID + template = document.getElementById(id); + if (template instanceof HTMLTemplateElement) { + return new Template(template); + } + + throw new Error("template " + id + " not found.") +} + +'use strict'; + + +/** + * @author schukai GmbH + */ + +import {getDocument, getWindow} from "./util.mjs"; + +export {domReady, windowReady} + +/** + * This variable is a promise that is fulfilled as soon as the dom is available. + * + * The DOMContentLoaded event is fired when the original HTML document is fully loaded and parsed + * without waiting for stylesheets, images, and subframes to finish loading. + * + * ``` + * <script type="module"> + * import {domReady} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/ready.mjs'; + * domReady().then(()=>{ + * // ... + * }) + * </script> + * ``` + * + * @since 1.31.0 + * @memberOf Monster.DOM + * @summary variable to check if dom is ready + * @type {Promise} + * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/DOMContentLoaded_event + * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/readyState + */ +const domReady = new Promise(resolve => { + + const document = getDocument(); + + if (document.readyState === "loading") { + document.addEventListener('DOMContentLoaded', resolve); + } else { + resolve(); + } +}); + + +/** + * This variable is a promise that is fulfilled as soon as the windows is available. + * + * The load event fires when the entire page is loaded, including all dependent resources such as stylesheets, + * assets, and images. Unlike DOMContentLoaded, which fires as soon as the DOM of the page is loaded, + * without waiting for the resources to finish loading. + * + * ``` + * <script type="module"> + * import {windowReady} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/ready.mjs'; + * windowReady().then(()=>{ + * // ... + * }) + * </script> + * ``` + * + * @since 1.31.0 + * @memberOf Monster.DOM + * @summary variable to check if window is ready + * @type {Promise} + * @see https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event + * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/readyState + */ +const windowReady = new Promise(resolve => { + + const document = getDocument(); + const window = getWindow(); + + if (document.readyState === 'complete') { + resolve(); + } else { + window.addEventListener('load', resolve); + } +}); +'use strict'; + + +/** + * @author schukai GmbH + */ + +import {extend} from "../data/extend.mjs"; +import {BaseWithOptions} from "../types/basewithoptions.mjs"; +import {getGlobalObject} from "../types/global.mjs"; +import {isArray} from "../types/is.mjs"; +import {Stack} from "../types/stack.mjs"; +import {validateInstance, validateString} from "../types/validate.mjs"; + +export {FocusManager} + +/** + * @private + * @type {string} + */ +const KEY_DOCUMENT = 'document'; + +/** + * @private + * @type {string} + */ +const KEY_CONTEXT = 'context'; + + +/** + * @private + * @type {Symbol} + */ +const stackSymbol = Symbol('stack'); + + +/** + * With the focusmanager the focus can be stored in a document, recalled and moved. + * + * ``` + * <script type="module"> + * import {FocusManager} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/focusmanager.mjs'; + * new FocusManager() + * </script> + * ``` + * + * @since 1.25.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @throws {Error} unsupported locale + * @summary Handle the focus + */ + class FocusManager extends BaseWithOptions { + + /** + * + * @param {Object|undefined} options + */ + constructor(options) { + super(options); + validateInstance(this.getOption(KEY_DOCUMENT), HTMLDocument); + + this[stackSymbol] = new Stack(); + } + + /** + * @property {HTMLDocument} document the document object into which the node is to be appended + */ + get defaults() { + return extend({}, super.defaults, { + [KEY_DOCUMENT]: getGlobalObject('document'), + [KEY_CONTEXT]: undefined, + }) + } + + /** + * Remembers the current focus on a stack. + * Several focus can be stored. + * + * @return {Monster.DOM.FocusManager} + */ + storeFocus() { + + const active = this.getActive(); + if (active instanceof Node) { + this[stackSymbol].push(active) + } + return this; + } + + /** + * The last focus on the stack is set again + * + * @return {Monster.DOM.FocusManager} + */ + restoreFocus() { + + const last = this[stackSymbol].pop(); + if (last instanceof Node) { + this.focus(last); + } + + return this; + } + + /** + * + * @param {Node} element + * @param {boolean} preventScroll + * @throws {TypeError} value is not an instance of + * @return {Monster.DOM.FocusManager} + */ + focus(element, preventScroll) { + + validateInstance(element, Node) + + element.focus({ + preventScroll: preventScroll ?? false + }) + + return this; + } + + /** + * + * @return {Element} + */ + getActive() { + return this.getOption(KEY_DOCUMENT).activeElement; + } + + /** + * Select all elements that can be focused + * + * @param {string|undefined} query + * @return {array} + * @throws {TypeError} value is not an instance of + */ + getFocusable(query) { + + let contextElement = this.getOption(KEY_CONTEXT); + if (contextElement === undefined) { + contextElement = this.getOption(KEY_DOCUMENT); + } + + validateInstance(contextElement, Node) + + if (query !== undefined) { + validateString(query); + } + + return [...contextElement.querySelectorAll( + 'details, button, input, [tabindex]:not([tabindex="-1"]), select, textarea, a[href], body' + )].filter((element) => { + + if (query !== undefined && !element.matches(query)) { + return false; + } + + if (element.hasAttribute('disabled')) return false; + if (element.getAttribute("aria-hidden") === 'true') return false; + + const rect = element.getBoundingClientRect(); + if(rect.width===0) return false; + if(rect.height===0) return false; + + return true; + }); + } + + /** + * @param {string} query + * @return {Monster.DOM.FocusManager} + */ + focusNext(query) { + + const current = this.getActive(); + const focusable = this.getFocusable(query); + + if (!isArray(focusable) || focusable.length === 0) { + return this; + } + + if (current instanceof Node) { + let index = focusable.indexOf(current); + + if (index > -1) { + this.focus(focusable[index + 1] || focusable[0]); + } else { + this.focus(focusable[0]); + } + } else { + this.focus(focusable[0]) + } + + return this; + } + + /** + * @param {string} query + * @return {Monster.DOM.FocusManager} + */ + focusPrev(query) { + + const current = this.getActive(); + const focusable = this.getFocusable(query); + + if (!isArray(focusable) || focusable.length === 0) { + return this; + } + + if (current instanceof Node) { + let index = focusable.indexOf(current); + + if (index > -1) { + this.focus(focusable[index - 1] || focusable[focusable.length - 1]); + } else { + this.focus(focusable[focusable.length - 1]); + } + } else { + this.focus(focusable[focusable.length - 1]) + } + + return this; + } + + +} + + + + + + + +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from '../types/base.mjs'; +import {getGlobalFunction} from "../types/global.mjs"; +import {ProxyObserver} from "../types/proxyobserver.mjs"; +import {validateInstance, validateString} from "../types/validate.mjs"; + +export {ATTRIBUTEPREFIX,Assembler} + +/** + * attribute prefix + * + * @type {string} + * @memberOf Monster.DOM + */ +const ATTRIBUTEPREFIX = "data-monster-"; + +/** + * Assembler class + * + * ``` + * <script type="module"> + * import {Assembler} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/assembler.mjs'; + * console.log(new Assembler()) + * </script> + * ``` + * + * @since 1.6.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @summary Allows you to build an html fragment + */ +class Assembler extends Base { + + /** + * @param {DocumentFragment} fragment + * @throws {TypeError} value is not an instance of + * @throws {TypeError} value is not a function + * @throws {Error} the function is not defined + */ + constructor(fragment) { + super(); + this.attributePrefix = ATTRIBUTEPREFIX; + validateInstance(fragment, getGlobalFunction('DocumentFragment')); + this.fragment = fragment; + } + + /** + * + * @param {string} prefix + * @returns {Assembler} + * @throws {TypeError} value is not a string + */ + setAttributePrefix(prefix) { + validateString(prefix); + this.attributePrefix = prefix; + return this; + } + + /** + * + * @returns {string} + */ + getAttributePrefix() { + return this.attributePrefix; + } + + /** + * + * @param {ProxyObserver|undefined} data + * @return {DocumentFragment} + * @throws {TypeError} value is not an instance of + */ + createDocumentFragment(data) { + + if (data === undefined) { + data = new ProxyObserver({}); + } + + validateInstance(data, ProxyObserver); + let fragment = this.fragment.cloneNode(true); + return fragment; + } + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from "../types/base.mjs"; +import {isObject, isString} from "../types/is.mjs"; +import {validateInstance, validateInteger, validateObject, validateString} from "../types/validate.mjs"; +import {Locale, parseLocale} from "./locale.mjs"; + +export {Translations} + +/** + * With this class you can manage translations and access the keys. + * + * ``` + * <script type="module"> + * import {Translations} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/i18n/translations.mjs'; + * new Translations() + * </script> + * ``` + * + * @example + * + * import {Translations} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/i18n/translations.mjs'; + * import {parseLocale} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/i18n/locale.mjs'; + * + * const translation = new Translations(parseLocale('en-GB')); + * + * translation.assignTranslations({ + * text1: "click", + * text2: { + * 'one': 'click once', + * 'other': 'click n times' + * } + * }); + * + * console.log(translation.getText('text1')); + * // ↦ click + * + * console.log(translation.getPluralRuleText('text2',1)); + * // -> click once + * console.log(translation.getPluralRuleText('text2',2)); + * // -> click n times + * + * @since 1.13.0 + * @copyright schukai GmbH + * @memberOf Monster.I18n + * @see https://datatracker.ietf.org/doc/html/rfc3066 + */ +class Translations extends Base { + + /** + * + * @param {Locale} locale + */ + constructor(locale) { + super(); + + if (isString(locale)) { + locale = parseLocale(locale); + } + + this.locale = validateInstance(locale, Locale); + this.storage = new Map(); + + } + + + /** + * Fetches a text using the specified key. + * If no suitable key is found, `defaultText` is taken. + * + * @param {string} key + * @param {string|undefined} defaultText + * @return {string} + * @throws {Error} key not found + */ + getText(key, defaultText) { + if (!this.storage.has(key)) { + if (defaultText === undefined) { + throw new Error('key ' + key + ' not found'); + } + + return validateString(defaultText); + } + + let r = this.storage.get(key); + if (isObject(r)) { + return this.getPluralRuleText(key, 'other', defaultText); + } + + return this.storage.get(key); + } + + /** + * A number `count` can be passed to this method. In addition to a number, one of the keywords can also be passed directly. + * "zero", "one", "two", "few", "many" and "other". Remember: not every language has all rules. + * + * The appropriate text for this number is then selected. If no suitable key is found, `defaultText` is taken. + * + * @param {string} key + * @param {integer|count} count + * @param {string|undefined} defaultText + * @return {string} + */ + getPluralRuleText(key, count, defaultText) { + if (!this.storage.has(key)) { + return validateString(defaultText); + } + + let r = validateObject(this.storage.get(key)); + + let keyword; + if (isString(count)) { + keyword = count.toLocaleString(); + } else { + count = validateInteger(count); + if (count === 0) { + // special handlig for zero count + if (r.hasOwnProperty('zero')) { + return validateString(r['zero']); + } + } + + keyword = new Intl.PluralRules(this.locale.toString()).select(validateInteger(count)); + } + + if (r.hasOwnProperty(keyword)) { + return validateString(r[keyword]); + } + + if (r.hasOwnProperty(DEFAULT_KEY)) { + return validateString(r[DEFAULT_KEY]); + } + + return validateString(defaultText); + } + + /** + * Set a text for a key + * + * ``` + * translations.setText("text1": "Make my day!"); + * // plural rules + * translations.setText("text6": { + * "zero": "There are no files on Disk.", + * "one": "There is one file on Disk.", + * "other": "There are files on Disk." + * "default": "There are files on Disk." + * }); + * ``` + * + * @param {string} key + * @param {string|object} text + * @return {Translations} + * @throws {TypeError} value is not a string or object + */ + setText(key, text) { + + if (isString(text) || isObject(text)) { + this.storage.set(validateString(key), text); + return this; + } + + throw new TypeError('value is not a string or object'); + + } + + /** + * This method can be used to transfer overlays from an object. The keys are transferred and the values are entered as text. + * + * The values can either be character strings or, in the case of texts with plural forms, objects. The plural forms must be stored as text via a standard key "zero", "one", "two", "few", "many" and "other". + * + * Additionally, the key default can be specified, which will be used if no other key fits. + * + * In some languages, like for example in german, there is no own more number at the value 0. In these languages the function applies additionally zero. + * + * ``` + * translations.assignTranslations({ + * "text1": "Make my day!", + * "text2": "I'll be back!", + * "text6": { + * "zero": "There are no files on Disk.", + * "one": "There is one file on Disk.", + * "other": "There are files on Disk." + * "default": "There are files on Disk." + * }); + * ``` + * + * @param {object} translations + * @return {Translations} + */ + assignTranslations(translations) { + validateObject(translations); + + for (const [k, v] of Object.entries(translations)) { + this.setText(k, v); + } + + return this; + + } + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from "../types/base.mjs"; +import {validateString} from "../types/validate.mjs"; +import {clone} from "../util/clone.mjs"; + +export {Locale, parseLocale} + +/** + * @memberOf Monster.I18n + * @type {symbol} + */ +const propertiesSymbol = Symbol('properties'); + +/** + * @type {symbol} + * @memberOf Monster.I18n + */ +const localeStringSymbol = Symbol('localeString'); + +/** + * The Locale class is a base class for the language classes. + * + * ``` + * <script type="module"> + * import {Locale} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/i18n/locale.mjs'; + * new Locale() + * </script> + * ``` + * + * RFC + * + * ``` + * A Language-Tag consists of: + * langtag ; generated tag + * -or- private-use ; a private use tag + * + * langtag = (language + * ["-" script] + * ["-" region] + * *("-" variant) + * *("-" extension) + * ["-" privateuse]) + * + * language = "en", "ale", or a registered value + * + * script = "Latn", "Cyrl", "Hant" ISO 15924 codes + * + * region = "US", "CS", "FR" ISO 3166 codes + * "419", "019", or UN M.49 codes + * + * variant = "rozaj", "nedis", "1996", multiple subtags can be used in a tag + * + * extension = single letter followed by additional subtags; more than one extension + * may be used in a language tag + * + * private-use = "x-" followed by additional subtags, as many as are required + * Note that these can start a tag or appear at the end (but not + * in the middle) + * ``` + * + * @since 1.13.0 + * @copyright schukai GmbH + * @memberOf Monster.I18n + * @see https://datatracker.ietf.org/doc/html/rfc3066 + */ +class Locale extends Base { + + /** + * @param {string} language + * @param {string} [region] + * @param {string} [script] + * @param {string} [variants] + * @param {string} [extlang] + * @param {string} [privateUse] + * @throws {Error} unsupported locale + */ + constructor(language, region, script, variants, extlang, privateUse) { + super(); + + this[propertiesSymbol] = { + language: (language === undefined) ? undefined : validateString(language), + script: (script === undefined) ? undefined : validateString(script), + region: (region === undefined) ? undefined : validateString(region), + variants: (variants === undefined) ? undefined : validateString(variants), + extlang: (extlang === undefined) ? undefined : validateString(extlang), + privateUse: (privateUse === undefined) ? undefined : validateString(privateUse), + }; + + let s = []; + if (language !== undefined) s.push(language); + if (script !== undefined) s.push(script); + if (region !== undefined) s.push(region); + if (variants !== undefined) s.push(variants); + if (extlang !== undefined) s.push(extlang); + if (privateUse !== undefined) s.push(privateUse); + + if (s.length === 0) { + throw new Error('unsupported locale'); + } + + this[localeStringSymbol] = s.join('-'); + + } + + /** + * @return {string} + */ + get localeString() { + return this[localeStringSymbol]; + } + + /** + * @return {string|undefined} + */ + get language() { + return this[propertiesSymbol].language; + } + + /** + * @return {string|undefined} + */ + get region() { + return this[propertiesSymbol].region; + } + + /** + * @return {string|undefined} + */ + get script() { + return this[propertiesSymbol].script; + } + + /** + * @return {string|undefined} + */ + get variants() { + return this[propertiesSymbol].variants; + } + + /** + * @return {string|undefined} + */ + get extlang() { + return this[propertiesSymbol].extlang; + } + + /** + * @return {string|undefined} + */ + get privateUse() { + return this[propertiesSymbol].privateValue; + } + + + /** + * @return {string} + */ + toString() { + return "" + this.localeString; + } + + /** + * The structure has the following: language, script, region, variants, extlang, privateUse + * + * @return {Monster.I18n.LocaleMap} + */ + getMap() { + return clone(this[propertiesSymbol]) + } + + +} + +/** + * @typedef {Object} LocaleMap + * @property {string} language + * @property {string} script + * @property {string} region + * @property {string} variants + * @property {string} extlang + * @property {string} privateUse + * @memberOf Monster.I18n + */ + +/** + * Parse local according to rfc4646 standard + * + * Limitations: The regex cannot handle multiple variants or private. + * + * You can call the method via the monster namespace `Monster.I18n.createLocale()`. + * + * ``` + * <script type="module"> + * import {Monster} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source//monster.mjs'; + * new Monster.I18n.createLocale() + * </script> + * ``` + * + * Alternatively, you can also integrate this function individually. + * + * ``` + * <script type="module"> + * import {createLocale} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/i18n/locale.mjs'; + * createLocale() + * </script> + * ``` + * + * RFC + * + * ``` + * The syntax of the language tag in ABNF [RFC4234] is: + * + * Language-Tag = langtag + * / privateuse ; private use tag + * / grandfathered ; grandfathered registrations + * + * langtag = (language + * ["-" script] + * ["-" region] + * *("-" variant) + * *("-" extension) + * ["-" privateuse]) + * + * language = (2*3ALPHA [ extlang ]) ; shortest ISO 639 code + * / 4ALPHA ; reserved for future use + * / 5*8ALPHA ; registered language subtag + * + * extlang = *3("-" 3ALPHA) ; reserved for future use + * + * script = 4ALPHA ; ISO 15924 code + * + * region = 2ALPHA ; ISO 3166 code + * / 3DIGIT ; UN M.49 code + * + * variant = 5*8alphanum ; registered variants + * / (DIGIT 3alphanum) + * + * extension = singleton 1*("-" (2*8alphanum)) + * + * singleton = %x41-57 / %x59-5A / %x61-77 / %x79-7A / DIGIT + * ; "a"-"w" / "y"-"z" / "A"-"W" / "Y"-"Z" / "0"-"9" + * ; Single letters: x/X is reserved for private use + * + * privateuse = ("x"/"X") 1*("-" (1*8alphanum)) + * + * grandfathered = 1*3ALPHA 1*2("-" (2*8alphanum)) + * ; grandfathered registration + * ; Note: i is the only singleton + * ; that starts a grandfathered tag + * + * alphanum = (ALPHA / DIGIT) ; letters and numbers + * + * Figure 1: Language Tag ABNF + * ``` + * + * @param {string} locale + * @returns {Locale} + * @since 1.14.0 + * @copyright schukai GmbH + * @memberOf Monster.I18n + * @throws {TypeError} value is not a string + * @throws {Error} unsupported locale + */ +function parseLocale(locale) { + + locale = validateString(locale).replace(/_/g, "-"); + + let language, region, variants, parts, script, extlang, + regexRegular = "(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang)", + regexIrregular = "(en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)", + regexGrandfathered = "(" + regexIrregular + "|" + regexRegular + ")", + regexPrivateUse = "(x(-[A-Za-z0-9]{1,8})+)", + regexSingleton = "[0-9A-WY-Za-wy-z]", + regexExtension = "(" + regexSingleton + "(-[A-Za-z0-9]{2,8})+)", + regexVariant = "([A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3})", + regexRegion = "([A-Za-z]{2}|[0-9]{3})", + regexScript = "([A-Za-z]{4})", + regexExtlang = "([A-Za-z]{3}(-[A-Za-z]{3}){0,2})", + regexLanguage = "(([A-Za-z]{2,3}(-" + regexExtlang + ")?)|[A-Za-z]{4}|[A-Za-z]{5,8})", + regexLangtag = "(" + regexLanguage + "(-" + regexScript + ")?" + "(-" + regexRegion + ")?" + "(-" + regexVariant + ")*" + "(-" + regexExtension + ")*" + "(-" + regexPrivateUse + ")?" + ")", + regexLanguageTag = "^(" + regexGrandfathered + "|" + regexLangtag + "|" + regexPrivateUse + ")$", + regex = new RegExp(regexLanguageTag), match; + + + if ((match = regex.exec(locale)) !== null) { + if (match.index === regex.lastIndex) { + regex.lastIndex++; + } + } + + if (match === undefined || match === null) { + throw new Error('unsupported locale'); + } + + if (match[6] !== undefined) { + language = match[6]; + + parts = language.split('-'); + if (parts.length > 1) { + language = parts[0]; + extlang = parts[1]; + } + + } + + if (match[14] !== undefined) { + region = match[14]; + } + + if (match[12] !== undefined) { + script = match[12]; + } + + if (match[16] !== undefined) { + variants = match[16]; + } + + return new Locale(language, region, script, variants, extlang); + +} +'use strict'; + +/** + * @author schukai GmbH + */ + + +import {internalSymbol} from "../constants.mjs"; +import {extend} from "../data/extend.mjs"; + +import {Formatter as TextFormatter} from "../text/formatter.mjs"; +import {validateInstance, validateString} from "../types/validate.mjs"; +import {Translations} from "./translations.mjs"; + +export {Formatter} + +/** + * @private + * @type {symbol} + */ +const internalTranslationSymbol = Symbol('internalTranslation') + +/** + * The Formatter extends the Text.Formatter with the possibility to replace the key by a translation. + * + * ``` + * <script type="module"> + * import {Formatter} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/i18n/formatter.mjs'; + * new Formatter() + * </script> + * ``` + * + * @example + * + * import {Formatter} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/i18n/formatter.mjs'; + * import {Translations} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/i18n/translations.mjs'; + * + * const translations = new Translations('en') + * .assignTranslations({ + * thekey: "${animal} has eaten the ${food}!" + * }); + * + * new Formatter({}, translations).format("thekey:animal=dog::food=cake") + * // ↦ dog has eaten the cake! + * + * @since 1.26.0 + * @copyright schukai GmbH + * @memberOf Monster.I18n + */ +class Formatter extends TextFormatter { + + /** + * Default values for the markers are `${` and `}` + * + * @param {object} object + * @throws {TypeError} value is not a object + */ + constructor(object, translation, options) { + super(object, options); + this[internalTranslationSymbol] = validateInstance(translation, Translations); + } + + /** + * @property {object} marker + * @property {array} marker.open=["i18n{","${"] + * @property {array} marker.close=["${"] + * @property {object} parameter + * @property {string} parameter.delimiter="::" + * @property {string} parameter.assignment="=" + * @property {object} callbacks + * @property {function} callbacks.i18n=()=>{} + */ + get defaults() { + const self = this; + return extend({}, super.defaults, { + callbacks: { + i18n: (value) => { + return self[internalTranslationSymbol].getText(validateString(value)); + } + }, + marker: { + open: ['i18n{', '${'], + close: ['}'], + }, + }) + } + + /** + * + * @param {string} text + * @return {string} + * @throws {TypeError} value is not a string + * @throws {Error} too deep nesting + * @throws {Error} key not found + * @throws {Error} the closing marker is missing + */ + format(text) { + validateString(text) + + const openMarker = this[internalSymbol]['marker']['open']?.[0]; + const closeMarker = this[internalSymbol]['marker']['close']?.[0]; + + if (text.indexOf(openMarker) === 0) { + text = text.substring(openMarker.length); + + if (text.indexOf(closeMarker) === text.length - closeMarker.length) { + text = text.substring(0, text.length - closeMarker.length); + } else { + throw new Error("the closing marker is missing") + } + } + + + const parts = validateString(text).split('::') + const translationKey = parts.shift().trim(); // key value delimiter + const parameter = parts.join('::').trim(); + + + let assembledText = openMarker + 'static:' + translationKey + ' | call:i18n'; + if (parameter.length > 0) { + assembledText += '::' + parameter; + } + assembledText += closeMarker; + return super.format(assembledText); + } + + +} +'use strict'; + +/** + * In this namespace you will find classes and methods for handling locale and localized texts. + * + * @namespace Monster.I18n.Providers + * @memberOf Monster + * @author schukai GmbH + */ +const ns = {}; +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../../constants.mjs"; +import {extend} from "../../data/extend.mjs"; +import {Formatter} from "../../text/formatter.mjs"; +import {getGlobalFunction} from "../../types/global.mjs"; +import {isInstance, isString} from "../../types/is.mjs"; +import {validateObject, validateString} from "../../types/validate.mjs"; +import {parseLocale} from "../locale.mjs"; +import {Provider} from "../provider.mjs"; +import {Translations} from "../translations.mjs"; + +export {Fetch} + +/** + * The fetch provider retrieves a JSON file from the given URL and returns a translation object. + * + * ``` + * <script type="module"> + * import {Fetch} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/i18n/providers/fetch.mjs'; + * new Fetch() + * </script> + * ``` + * + * @example <caption>das ist ein test</caption> + * + * import {Fetch} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/i18n/providers/fetch.mjs'; + * + * // fetch from API + * const translation = new Fetch('https://example.com/${language}.json').getTranslation('en-GB'); + * // ↦ https://example.com/en.json + * + * @since 1.13.0 + * @copyright schukai GmbH + * @memberOf Monster.I18n.Providers + * @see {@link https://datatracker.ietf.org/doc/html/rfc3066} + * @tutorial i18n-locale-and-formatter + */ + class Fetch extends Provider { + + /** + * As options the key `fetch` can be passed. This config object is passed to the fetch method as init. + * + * The url may contain placeholders (language, script, region, variants, extlang, privateUse), so you can specify one url for all translations. + * + * ``` + * new Fetch('https://www.example.com/assets/${language}.json') + * ``` + * + * @param {string|URL} url + * @param {Object} options see {@link Monster.I18n.Providers.Fetch#defaults} + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/fetch} + */ + constructor(url, options) { + super(options); + + if (isInstance(url, URL)) { + url = url.toString(); + } + + if (options === undefined) { + options = {}; + } + + validateString(url); + + /** + * @property {string} + */ + this.url = url; + + /** + * @private + * @property {Object} options + */ + this[internalSymbol] = extend({}, super.defaults, this.defaults, validateObject(options)); + + } + + /** + * Defaults + * + * @property {Object} fetch + * @property {String} fetch.method=GET + * @property {String} fetch.mode=cors + * @property {String} fetch.cache=no-cache + * @property {String} fetch.credentials=omit + * @property {String} fetch.redirect=follow + * @property {String} fetch.referrerPolicy=no-referrer + * + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API} + */ + get defaults() { + + return { + fetch: { + method: 'GET', // *GET, POST, PUT, DELETE, etc. + mode: 'cors', // no-cors, *cors, same-origin + cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached + credentials: 'omit', // include, *same-origin, omit + redirect: 'follow', // manual, *follow, error + referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url + } + } + + } + + /** + * + * @param {Locale|string} locale + * @return {Promise} + */ + getTranslations(locale) { + + if (isString(locale)) { + locale = parseLocale(locale); + } + + let formatter = new Formatter(locale.getMap()) + + return getGlobalFunction('fetch')(formatter.format(this.url), this.getOption('fetch', {})) + .then((response) => response.json()).then(data => { + return new Translations(locale).assignTranslations(data); + }); + + } + + +} + +'use strict'; + +/** + * In this namespace you will find classes and methods for handling locale and localized texts. + * + * @namespace Monster.I18n + * @memberOf Monster + * @author schukai GmbH + */ +const ns = {};'use strict'; + +/** + * @author schukai GmbH + */ + +import {BaseWithOptions} from "../types/basewithoptions.mjs"; +import {Locale} from "./locale.mjs" +import {Translations} from "./translations.mjs" + +export {Provider} + +/** + * A provider makes a translation object available. + * + * ``` + * <script type="module"> + * import {Provider} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/i18n/provider.mjs'; + * new Provider() + * </script> + * ``` + * + * @since 1.13.0 + * @copyright schukai GmbH + * @memberOf Monster.I18n + * @see {@link https://datatracker.ietf.org/doc/html/rfc3066} + */ +class Provider extends BaseWithOptions { + + /** + * @param {Locale|string} locale + * @return {Promise} + */ + getTranslations(locale) { + return new Promise((resolve, reject) => { + try { + resolve(new Translations(locale)); + } catch (e) { + reject(e); + } + + }); + } + +} +'use strict'; + +/** + * Property-Keys + * @author schukai GmbH + */ + +export { + internalSymbol, + internalStateSymbol +} + + +/** + * @private + * @type {symbol} + * @memberOf Monster + * @since 1.24.0 + */ +const internalSymbol = Symbol('internalData'); + +/** + * @private + * @type {symbol} + * @memberOf Monster + * @since 1.25.0 + */ +const internalStateSymbol = Symbol('state'); + +'use strict'; + + +/** + * @author schukai GmbH + */ + +import {Base} from "./base.mjs"; +import {isString} from "./is.mjs"; +import {validateArray, validateString} from "./validate.mjs"; + +export {MediaType, parseMediaType} + +/** + * @private + * @type {symbol} + */ +const internal = Symbol('internal'); + +/** + * @typedef {Object} Parameter + * @property {string} key + * @property {string} value + * @memberOf Monster.Types + */ + + +/** + * You can create an object via the monster namespace `new Monster.Types.MediaType()`. + * + * ``` + * <script type="module"> + * import {MediaType} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/mediatype.mjs'; + * console.log(new MediaType()) + * </script> + * ``` + * + * @since 1.8.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +class MediaType extends Base { + + /** + * + * @param {String} type + * @param {String} subtype + * @param {Monster.Types.Parameter[]} parameter + */ + constructor(type, subtype, parameter) { + super(); + + this[internal] = { + type: validateString(type).toLowerCase(), + subtype: validateString(subtype).toLowerCase(), + parameter: [] + } + + if (parameter !== undefined) { + this[internal]['parameter'] = validateArray(parameter); + } + + + } + + /** + * @return {String} + */ + get type() { + return this[internal].type; + } + + /** + * @return {String} + */ + get subtype() { + return this[internal].subtype; + } + + /** + * @return {Monster.Types.Parameter[]} + */ + get parameter() { + return this[internal].parameter; + } + + /** + * + * + * @return {Map} + */ + get parameter() { + + const result = new Map + + this[internal]['parameter'].forEach(p => { + + let value = p.value; + + // internally special values are partly stored with quotes, this function removes them. + if (value.startsWith('"') && value.endsWith('"')) { + value = value.substring(1, value.length - 1); + } + + result.set(p.key, value); + }) + + + return result; + } + + /** + * + * @return {string} + */ + toString() { + + let parameter = []; + for (let a of this[internal].parameter) { + parameter.push(a.key + '=' + a.value); + } + + return this[internal].type + '/' + this[internal].subtype + (parameter.length > 0 ? ';' + parameter.join(';') : ''); + } + +} + +/** + * You can call the function via the monster namespace `Monster.Types.parseMediaType()`. + * + * ``` + * <script type="module"> + * import {Monster} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source//monster.mjs'; + * console.log(Monster.Types.parseMediaType()) + * </script> + * ``` + * + * Alternatively, you can also integrate this function individually. + * + * ``` + * <script type="module"> + * import {parseMediaType} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/dataurl.mjs'; + * console.log(parseMediaType()) + * </script> + * ``` + * + * Specification: + * + * ``` + * dataurl := "data:" [ mediatype ] [ ";base64" ] "," data + * mediatype := [ type "/" subtype ] *( ";" parameter ) + * data := *urlchar + * parameter := attribute "=" value + * ``` + * + * @param {String} mediatype + * @return {Monster.Types.MediaType} + * @see https://datatracker.ietf.org/doc/html/rfc2045#section-5.1 + * @throws {TypeError} the mimetype can not be parsed + * @throws {TypeError} blank value is not allowed + * @throws {TypeError} malformed data url + * @memberOf Monster.Types + */ +function parseMediaType(mediatype) { + + const regex = /(?<type>[A-Za-z]+|\*)\/(?<subtype>([a-zA-Z0-9.\+_\-]+)|\*|)(?<parameter>\s*;\s*([a-zA-Z0-9]+)\s*(=\s*("?[A-Za-z0-9_\-]+"?))?)*/g; + const result = regex.exec(validateString(mediatype)); + + const groups = result?.['groups']; + if (groups === undefined) { + throw new TypeError('the mimetype can not be parsed') + } + + const type = groups?.['type']; + const subtype = groups?.['subtype']; + const parameter = groups?.['parameter']; + + if (subtype === "" || type === "") { + throw new TypeError('blank value is not allowed'); + } + + return new MediaType(type, subtype, parseParameter(parameter)); + + +} + +/** + * @private + * @since 1.18.0 + * @param {String} parameter + * @return {Monster.Types.Parameter[]|undefined} + * @memberOf Monster.Types + */ +function parseParameter(parameter) { + + if (!isString(parameter)) { + return undefined; + } + + let result = []; + + parameter.split(';').forEach((entry) => { + + entry = entry.trim(); + if (entry === "") { + return; + } + + const kv = entry.split('=') + + let key = validateString(kv?.[0]).trim(); + let value = validateString(kv?.[1]).trim(); + + // if values are quoted, they remain so internally + result.push({ + key: key, + value: value + }) + + + }) + + return result; + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +export {typeOf} + +/** + * The built-in typeof method is known to have some historical weaknesses. This function tries to provide a better and more accurate result. + * + * ``` + * <script type="module"> + * import {typeOf} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/typeof.mjs'; + * console.log(typeOf()) + * </script> + * ``` + * + * @example + * + * import {typeOf} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/typeof.mjs'; + * + * console.log(typeOf(undefined)); // ↦ undefined + * console.log(typeOf("")); // ↦ string + * console.log(typeOf(5)); // ↦ number + * console.log(typeOf({})); // ↦ object + * console.log(typeOf([])); // ↦ array + * console.log(typeOf(new Map)); // ↦ map + * console.log(typeOf(true)); // ↦ boolean + * + * @param {*} value + * @return {string} + * @since 1.7.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @throws {TypeError} value is not a primitive + */ +function typeOf(value) { + let type = ({}).toString.call(value).match(/\s([a-zA-Z]+)/)[1]; + if ('Object' === type) { + + const name=value.constructor.name; + if (name) { + return name.toLowerCase(); + } + + const results = (/^(class|function)\s+(\w+)/).exec(value.constructor.toString()); + type = (results && results.length > 2) ? results[2] : ''; + } + + return type.toLowerCase(); +} + +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from './base.mjs'; +import {Observer} from "./observer.mjs"; +import {validateInstance} from "./validate.mjs"; + +export {ObserverList} + +/** + * With the help of the ObserverList class, observer can be managed. + * + * ``` + * <script type="module"> + * import {ObserverList} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/observerlist.mjs'; + * console.log(ObserverList()) + * console.log(ObserverList()) + * </script> + * ``` + * + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +class ObserverList extends Base { + + /** + * + */ + constructor() { + super(); + this.observers = []; + } + + /** + * + * @param {Observer} observer + * @return {ObserverList} + * @throws {TypeError} value is not an instance of Observer + */ + attach(observer) { + validateInstance(observer, Observer) + + this.observers.push(observer); + return this; + }; + + /** + * + * @param {Observer} observer + * @return {ObserverList} + * @throws {TypeError} value is not an instance of Observer + */ + detach(observer) { + validateInstance(observer, Observer) + + var i = 0, l = this.observers.length; + for (; i < l; i++) { + if (this.observers[i] === observer) { + this.observers.splice(i, 1); + } + } + + return this; + }; + + /** + * + * @param {Observer} observer + * @return {boolean} + * @throws {TypeError} value is not an instance of Observer + */ + contains(observer) { + validateInstance(observer, Observer) + var i = 0, l = this.observers.length; + for (; i < l; i++) { + if (this.observers[i] === observer) { + return true; + } + } + return false; + }; + + /** + * + * @param subject + * @return {Promise} + */ + notify(subject) { + + let pomises = [] + + let i = 0, l = this.observers.length; + for (; i < l; i++) { + pomises.push(this.observers[i].update(subject)); + } + + return Promise.all(pomises); + }; + +} +'use strict'; + + +/** + * @author schukai GmbH + */ + +import {random} from "../math/random.mjs"; +import {getGlobal} from "./global.mjs"; +import {ID} from "./id.mjs"; + +export {RandomID} + +/** + * @private + * @type {number} + */ +let internalCounter = 0; + +/** + * The `RandomID` class provides a unique ID for an item. + * + * ``` + * <script type="module"> + * import {RandomID} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/randomid.mjs'; + * console.log(new RandomID()) + * </script> + * ``` + * + * @since 1.6.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @summary class to generate random numbers + */ +class RandomID extends ID { + + /** + * create new object + */ + constructor() { + super(); + + internalCounter += 1; + + this.id = getGlobal().btoa(random(1, 10000)) + .replace(/=/g, '') + /** No numbers at the beginning of the ID, because of possible problems with DOM */ + .replace(/^[0-9]+/, 'X') + internalCounter; + } + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../constants.mjs"; +import {random} from "../math/random.mjs"; +import {isObject} from '../types/is.mjs'; +import {Base} from "./base.mjs"; +import {getGlobalObject} from "./global.mjs"; + +export {UUID} + +/** + * The UUID class makes it possible to get a unique UUID for an object. + * + * ``` + * <script type="module"> + * import {Base} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/uuid.mjs'; + * new UUID() + * </script> + * ``` + * + * @since 1.25.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @throws {Error} unsupported + */ + class UUID extends Base { + + /** + * + */ + constructor() { + super(); + + let uuid = createWithCrypto(); + + if (uuid === undefined) { + uuid = createWithRandom(); + } + + + if (uuid === undefined) { + throw new Error('unsupported') + } + + this[internalSymbol] = { + value: uuid + } + + } + + /** + * + * @return {string} + */ + toString() { + return this[internalSymbol]['value']; + } + + +} + +/** + * @private + * @return {string|undefined} + */ +function createWithRandom() { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { + var r = random(0, 65000) * 16 | 0, + v = ((c === 'x') ? r : (r & 0x3 | 0x8)); + return v.toString(16)[0]; + }) +} + + +/** + * @private + * @return {string|undefined} + */ +function createWithCrypto() { + const crypt = getGlobalObject('crypto'); + if (!isObject(crypt)) return; + if (typeof crypt?.['randomUUID']) return; + return crypt.randomUUID(); +} + +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from './base.mjs'; +import {isObject} from './is.mjs'; +import {TokenList} from './tokenlist.mjs'; +import {UniqueQueue} from './uniquequeue.mjs'; + +export {Observer} + +/** + * An observer manages a callback function + * + * ``` + * import {Observer} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/observer.mjs'; + * new Observer() + * ``` + * + * The update method is called with the subject object as this pointer. For this reason the callback should not + * be an arrow function, because it gets the this pointer of its own context. + * + * ``` + * new Observer(()=>{ + * // this is not subject + * }) + * + * new Observer(function() { + * // this is subject + * }) + * ``` + * + * Additional arguments can be passed to the callback. To do this, simply specify them. + * + * ``` + * Observer(function(a, b, c) { + * console.log(a, b, c); // ↦ "a", 2, true + * }, "a", 2, true) + * ``` + * + * The callback function must have as many parameters as arguments are given. + * + * @example + * + * import {Observer} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/observer.mjs'; + * + * const observer = new Observer(function(a, b, c) { + * console.log(this, a, b, c); // ↦ "a", 2, true + * }, "a", 2, true); + * + * observer.update({value:true}).then(()=>{}); + * // ↦ {value: true} "a" 2 true + * + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +class Observer extends Base { + + /** + * + * @param {function} callback + * @param {*} args + */ + constructor(callback, ...args) { + super(); + + if (typeof callback !== 'function') { + throw new Error("observer callback must be a function") + } + + this.callback = callback; + this.arguments = args; + this.tags = new TokenList; + this.queue = new UniqueQueue(); + } + + /** + * + * @param {string} tag + * @returns {Observer} + */ + addTag(tag) { + this.tags.add(tag); + return this; + } + + /** + * + * @param {string} tag + * @returns {Observer} + */ + removeTag(tag) { + this.tags.remove(tag); + return this; + } + + /** + * + * @returns {Array} + */ + getTags() { + return this.tags.entries() + } + + /** + * + * @param {string} tag + * @returns {boolean} + */ + hasTag(tag) { + return this.tags.contains(tag) + } + + /** + * + * @param {object} subject + * @returns {Promise} + */ + update(subject) { + let self = this; + + return new Promise(function (resolve, reject) { + if (!isObject(subject)) { + reject("subject must be an object"); + return; + } + + self.queue.add(subject); + + setTimeout(() => { + + try { + // the queue and the settimeout ensure that an object is not + // informed of the same change more than once. + if (self.queue.isEmpty()) { + resolve(); + return; + } + + let s = self.queue.poll(); + let result = self.callback.apply(s, self.arguments); + + if (isObject(result) && result instanceof Promise) { + result.then(resolve).catch(reject); + return; + } + + resolve(result); + + } catch (e) { + reject(e); + } + }, 0) + + }); + + }; + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {isIterable, isString} from '../types/is.mjs'; +import {validateFunction, validateString} from '../types/validate.mjs'; +import {Base} from './base.mjs'; + +export {TokenList} + +/** + * A tokenlist allows you to manage tokens (individual character strings such as css classes in an attribute string). + * + * The tokenlist offers various functions to manipulate values. For example, you can add, remove or replace a class in a CSS list. + * + * ``` + * <script type="module"> + * import {TokenList} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/tokenlist.mjs'; + * new TokenList("myclass row") + * </script> + * ``` + * + * This class implements the [iteration protocol](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols). + * + * ``` + * typeof new TokenList("myclass row")[Symbol.iterator]; + * // ↦ "function" + * ``` + * + * @since 1.2.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +class TokenList extends Base { + + /** + * + * @param {array|string|iteratable} init + */ + constructor(init) { + super(); + this.tokens = new Set(); + + if (typeof init !== "undefined") { + this.add(init); + } + + } + + /** + * Iterator protocol + * + * @returns {Symbol.iterator} + */ + getIterator() { + return this[Symbol.iterator](); + } + + /** + * Iterator + * + * @returns {{next: ((function(): ({value: *, done: boolean}))|*)}} + */ + [Symbol.iterator]() { + // Use a new index for each iterator. This makes multiple + // iterations over the iterable safe for non-trivial cases, + // such as use of break or nested looping over the same iterable. + let index = 0; + let entries = this.entries() + + return { + next: () => { + if (index < entries.length) { + return {value: entries?.[index++], done: false} + } else { + return {done: true} + } + } + } + } + + /** + * Returns true if it contains token, otherwise false + * + * ``` + * new TokenList("start middle end").contains('start')); // ↦ true + * new TokenList("start middle end").contains('end')); // ↦ true + * new TokenList("start middle end").contains('xyz')); // ↦ false + * new TokenList("start middle end").contains(['end','start','middle'])); // ↦ true + * new TokenList("start middle end").contains(['end','start','xyz'])); // ↦ false + * ``` + * + * @param {array|string|iteratable} value + * @returns {boolean} + */ + contains(value) { + if (isString(value)) { + value = value.trim() + let counter = 0; + value.split(" ").forEach(token => { + if (this.tokens.has(token.trim()) === false) return false; + counter++ + }) + return counter > 0 ? true : false; + } + + if (isIterable(value)) { + let counter = 0; + for (let token of value) { + validateString(token); + if (this.tokens.has(token.trim()) === false) return false; + counter++ + } + return counter > 0 ? true : false; + } + + return false; + } + + /** + * add tokens + * + * ``` + * new TokenList().add("abc xyz").toString(); // ↦ "abc xyz" + * new TokenList().add(["abc","xyz"]).toString(); // ↦ "abc xyz" + * new TokenList().add(undefined); // ↦ add nothing + * ``` + * + * @param {array|string|iteratable} value + * @returns {TokenList} + * @throws {TypeError} unsupported value + */ + add(value) { + if (isString(value)) { + value.split(" ").forEach(token => { + this.tokens.add(token.trim()); + }) + } else if (isIterable(value)) { + for (let token of value) { + validateString(token); + this.tokens.add(token.trim()); + } + } else if (typeof value !== "undefined") { + throw new TypeError("unsupported value"); + } + + return this; + } + + /** + * remove all tokens + * + * @returns {TokenList} + */ + clear() { + this.tokens.clear(); + return this; + } + + /** + * Removes token + * + * ``` + * new TokenList("abc xyz").remove("xyz").toString(); // ↦ "abc" + * new TokenList("abc xyz").remove(["xyz"]).toString(); // ↦ "abc" + * new TokenList("abc xyz").remove(undefined); // ↦ remove nothing + * ``` + * + * @param {array|string|iteratable} value + * @returns {TokenList} + * @throws {TypeError} unsupported value + */ + remove(value) { + if (isString(value)) { + value.split(" ").forEach(token => { + this.tokens.delete(token.trim()); + }) + } else if (isIterable(value)) { + for (let token of value) { + validateString(token); + this.tokens.delete(token.trim()); + } + } else if (typeof value !== "undefined") { + throw new TypeError("unsupported value", "types/tokenlist.mjs"); + } + + return this; + } + + /** + * this method replaces a token with a new token. + * + * if the passed token exists, it is replaced with newToken and TokenList is returned. + * if the token does not exist, newToken is not set and TokenList is returned. + * + * @param {string} token + * @param {string} newToken + * @returns {TokenList} + */ + replace(token, newToken) { + validateString(token); + validateString(newToken); + if (!this.contains(token)) { + return this; + } + + let a = Array.from(this.tokens) + let i = a.indexOf(token); + if (i === -1) return this; + + a.splice(i, 1, newToken); + this.tokens = new Set(); + this.add(a); + + return this; + + + } + + /** + * Removes token from string. If token doesn't exist it's added. + * + * ``` + * new TokenList("abc def ghi").toggle("def xyz").toString(); // ↦ "abc ghi xyz" + * new TokenList("abc def ghi").toggle(["abc","xyz"]).toString(); // ↦ "def ghi xyz" + * new TokenList().toggle(undefined); // ↦ nothing + * ``` + * + * @param {array|string|iteratable} value + * @returns {boolean} + * @throws {TypeError} unsupported value + */ + toggle(value) { + + if (isString(value)) { + value.split(" ").forEach(token => { + toggleValue.call(this, token); + }) + } else if (isIterable(value)) { + for (let token of value) { + toggleValue.call(this, token); + } + } else if (typeof value !== "undefined") { + throw new TypeError("unsupported value", "types/tokenlist.mjs"); + } + + return this; + + } + + /** + * returns an array with all tokens + * + * @returns {array} + */ + entries() { + return Array.from(this.tokens) + } + + /** + * executes the provided function with each value of the set + * + * @param {function} callback + * @returns {TokenList} + */ + forEach(callback) { + validateFunction(callback); + this.tokens.forEach(callback); + return this; + } + + /** + * returns the individual tokens separated by a blank character + * + * @returns {string} + */ + toString() { + return this.entries().join(' '); + } + +} + +/** + * @private + * @param token + * @returns {toggleValue} + * @throws {Error} must be called with TokenList.call + */ +function toggleValue(token) { + if (!(this instanceof TokenList)) throw Error("must be called with TokenList.call") + validateString(token); + token = token.trim(); + if (this.contains(token)) { + this.remove(token); + return this; + } + this.add(token); + return this; +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from './base.mjs'; + +export {Queue} + +/** + * You can create the instance via the monster namespace `new Monster.Types.Queue()`. + * + * ``` + * <script type="module"> + * import {Queue} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/queue.mjs'; + * new Queue() + * </script> + * ``` + * + * @example + * + * import {Queue} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/queue.mjs'; + * + * const queue = new Queue; + * + * queue.add(2); + * queue.add(true); + * queue.add("Hello"); + * queue.add(4.5); + * + * console.log(queue.poll()); + * // ↦ 2 + * console.log(queue.poll()); + * // ↦ true + * console.log(queue.poll()); + * // ↦ "Hello" + * console.log(queue.poll()); + * // ↦ 4.5 + * console.log(queue.poll()); + * // ↦ undefined + * + * + * @since 1.4.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @summary A Queue (Fifo) + */ +class Queue extends Base { + + /** + * + */ + constructor() { + super(); + this.data = []; + } + + + /** + * @return {boolean} + */ + isEmpty() { + return this.data.length === 0; + } + + /** + * Read the element at the front of the queue without removing it. + * + * @return {*} + */ + peek() { + if (this.isEmpty()) { + return undefined; + } + + return this.data[0]; + } + + /** + * Add a new element to the end of the queue. + * + * @param {*} value + * @returns {Queue} + */ + add(value) { + this.data.push(value) + return this; + } + + /** + * remove all entries + * + * @returns {Queue} + */ + clear() { + this.data = []; + return this; + } + + /** + * Remove the element at the front of the queue + * If the queue is empty, return undefined. + * + * @return {*} + */ + poll() { + if (this.isEmpty()) { + return undefined; + } + return this.data.shift(); + } + + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from './base.mjs'; + +export {Stack} + +/** + * You can call the method via the monster namespace `new Monster.Types.Queue()`. + * + * ``` + * <script type="module"> + * import {ID} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/stack.mjs'; + * console.log(new Stack()) + * </script> + * ``` + * + * @since 1.4.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +class Stack extends Base { + + /** + * + */ + constructor() { + super(); + this.data = []; + } + + + /** + * @return {boolean} + */ + isEmpty() { + return this.data.length === 0; + } + + /** + * looks at the object at the top of this stack without removing it from the stack. + * + * @return {*} + */ + peek() { + if (this.isEmpty()) { + return undefined; + } + + return this.data?.[this.data.length - 1]; + } + + /** + * pushes an item onto the top of this stack. + * + * @param {*} value + * @returns {Queue} + */ + push(value) { + this.data.push(value) + return this; + } + + /** + * remove all entries + * + * @returns {Queue} + */ + clear() { + this.data = []; + return this; + } + + /** + * removes the object at the top of this stack and returns + * that object as the value of this function. is the stack empty + * the return value is undefined. + * + * @return {*} + */ + pop() { + if (this.isEmpty()) { + return undefined; + } + return this.data.pop(); + } + + +} +'use strict'; + +/** + * @author schukai GmbH + */ +import {validateString} from "./validate.mjs"; + +export {toBinary, fromBinary} + +/** + * You can call the function via the monster namespace `Monster.Types.toBinary()`. + * + * ``` + * <script type="module"> + * import {toBinary} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/binary.mjs'; + * toBinary() + * </script> + * ``` + * + * @param {String} binary + * @return {String} + * @throws {TypeError} value is not a string + * @memberOf Monster.Types + * @since 1.18.0 + */ +function toBinary(string) { + const codeUnits = new Uint16Array(validateString(string).length); + for (let i = 0; i < codeUnits.length; i++) { + codeUnits[i] = string.charCodeAt(i); + } + + const charCodes = new Uint8Array(codeUnits.buffer); + let result = ''; + + for (let i = 0; i < charCodes.byteLength; i++) { + result += String.fromCharCode(charCodes[i]); + } + + return result; +} + +/** + * You can call the function via the monster namespace `Monster.Types.fromBinary()`. + * + * ``` + * <script type="module"> + * import {fromBinary} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/binary.mjs'; + * fromBinary() + * </script> + * ``` + * + * @param {String} binary + * @return {String} + * @throws {TypeError} value is not a string + * @memberOf Monster.Types + * @since 1.18.0 + */ +function fromBinary(binary) { + const bytes = new Uint8Array(validateString(binary).length); + for (let i = 0; i < bytes.length; i++) { + bytes[i] = binary.charCodeAt(i); + } + const charCodes = new Uint16Array(bytes.buffer); + let result = ''; + for (let i = 0; i < charCodes.length; i++) { + result += String.fromCharCode(charCodes[i]); + } + return result; +} + +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../constants.mjs"; +import {extend} from "../data/extend.mjs"; +import {Pathfinder} from "../data/pathfinder.mjs"; + +import {Base} from "./base.mjs"; +import {validateObject} from "./validate.mjs"; + +export {BaseWithOptions} + +/** + * This is the base class with options from which some monster classes are derived. + * + * This class is actually only used as a base class. + * + * ```html + * <script type="module"> + * import {BaseWithOptions} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/basewithoptions.mjs'; + * new BaseWithOptions() + * </script> + * ``` + * + * Classes that require the possibility of options can be derived directly from this class. + * Derived classes almost always override the `defaul` getter with their own values. + * + * ```javascript + * class My extends BaseWithOptions { + * get defaults() { + * return Object.assign({}, super.defaults, { + * mykey: true + * }); + * } + * } + * ``` + * + * The class was formerly called Object. + * + * @since 1.13.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +class BaseWithOptions extends Base { + + /** + * + * @param {object} options + */ + constructor(options) { + super(); + + if (options === undefined) { + options = {}; + } + + this[internalSymbol] = extend({}, this.defaults, validateObject(options)); + + } + + /** + * This getter provides the options. Derived classes overwrite + * this getter with their own values. It is good karma to always include + * the values from the parent class. + * + * ```javascript + * get defaults() { + * return Object.assign({}, super.defaults, { + * mykey: true + * }); + * } + * + * ``` + * + * @return {object} + */ + get defaults() { + return {} + } + + /** + * nested options can be specified by path `a.b.c` + * + * @param {string} path + * @param {*} defaultValue + * @return {*} + * @since 1.10.0 + */ + getOption(path, defaultValue) { + let value; + + try { + value = new Pathfinder(this[internalSymbol]).getVia(path); + } catch (e) { + + } + + if (value === undefined) return defaultValue; + return value; + } + + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {validateString} from "./validate.mjs"; + +export {escapeString} + +/** + * This function prefixes all special characters that may appear in a regex with a slash. + * + * ``` + * <script type="module"> + * import {escapeString} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/validate.mjs'; + * escapeString() + * </script> + * ``` + * + * @param {string} value + * @return {string} + * @since 1.26.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @throws {TypeError} value is not a string + */ +function escapeString(value) { + return validateString(value) + .replace(/[|\\{}()[\]^$+*?.]/g, '\\$&') + .replace(/-/g, '\\x2d'); +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from './base.mjs'; +import {isArray, isObject, isPrimitive} from "./is.mjs"; +import {Observer} from "./observer.mjs"; +import {ObserverList} from "./observerlist.mjs"; +import {validateObject} from "./validate.mjs"; +import {extend} from "../data/extend.mjs"; + +export {ProxyObserver} + +/** + * An observer manages a callback function + * + * ``` + * <script type="module"> + * import {ProxyObserver} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/proxyobserver.mjs'; + * new ProxyObserver() + * </script> + * ``` + * + * With the ProxyObserver you can attach observer for observation. with each change at the object to be observed an update takes place. + * + * this also applies to nested objects. + * + * @example + * + * import {ProxyObserver} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/proxyobserver.mjs'; + * import {Observer} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/observer.mjs'; + * import {isObject} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/is.mjs'; + * + * const o = new Observer(function () { + * if (isObject(this) && this instanceof ProxyObserver) { + * // do something (this ist ProxyObserver) + * const subject = this.getSubject(); + * console.log(subject); + * } + * }); + * + * let realSubject = { + * a: { + * b: { + * c: true + * }, + * d: 9 + * } + * } + * + * const p = new ProxyObserver(realSubject); + * p.attachObserver(o); + * const s = p.getSubject(); + * s.a.b.c = false; + * + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ + class ProxyObserver extends Base { + + /** + * + * @param {object} object + * @throws {TypeError} value is not a object + */ + constructor(object) { + super(); + + this.realSubject = validateObject(object); + this.subject = new Proxy(object, getHandler.call(this)); + + this.objectMap = new WeakMap(); + this.objectMap.set(this.realSubject, this.subject); + + this.proxyMap = new WeakMap(); + this.proxyMap.set(this.subject, this.realSubject); + + this.observers = new ObserverList; + } + + /** + * get the real object + * + * changes to this object are not noticed by the observers, so you can make a large number of changes and inform the observers later. + * + * @returns {object} + */ + getSubject() { + return this.subject + } + + /** + * @since 1.24.0 + * @param {Object} obj + * @return {Monster.Types.ProxyObserver} + */ + setSubject(obj) { + + let i, k = Object.keys(this.subject); + for (i = 0; i < k.length; i++) { + delete this.subject[k[i]]; + } + + this.subject = extend(this.subject, obj); + return this; + } + + /** + * get the proxied object + * + * @returns {object} + */ + getRealSubject() { + return this.realSubject + } + + /** + * attach a new observer + * + * @param {Observer} observer + * @returns {ProxyObserver} + */ + attachObserver(observer) { + this.observers.attach(observer) + return this; + } + + /** + * detach a observer + * + * @param {Observer} observer + * @returns {ProxyObserver} + */ + detachObserver(observer) { + this.observers.detach(observer) + return this; + } + + /** + * notify all observer + * + * @returns {Promise} + */ + notifyObservers() { + return this.observers.notify(this); + } + + /** + * @param {Observer} observer + * @returns {boolean} + */ + containsObserver(observer) { + return this.observers.contains(observer) + } + +} + +/** + * + * @returns {{defineProperty: (function(*=, *=, *=): *), setPrototypeOf: (function(*, *=): boolean), set: (function(*, *, *, *): boolean), get: ((function(*=, *=, *=): (undefined))|*), deleteProperty: ((function(*, *): (boolean))|*)}} + * @private + * @see {@link https://gitlab.schukai.com/-/snippets/49} + */ +function getHandler() { + + const proxy = this; + + // https://262.ecma-international.org/9.0/#sec-proxy-object-internal-methods-and-internal-slots + const handler = { + + // https://262.ecma-international.org/9.0/#sec-proxy-object-internal-methods-and-internal-slots-get-p-receiver + get: function (target, key, receiver) { + + const value = Reflect.get(target, key, receiver); + + if (typeof key === "symbol") { + return value; + } + + if (isPrimitive(value)) { + return value; + } + + // set value as proxy if object or array + if ((isArray(value) || isObject(value))) { + if (proxy.objectMap.has(value)) { + return proxy.objectMap.get(value); + } else if (proxy.proxyMap.has(value)) { + return value; + } else { + let p = new Proxy(value, handler); + proxy.objectMap.set(value, p); + proxy.proxyMap.set(p, value); + return p; + } + + } + + return value; + + }, + + // https://262.ecma-international.org/9.0/#sec-proxy-object-internal-methods-and-internal-slots-set-p-v-receiver + set: function (target, key, value, receiver) { + + if (proxy.proxyMap.has(value)) { + value = proxy.proxyMap.get(value); + } + + if (proxy.proxyMap.has(target)) { + target = proxy.proxyMap.get(target); + } + + let current = Reflect.get(target, key, receiver); + if (proxy.proxyMap.has(current)) { + current = proxy.proxyMap.get(current); + } + + if (current === value) { + return true; + } + + let result; + let descriptor = Reflect.getOwnPropertyDescriptor(target, key); + + if (descriptor === undefined) { + descriptor = { + writable: true, + enumerable: true, + configurable: true + } + } + + descriptor['value'] = value; + result = Reflect.defineProperty(target, key, descriptor); + + if (typeof key !== "symbol") { + proxy.observers.notify(proxy); + } + + return result; + }, + + + // https://262.ecma-international.org/9.0/#sec-proxy-object-internal-methods-and-internal-slots-delete-p + deleteProperty: function (target, key) { + if (key in target) { + delete target[key]; + + if (typeof key !== "symbol") { + proxy.observers.notify(proxy); + } + + return true; + } + return false; + }, + + // https://262.ecma-international.org/9.0/#sec-proxy-object-internal-methods-and-internal-slots-defineownproperty-p-desc + defineProperty: function (target, key, descriptor) { + + let result = Reflect.defineProperty(target, key, descriptor); + if (typeof key !== "symbol") { + proxy.observers.notify(proxy); + } + return result; + }, + + // https://262.ecma-international.org/9.0/#sec-proxy-object-internal-methods-and-internal-slots-setprototypeof-v + setPrototypeOf: function (target, key) { + let result = Reflect.setPrototypeOf(object1, key); + + if (typeof key !== "symbol") { + proxy.observers.notify(proxy); + } + + return result; + } + + }; + + + return handler; +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {isArray, isInstance} from "./is.mjs"; +import {Node} from "./node.mjs"; +import {validateInstance} from "./validate.mjs"; + +export {NodeList} + +/** + * You can create the instance via the monster namespace `new Monster.Types.NodeList()`. + * + * ``` + * <script type="module"> + * import {NodeList} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/nodelist.mjs'; + * new NodeList() + * </script> + * ``` + * + * @since 1.26.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @summary A NodeList class + */ +class NodeList extends Set { + + /** + * @throws {Error} invalid value type + * @param {NodeList|Node|Array<Node>}values + */ + constructor(values) { + super(); + + const self = this + + if (values === undefined) return; + + if (isArray(values)) { + values.forEach(value => self.add(value)); + } else if (isInstance(values, NodeList)) { + values.forEach(value => self.add(value)); + } else if (isInstance(values, Node)) { + self.add(values); + } else { + throw new Error('invalid value type'); + } + } + + /** + * + * @param {Node} node + * @return {Monster.Types.NodeList} + */ + add(node) { + super.add(validateInstance(node, Node)); + return this; + } + + /** + * @param {Node} node + * @returns {NodeList} + */ + remove(node) { + super.delete(validateInstance(node, Node)); + return this; + } + + /** + * @param {Node} node + * @returns {boolean} + */ + has(node) { + return super.has(validateInstance(node, Node)); + return false; + } + + /** + * @returns {NodeList} + */ + clear() { + super.clear(); + return this; + } + + /** + * @returns {NodeList} + */ + toArray() { + return Array.from(this); + } + + /** + * @returns {NodeList} + */ + toJSON() { + return this.toArray(); + } + + /** + * @returns {NodeList} + */ + toString() { + let parts = []; + + for (const node of this.toArray()) { + parts.push(node.toString()) + } + + return parts.join("\n"); + } + + get length() { + return super.size; + } +} + +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from './base.mjs'; + +export {Version, getMonsterVersion} + +/** + * The version object contains a sematic version number + * + * ``` + * <script type="module"> + * import {Version} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/version.mjs'; + * console.log(new Version('1.2.3')) // ↦ 1.2.3 + * console.log(new Version('1')) // ↦ 1.0.0 + * </script> + * ``` + * + * @example + * + * import {Version} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/version.mjs'; + * + * new Version('1.0.0') // ↦ 1.0.0 + * new Version(1) // ↦ 1.0.0 + * new Version(1, 0, 0) // ↦ 1.0.0 + * new Version('1.2.3', 4, 5) // ↦ 1.4.5 + * + * @since 1.0.0 + * @author schukai GmbH + * @copyright schukai GmbH + * @memberOf Monster.Types + * @summary The version object contains a sematic version number + */ +class Version extends Base { + + /** + * + * @param major + * @param minor + * @param patch + * @throws {Error} major is not a number + * @throws {Error} minor is not a number + * @throws {Error} patch is not a number + */ + constructor(major, minor, patch) { + super(); + + if (typeof major === 'string' && minor === undefined && patch === undefined) { + + let parts = major.toString().split('.'); + major = parseInt(parts[0] || 0); + minor = parseInt(parts[1] || 0); + patch = parseInt(parts[2] || 0); + } + + if (major === undefined) { + throw new Error("major version is undefined"); + } + + if (minor === undefined) { + minor = 0; + } + + if (patch === undefined) { + patch = 0; + } + + this.major = parseInt(major); + this.minor = parseInt(minor); + this.patch = parseInt(patch); + + if (isNaN(this.major)) { + throw new Error("major is not a number"); + } + + if (isNaN(this.minor)) { + throw new Error("minor is not a number"); + } + + if (isNaN(this.patch)) { + throw new Error("patch is not a number"); + } + + } + + /** + * + * @returns {string} + */ + toString() { + return this.major + '.' + this.minor + '.' + this.patch; + } + + /** + * returns 0 if equal, -1 if the object version is less and 1 if greater + * then the compared version + * + * @param {string|Version} version Version to compare + * @returns {number} + */ + compareTo(version) { + + if (version instanceof Version) { + version = version.toString(); + } + + if (typeof version !== 'string') { + throw new Error("type exception"); + } + + if (version === this.toString()) { + return 0; + } + + let a = [this.major, this.minor, this.patch]; + let b = version.split('.'); + let len = Math.max(a.length, b.length); + + for (let i = 0; i < len; i += 1) { + if ((a[i] && !b[i] && parseInt(a[i]) > 0) || (parseInt(a[i]) > parseInt(b[i]))) { + return 1; + } else if ((b[i] && !a[i] && parseInt(b[i]) > 0) || (parseInt(a[i]) < parseInt(b[i]))) { + return -1; + } + } + + return 0; + }; + +} + +let monsterVersion; + +/** + * Version of monster + * + * You can call the method via the monster namespace `Monster.getVersion()`. + * + * ``` + * <script type="module"> + * import {Monster} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source//monster.mjs'; + * console.log(Monster.getVersion()) + * </script> + * ``` + * + * Alternatively, you can also integrate this function individually. + * + * ``` + * <script type="module"> + * import {getMonsterVersion} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/version.mjs'; + * console.log(getMonsterVersion()) + * </script> + * ``` + * + * @returns {Monster.Types.Version} + * @since 1.0.0 + * @copyright schukai GmbH + * @author schukai GmbH + * @memberOf Monster + */ +function getMonsterVersion() { + if (monsterVersion instanceof Version) { + return monsterVersion; + } + /**#@+ dont touch, replaced by make with package.json version */ + monsterVersion = new Version('1.31.0') + /**#@-*/ + + return monsterVersion; + +} +'use strict'; + +/** + * Namespace for types. + * + * @namespace Monster.Types + * @memberOf Monster + * @author schukai GmbH + */ +const ns = {}; + +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from './base.mjs'; +import {isPrimitive} from "./is.mjs"; +import {NodeList} from './nodelist.mjs'; +import {validateInstance} from './validate.mjs'; + +export {Node} + +/** + * @private + * @type {symbol} + */ +const internalValueSymbol = Symbol('internalData'); + +/** + * @private + * @type {symbol} + */ +const treeStructureSymbol = Symbol('treeStructure'); + + +/** + * You can create the instance via the monster namespace `new Monster.Types.Node()`. + * + * ``` + * <script type="module"> + * import {Node} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/node.mjs'; + * new Node() + * </script> + * ``` + * + * @since 1.26.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @summary A Node Class + * @see https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Iteration_protocols + */ +class Node extends Base { + + /** + * @param {*} [value] + */ + constructor(value) { + super(); + this[internalValueSymbol] = value; + + this[treeStructureSymbol] = { + parent: null, + childNodes: new NodeList, + level: 0 + } + + } + + /** + * @property {*} + */ + get value() { + return this[internalValueSymbol]; + } + + /** + * @property {*} + */ + set value(value) { + this[internalValueSymbol] = value; + } + + /** + * @property {Monster.Types.Node|null} + */ + get parent() { + return this[treeStructureSymbol].parent; + } + + /** + * @property {integer} + */ + get level() { + return this[treeStructureSymbol].level; + } + + /** + * + * @property {NodeList} + */ + get childNodes() { + return this[treeStructureSymbol].childNodes; + } + + /** + * + * @property {NodeList} + */ + set childNodes(childNodes) { + this[treeStructureSymbol].childNodes = validateInstance(childNodes, NodeList); + setChildLevelAndParent.call(this, this, 1); + } + + /** + * @return {Monster.Types.Node} + * @param {Node} node + */ + appendChild(node) { + this[treeStructureSymbol].childNodes.add(validateInstance(node, Node)); + node[treeStructureSymbol].parent = this; + + node[treeStructureSymbol].level = this.level + 1; + setChildLevelAndParent.call(this, node, 1); + return this; + } + + /** + * @return {Monster.Types.Node} + * @param {Node} node + */ + removeChild(node) { + this[treeStructureSymbol].childNodes.remove(validateInstance(node, Node)); + node[treeStructureSymbol].parent = null; + + node[treeStructureSymbol].level = 0; + setChildLevelAndParent.call(this, node, -1); + return this; + } + + /** + * + * @return {boolean} + */ + hasChildNodes() { + return this[treeStructureSymbol].childNodes.length > 0; + } + + /** + * @return {Monster.Types.Node} + * @param {Node} node + */ + hasChild(node) { + return this[treeStructureSymbol].childNodes.has(validateInstance(node, Node)); + } + + /** + * @since 1.28.0 + * @return {string} + */ + toString() { + + let parts = []; + if (this[internalValueSymbol]) { + let label = this[internalValueSymbol]; + if (!isPrimitive(label)) label = JSON.stringify(this[internalValueSymbol]) + + parts.push(label); + } + + if (!this.hasChildNodes()) { + return parts.join("\n"); + } + + let count = this.childNodes.length, + counter = 0; + + for (const node of this.childNodes) { + counter++; + const prefix = (count === counter ? '└' : '├').padStart(2 * node.level, ' |'); + parts.push(prefix + node.toString()); + } + + return parts.join("\n"); + } + +} + +/** + * @private + * @param {Node} node + * @param {int} operand + * @return {setChildLevelAndParent} + */ +function setChildLevelAndParent(node, operand) { + const self = this; + + if (node !== this) { + node[treeStructureSymbol].parent = this + } + + node[treeStructureSymbol].childNodes.forEach(function (child) { + child[treeStructureSymbol].parent = node; + child[treeStructureSymbol].level = node[treeStructureSymbol].level + operand; + setChildLevelAndParent.call(self, child, operand); + }); + return this; +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../constants.mjs"; + +import {Base} from './base.mjs'; +import {isInstance} from "./is.mjs"; +import {Node} from "./node.mjs"; +import {NodeList} from "./nodelist.mjs"; +import {validateInstance} from "./validate.mjs"; + +export {NodeRecursiveIterator} + +/** + * @private + * @type {symbol} + */ +const isNodeListSymbol = Symbol('isNodeList'); + +/** + * You can create the instance via the monster namespace `new Monster.Types.NodeRecursiveIterator()`. + * + * ``` + * <script type="module"> + * import {NodeRecursiveIterator} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/noderecursiveiterator.mjs'; + * new NodeRecursiveIterator() + * </script> + * ``` + * + * @example + * import {Node} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/node.mjs'; + * import {NodeRecursiveIterator} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/noderecursiveiterator.mjs'; + * + * const node =new Node('1') + * + * // 1 + * // 2 + * // ├ 2.1 + * // ├ 2.2 + * // └ 2.3 + * // 3 + * // 4 + * // ├ 4.1 + * // └ 4.2 + * node.appendChild( + * (new Node('2')) + * .appendChild(new Node('2.1')) + * .appendChild(new Node('2.2')) + * .appendChild(new Node('2.3'))) + * .appendChild(new Node('3')) + * .appendChild(new Node('4') + * .appendChild(new Node('4.1')) + * .appendChild(new Node('4.2'))); + * + * const iterator = new NodeRecursiveIterator(node); + * + * const result = []; + * for (const n of iterator) { + * result.push(n.value); + * } + * + * // ↦ ['1', '2', '2.1', '2.2', '2.3', '3', '4', '4.1', '4.2'] + * + * @since 1.26.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @summary An iterator to run recursively through a tree of nodes + */ + class NodeRecursiveIterator extends Base { + + /** + * @param {Node} [data] + */ + constructor(node) { + super(); + + this[isNodeListSymbol] = false; + + // iterator is a nodelist + if (isInstance(node, NodeList)) { + let children = node; + node = new Node(); + node.childNodes = children; + this[isNodeListSymbol] = true; + } + + this[internalSymbol] = validateInstance(node, Node); + } + + /** + * @private + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield + */ + [Symbol.iterator] = function* () { + + /** + * The end of the generator function is reached. In this case, execution of the generator + * ends and an IteratorResult is returned to the caller in which the value is undefined and done is true. + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield + */ + if (this[internalSymbol] === undefined) { + return; + } + + // iterator is a nodelist and the main node is only a placeholder + if (this[isNodeListSymbol] !== true) { + yield this[internalSymbol]; + } + + if (this[internalSymbol].hasChildNodes()) { + let childNodes = this[internalSymbol].childNodes; + + for (let node of childNodes) { + yield* new NodeRecursiveIterator(node); + } + } + + return; + } + + /** + * @param {function} callback + * @return {Monster.Types.NodeRecursiveIterator} + */ + forEach(callback) { + for (const node of this) { + callback(node); + } + return this; + } + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {validateFunction, validateObject, validateString} from "./validate.mjs"; + +export {getGlobal, getGlobalObject, getGlobalFunction} + +/** + * @type {objec} + * @private + */ +let globalReference; + +/** + * @private + * @throws {Error} unsupported environment. + */ +(function () { + + if (typeof globalThis === 'object') { + globalReference = globalThis; + return; + } + + if (typeof self !== 'undefined') { + globalReference = self; + return; + } else if (typeof window !== 'undefined') { + globalReference = window; + return; + } + + Object.defineProperty(Object.prototype, '__monster__', { + get: function () { + return this; + }, + configurable: true + }); + + if (typeof __monster__ === 'object') { + __monster__.globalThis = __monster__; + delete Object.prototype.__monster__; + + globalReference = globalThis; + return; + } + + try { + globalReference = Function('return this')(); + } catch (e) { + + } + + throw new Error("unsupported environment.") + + +}()); + +/** + * Return globalThis + * + * If globalThis is not available, it will be polyfilled + * + * @since 1.6.0 + * @memberOf Monster.Types + * @returns {objec} globalThis + */ +function getGlobal() { + return globalReference; +} + +/** + * Return global object or throw Error + * + * You can call the method via the monster namespace `Monster.Types.getGlobalObject()`. + * + * ``` + * <script type="module"> + * import {Monster} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@1.30.0/dist/monster.mjs'; + * Monster.Types.getGlobalObject('document') + * // ↦ { } + * </script> + * ``` + * + * Alternatively, you can also integrate this function individually. + * + * ``` + * <script type="module"> + * import {getGlobalObject} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@1.30.0/dist/modules/types/global.mjs'; + * getGlobalObject('document') + * // ↦ { } + * </script> + * ``` + * + * @since 1.6.0 + * @memberOf Monster.Types + * @param {string} name + * @returns {objec} + * @throws {Error} the object is not defined + * @throws {TypeError} value is not a object + * @throws {TypeError} value is not a string + */ +function getGlobalObject(name) { + validateString(name); + let o = globalReference?.[name]; + if (typeof o === 'undefined') throw new Error('the object ' + name + ' is not defined'); + validateObject(o); + return o; +} + +/** + * Return global function or throw Error + * + * You can call the method via the monster namespace `Monster.Types.getGlobalFunction()`. + * + * ``` + * <script type="module"> + * import {Monster} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@1.30.0/dist/monster.mjs'; + * console.log(Monster.Types.getGlobalFunction('parseInt')) // ↦ f parseInt() { } + * </script> + * ``` + * + * Alternatively, you can also integrate this function individually. + * + * ``` + * <script type="module"> + * import {getGlobalFunction} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@1.30.0/dist/modules/types/global.mjs'; + * console.log(getGlobalFunction('parseInt')) // ↦ f parseInt() { } + * </script> + * ``` + * + * @since 1.6.0 + * @memberOf Monster.Types + * @param {string} name + * @return {objec} + * @throws {TypeError} value is not a function + * @throws {Error} the function is not defined + * @throws {TypeError} value is not a string + */ +function getGlobalFunction(name) { + validateString(name); + let f = globalReference?.[name]; + if (typeof f === 'undefined') throw new Error('the function ' + name + ' is not defined'); + validateFunction(f); + return f; +} + + + + +'use strict'; + + +/** + * @author schukai GmbH + */ + +import {Base} from "./base.mjs"; +import {isString} from "./is.mjs"; +import {MediaType, parseMediaType} from "./mediatype.mjs"; +import {validateBoolean, validateInstance, validateString} from "./validate.mjs"; + +export {DataUrl, parseDataURL} + +/** + * @private + * @type {symbol} + */ +const internal = Symbol('internal'); + + +/** + * You can create an object via the monster namespace `new Monster.Types.DataUrl()`. + * + * ``` + * <script type="module"> + * import {DataUrl} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/dataurl.mjs'; + * new DataUrl() + * </script> + * ``` + * + * @since 1.8.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs + * @see https://datatracker.ietf.org/doc/html/rfc2397 + */ +class DataUrl extends Base { + + /** + * + * @param {String} content + * @param {String|Monster.Types.MediaType} mediatype + * @param {boolean} base64=true + */ + constructor(content, mediatype, base64) { + super(); + + if (isString(mediatype)) { + mediatype = parseMediaType(mediatype); + } + + this[internal] = { + content: validateString(content), + mediatype: validateInstance(mediatype, MediaType), + base64: validateBoolean(base64 === undefined ? true : base64) + } + + + } + + get content() { + return this[internal].base64 ? atob(this[internal].content) : this[internal].content; + } + + get mediatype() { + return this[internal].mediatype; + } + + + /** + * + * @return {string} + * @see https://datatracker.ietf.org/doc/html/rfc2397 + */ + toString() { + + let content = this[internal].content; + + if (this[internal].base64 === true) { + content = ';base64,' + content; + } else { + content = ',' + encodeURIComponent(content); + } + + return 'data:' + this[internal].mediatype.toString() + content; + } + +} + +/** + * You can call the function via the monster namespace `Monster.Types.parseDataURL()`. + * + * ``` + * <script type="module"> + * import {parseDataURL} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/dataurl.mjs'; + * parseDataURL(...) + * </script> + * ``` + * + * Specification: + * + * ``` + * dataurl := "data:" [ mediatype ] [ ";base64" ] "," data + * mediatype := [ type "/" subtype ] *( ";" parameter ) + * data := *urlchar + * parameter := attribute "=" value + * ``` + * + * @param {String} dataurl + * @return {Monster.Types.DataUrl} + * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs + * @see https://datatracker.ietf.org/doc/html/rfc2397 + * @throws {TypeError} incorrect or missing data protocol + * @throws {TypeError} malformed data url + * @memberOf Monster.Types + */ +function parseDataURL(dataurl) { + + validateString(dataurl); + + dataurl = dataurl.trim(); + + if (dataurl.substring(0, 5) !== 'data:') { + throw new TypeError('incorrect or missing data protocol') + } + + dataurl = dataurl.substring(5); + + let p = dataurl.indexOf(','); + if (p === -1) { + throw new TypeError('malformed data url') + } + + let content = dataurl.substring(p + 1); + let mediatypeAndBase64 = dataurl.substring(0, p).trim(); + let mediatype = 'text/plain;charset=US-ASCII'; + let base64Flag = false; + + if (mediatypeAndBase64 !== "") { + mediatype = mediatypeAndBase64; + if (mediatypeAndBase64.endsWith('base64')) { + let i = mediatypeAndBase64.lastIndexOf(';'); + mediatype = mediatypeAndBase64.substring(0, i); + base64Flag = true; + } else { + content = decodeURIComponent(content); + } + + mediatype = parseMediaType(mediatype); + } else { + content = decodeURIComponent(content); + } + + return new DataUrl(content, mediatype, base64Flag); + + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import { + isArray, + isBoolean, + isFunction, + isInstance, + isInteger, + isIterable, + isObject, + isPrimitive, + isString, + isSymbol +} from './is.mjs'; + +export { + validateIterable, + validatePrimitive, + validateBoolean, + validateString, + validateObject, + validateInstance, + validateArray, + validateSymbol, + validateFunction, + validateInteger +} + +/** + * This method checks if the type matches the primitive type. this function is identical to isPrimitive() except that a TypeError is thrown. + * + * ``` + * <script type="module"> + * import {validateIterable} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/validate.mjs'; + * console.log(validateIterable('2')) // ↦ TypeError + * console.log(validateIterable([])) // ↦ value + * </script> + * ``` + * + * @param {*} value + * @return {*} + * @since 1.2.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @throws {TypeError} value is not a primitive + * @see {@link isPrimitive} + * @see {@link Monster.Types.isPrimitive} + * @see {@link Monster.Types#isPrimitive} + */ +function validateIterable(value) { + if (!isIterable(value)) { + throw new TypeError('value is not iterable') + } + return value +} + +/** + * This method checks if the type matches the primitive type. this function is identical to isPrimitive() except that a TypeError is thrown. + * + * ``` + * <script type="module"> + * import {validatePrimitive} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/validate.mjs'; + * console.log(validatePrimitive('2')) // ↦ value + * console.log(validatePrimitive([])) // ↦ TypeError + * </script> + * ``` + * + * @param {*} value + * @return {*} + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @throws {TypeError} value is not a primitive + * @see {@link isPrimitive} + * @see {@link Monster.Types.isPrimitive} + * @see {@link Monster.Types#isPrimitive} + */ +function validatePrimitive(value) { + if (!isPrimitive(value)) { + throw new TypeError('value is not a primitive') + } + return value +} + +/** + * This method checks if the type matches the boolean type. this function is identical to isBoolean() except that a TypeError is thrown. + * + * ``` + * <script type="module"> + * import {validateBoolean} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/validate.mjs'; + * console.log(validateBoolean(false)) // ↦ value + * console.log(validateBoolean('2')) // ↦ TypeError + * console.log(validateBoolean([])) // ↦ TypeError + * </script> + * ``` + * + * @param {*} value + * @return {*} + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + + * @throws {TypeError} value is not primitive + */ +function validateBoolean(value) { + if (!isBoolean(value)) { + throw new TypeError('value is not a boolean') + } + return value +} + +/** + * This method checks if the type matches the string type. this function is identical to isString() except that a TypeError is thrown. + * + * ``` + * <script type="module"> + * import {validateString} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/validate.mjs'; + * console.log(validateString('2')) // ↦ value + * console.log(validateString([])) // ↦ TypeError + * </script> + * ``` + * + * @param {*} value + * @return {*} + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @throws {TypeError} value is not a string + */ +function validateString(value) { + if (!isString(value)) { + throw new TypeError('value is not a string') + } + return value +} + + +/** + * This method checks if the type matches the object type. this function is identical to isObject() except that a TypeError is thrown. + * + * ``` + * <script type="module"> + * import {validateObject} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/validate.mjs'; + * console.log(validateObject({})) // ↦ value + * console.log(validateObject('2')) // ↦ TypeError + * console.log(validateObject([])) // ↦ TypeError + * </script> + * ``` + * + * @param {*} value + * @return {*} + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @throws {TypeError} value is not a object + */ +function validateObject(value) { + if (!isObject(value)) { + throw new TypeError('value is not a object') + } + return value +} + +/** + * This method checks if the type matches the object instance. + * + * ``` + * <script type="module"> + * import {validateInstance} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/validate.mjs'; + * console.log(validateInstance({}, Object)) // ↦ value + * console.log(validateInstance('2', Object)) // ↦ TypeError + * console.log(validateInstance([], Object)) // ↦ TypeError + * </script> + * ``` + * + * @param {*} value + * @return {*} + * @since 1.5.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @throws {TypeError} value is not an instance of + */ +function validateInstance(value, instance) { + if (!isInstance(value, instance)) { + let n = ""; + if (isObject(instance) || isFunction(instance)) { + n = instance?.['name'] + } + + if (n) { + n = " " + n; + } + + throw new TypeError('value is not an instance of' + n) + } + return value +} + +/** + * This method checks if the type matches the array type. this function is identical to isArray() except that a TypeError is thrown. + * + * ``` + * <script type="module"> + * import {validateArray} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/validate.mjs'; + * console.log(validateArray('2')) // ↦ TypeError + * console.log(validateArray([])) // ↦ value + * </script> + * ``` + * + * @param {*} value + * @return {*} + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @throws {TypeError} value is not an array + */ +function validateArray(value) { + if (!isArray(value)) { + throw new TypeError('value is not an array') + } + return value +} + +/** + * This method checks if the type matches the symbol type. this function is identical to isSymbol() except that a TypeError is thrown. + * + * ``` + * <script type="module"> + * import {validateSymbol} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/validate.mjs'; + * console.log(validateSymbol('2')) // ↦ TypeError + * console.log(validateSymbol()) // ↦ value + * </script> + * ``` + * + * @param {*} value + * @return {*} + * @since 1.9.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @throws {TypeError} value is not an symbol + */ +function validateSymbol(value) { + if (!isSymbol(value)) { + throw new TypeError('value is not an symbol') + } + return value +} + +/** + * This method checks if the type matches the function type. this function is identical to isFunction() except that a TypeError is thrown. + * + * ``` + * <script type="module"> + * import {validateFunction} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/validate.mjs'; + * console.log(validateFunction(()=>{})) // ↦ value + * console.log(validateFunction('2')) // ↦ TypeError + * console.log(validateFunction([])) // ↦ TypeError + * </script> + * ``` + * + * @param {*} value + * @return {*} + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @throws {TypeError} value is not a function + */ +function validateFunction(value) { + if (!isFunction(value)) { + throw new TypeError('value is not a function') + } + return value +} + +/** + * This method checks if the type is an integer. this function is identical to isInteger() except that a TypeError is thrown. + * + * ``` + * <script type="module"> + * import {validateFunction} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/validate.mjs'; + * console.log(validateInteger(true)) // ↦ TypeError + * console.log(validateInteger('2')) // ↦ TypeError + * console.log(validateInteger(2)) // ↦ value + * </script> + * ``` + * + * @param {*} value + * @return {*} + * @since 1.4.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @throws {TypeError} value is not an integer + */ +function validateInteger(value) { + if (!isInteger(value)) { + throw new TypeError('value is not an integer') + } + return value +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Queue} from "./queue.mjs"; +import {validateObject} from "./validate.mjs"; + +export {UniqueQueue} + +/** + * A UniqueQueue is a queue that contains items only once. + * + * ``` + * <script type="module"> + * import {UniqueQueue} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/uniquequeue.mjs'; + * new UniqueQueue() + * </script> + * ``` + * + * @since 1.4.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @summary A queue for unique values + */ + class UniqueQueue extends Queue { + + /** + * + */ + constructor() { + super(); + this.unique = new WeakSet(); + } + + /** + * Add a new element to the end of the queue. + * + * @param {object} value + * @returns {Queue} + * @throws {TypeError} value is not a object + */ + add(value) { + + validateObject(value); + + if (!this.unique.has(value)) { + this.unique.add(value); + super.add(value); + } + + return this; + } + + /** + * remove all entries + * + * @returns {Queue} + */ + clear() { + super.clear(); + this.unique = new WeakSet; + return this; + } + + /** + * Remove the element at the front of the queue + * If the queue is empty, return undefined. + * + * @return {object} + */ + poll() { + + if (this.isEmpty()) { + return undefined; + } + let value = this.data.shift(); + this.unique.delete(value); + return value; + } + + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +export {Base} + +/** + * This is the base class from which all monster classes are derived. + * + * ``` + * <script type="module"> + * import {Base} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/base.mjs'; + * new Base() + * </script> + * ``` + * + * The class was formerly called Object. + * + * @since 1.5.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ + class Base extends Object { + + /** + * + * @returns {string} + */ + toString() { + return JSON.stringify(this); + }; + + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +export {isIterable, isPrimitive, isSymbol, isBoolean, isString, isObject, isInstance, isArray, isFunction, isInteger} + + +/** + * With this function you can check if a value is iterable. + * + * This method is used in the library to have consistent names. + * + * You can call the method via the monster namespace `Monster.Types.isPrimitive()`. + * + * ``` + * <script type="module"> + * import {isIterable} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/is.mjs'; + * isIterable(null) // ↦ false + * isIterable('hello') // ↦ true + * isIterable([]) // ↦ true + * </script> + * ``` + * + * @param {*} value + * @returns {boolean} + * @since 1.2.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +function isIterable(value) { + if (value === undefined) return false; + if (value === null) return false; + return typeof value?.[Symbol.iterator] === 'function'; +} + + +/** + * Checks whether the value passed is a primitive (string, number, boolean, NaN, undefined, null or symbol) + * + * This method is used in the library to have consistent names. + * + * ``` + * <script type="module"> + * import {isPrimitive} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/is.mjs'; + * isPrimitive('2')) // ↦ true + * isPrimitive([])) // ↦ false + * </script> + * ``` + * + * @param {*} value + * @returns {boolean} + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +function isPrimitive(value) { + var type; + + if (value === undefined || value === null) { + return true; + } + + type = typeof value; + + if (type === 'string' || type === 'number' || type === 'boolean' || type === 'symbol') { + return true; + } + + return false; +} + +/** + * Checks whether the value passed is a symbol + * + * This method is used in the library to have consistent names. + * + * ``` + * <script type="module"> + * import {isSymbol} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/is.mjs'; + * isSymbol(Symbol('a'))) // ↦ true + * isSymbol([]) // ↦ false + * </script> + * ``` + * + * @param {*} value + * @returns {boolean} + * @since 1.9.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +function isSymbol(value) { + return ('symbol' === typeof value) ? true : false; +} + +/** + * Checks whether the value passed is a boolean. + * + * This method is used in the library to have consistent names. + * + * ``` + * <script type="module"> + * import {isBoolean} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/is.mjs'; + * isBoolean('2')) // ↦ false + * isBoolean([])) // ↦ false + * isBoolean(2>4)) // ↦ true + * </script> + * ``` + * + * @param {*} value + * @returns {boolean} + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +function isBoolean(value) { + + if (value === true || value === false) { + return true; + } + + return false; +} + +/** + * Checks whether the value passed is a string + * + * This method is used in the library to have consistent names. + * + * ``` + * <script type="module"> + * import {isString} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/is.mjs'; + * isString('2')) // ↦ true + * isString([])) // ↦ false + * </script> + * ``` + * + * @param {*} value + * @returns {boolean} + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +function isString(value) { + if (value === undefined || typeof value !== 'string') { + return false; + } + return true; +} + +/** + * Checks whether the value passed is a object + * + * This method is used in the library to have consistent names. + * + * ``` + * <script type="module"> + * import {isObject} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/is.mjs'; + * isObject('2')) // ↦ false + * isObject([])) // ↦ false + * </script> + * ``` + * + * @param {*} value + * @returns {boolean} + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +function isObject(value) { + + if (isArray(value)) return false; + if (isPrimitive(value)) return false; + + if (typeof value === 'object') { + return true; + } + + return false; +} + +/** + * Checks whether the value passed is a object and instance of instance. + * + * This method is used in the library to have consistent names. + * + * ``` + * <script type="module"> + * import {isInstance} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/is.mjs'; + * isInstance('2')) // ↦ false + * isInstance([])) // ↦ false + * </script> + * ``` + * + * @param {*} value + * @param {*} instance + * @returns {boolean} + * @since 1.5.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +function isInstance(value, instance) { + if (!isObject(value)) return false; + if (!isFunction(instance)) return false; + if (!instance.hasOwnProperty('prototype')) return false; + return (value instanceof instance) ? true : false; +} + +/** + * Checks whether the value passed is a array + * + * This method is used in the library to have consistent names. + * + * ``` + * <script type="module"> + * import {isArray} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/is.mjs'; + * isArray('2')) // ↦ false + * isArray([])) // ↦ true + * </script> + * ``` + * + * @param {*} value + * @returns {boolean} + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @see https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray + */ +function isArray(value) { + return Array.isArray(value); +} + +/** + * Checks whether the value passed is a function + * + * This method is used in the library to have consistent names. + * + * ``` + * <script type="module"> + * import {isFunction} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/is.mjs'; + * isFunction(()=>{}) // ↦ true + * isFunction('2')) // ↦ false + * isFunction([])) // ↦ false + * </script> + * ``` + * + * @param {*} value + * @returns {boolean} + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +function isFunction(value) { + if (isArray(value)) return false; + if (isPrimitive(value)) return false; + + if (typeof value === 'function') { + return true; + } + + return false; + +} + +/** + * Checks whether the value passed is an integer. + * + * This method is used in the library to have consistent names. + * + * ``` + * <script type="module"> + * import {isInteger} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/is.mjs'; + * isInteger(()=>{}) // ↦ true + * isInteger('2')) // ↦ false + * isInteger(2)) // ↦ true + * </script> + * ``` + * + * @param {*} value + * @returns {boolean} + * @since 1.4.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +function isInteger(value) { + return Number.isInteger(value); +} + +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from './base.mjs'; +import {validateString} from "./validate.mjs"; + +export {ID} + +/** + * @private + * @type {Map<string, integer>} + */ +let internalCounter = new Map; + +/** + * With the id class, sequences of ids can be created. for this purpose, an internal counter is incremented for each prefix. + * thus, the first id with the prefix `myid` will be `myid1` and the second id `myid2`. + * The ids are the same for every call, for example on a web page. + * + * So the ids can also be used for navigation. you just have to take care that the order stays the same. + * + * ``` + * <script type="module"> + * import {ID} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/id.mjs'; + * console.log(new ID()) + * </script> + * ``` + * + * As of version 1.6.0 there is the new RandomID. this ID class is continuous from now on. + * + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @summary Automatic generation of ids + */ + class ID extends Base { + + /** + * create new id with prefix + * + * @param {string} prefix + */ + constructor(prefix) { + super(); + + if (prefix === undefined) { + prefix = "id"; + } + + validateString(prefix); + + if (!internalCounter.has(prefix)) { + internalCounter.set(prefix, 1); + } + + let count = internalCounter.get(prefix); + this.id = prefix + count; + + internalCounter.set(prefix, ++count); + } + + /** + * @return {string} + */ + toString() { + return this.id; + } + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {ID} from "../types/id.mjs"; +import {isObject} from "../types/is.mjs"; +import {validateString} from "../types/validate.mjs"; + +export {trimSpaces} + +/** + * This special trim function allows to trim spaces that have been protected by a special escape character. + * + * ``` + * <script type="module"> + * import {trimSpaces} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/util/trimspaces.mjs'; + * trimSpaces(' hello \\ ') + * </script> + * ``` + * + * Hint: One stroke is escaped by the javascript interpreter, the second stroke escapes the stroke. + * + * ```text + * a\ b ↦ a b + * a\\ b ↦ a\ b + * ``` + * + * @since 1.24.0 + * @memberOf Monster.Util + * @copyright schukai GmbH + * @param {string} value + * @return {string} + * @throws {TypeError} value is not a string + */ + function trimSpaces(value) { + + validateString(value); + + let placeholder = new Map; + const regex = /((?<pattern>\\(?<char>.)){1})/mig; + + // The separator for args must be escaped + // undefined string which should not occur normally and is also not a regex + let result = value.matchAll(regex) + + for (let m of result) { + let g = m?.['groups']; + if (!isObject(g)) { + continue; + } + + let p = g?.['pattern']; + let c = g?.['char']; + + if (p && c) { + let r = '__' + new ID().toString() + '__'; + placeholder.set(r, c); + value = value.replace(p, r); + } + + } + + value = value.trim(); + placeholder.forEach((v, k) => { + value = value.replace(k, '\\' + v) + }) + + return value; + +} + +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../constants.mjs"; + +import {Base} from "../types/base.mjs"; +import {isInteger} from "../types/is.mjs"; +import {validateFunction, validateInteger} from "../types/validate.mjs"; + +export {DeadMansSwitch} + +/** + * The dead man's switch allows to set a timer which can be reset again and again within a defined period of time. + * + * ``` + * <script type="module"> + * import {DeadMansSwitch} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/util/deadmansswitch.mjs'; + * new DeadMansSwitch(); + * </script> + * ``` + * + * @example + * import {DeadMansSwitch} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/util/deadmansswitch.mjs'; + * + * const deadmansswitch = new DeadMansSwitch(100, ()=>{ + * console.log('yeah!') + * // ↦ "yeah!" + * }) + * + * deadmansswitch.touch(); // from here wait again 100 ms + * deadmansswitch.touch(200); // from here wait 200 ms + * + * @copyright schukai GmbH + * @since 1.29.0 + * @memberOf Monster.Util + * @summary Class to be able to execute function chains + */ + class DeadMansSwitch extends Base { + + /** + * Create new dead man's switch + * + * @param {Integer} delay + * @param {function} callback + * @throw {TypeError} the arguments must be either integer or functions + * @throws {TypeError} value is not an integer + */ + constructor(delay, callback) { + super(); + + init.call(this, validateInteger(delay), validateFunction(callback)); + } + + /** + * + * @param {Integer|undefined} [delay] + */ + touch(delay) { + + if (this[internalSymbol]['isAlreadyRun'] === true) { + throw new Error('has already run') + } + + if (isInteger(delay)) { + this[internalSymbol]['delay'] = delay + } else if (delay !== undefined) { + throw new Error('unsupported argument') + } + + clearTimeout(this[internalSymbol]['timer']); + + initCallback.call(this); + + return this; + } +} + +/** + * @private + */ +function initCallback() { + + const self = this; + + self[internalSymbol]['timer'] = setTimeout(() => { + self[internalSymbol]['isAlreadyRun'] = true; + self[internalSymbol]['callback'](); + }, self[internalSymbol]['delay']) +} + +/** + * @private + * @param {integer} delay + * @param {function} callback + */ +function init(delay, callback) { + const self = this; + + self[internalSymbol] = { + callback, + delay, + isAlreadyRun: false, + timer: undefined + }; + + initCallback.call(self); + +} + + + +'use strict'; + +/** + * @author schukai GmbH + */ + +import {validateObject} from '../types/validate.mjs'; + +export {deepFreeze} + +/** + * Deep freeze a object + * + * ``` + * <script type="module"> + * import {deepFreeze} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/util/freeze.mjs'; + * deepFreeze({}) + * </script> + * ``` + * + * @param {object} object object to be freeze + * @since 1.0.0 + * @returns {object} + * @memberOf Monster.Util + * @copyright schukai GmbH + * @throws {TypeError} value is not a object + */ + function deepFreeze(object) { + + validateObject(object) + + // Retrieve the defined property names of the object + var propNames = Object.getOwnPropertyNames(object); + + // Freeze properties before freezing yourself + for (let name of propNames) { + let value = object[name]; + + object[name] = (value && typeof value === "object") ? + deepFreeze(value) : value; + } + + return Object.freeze(object); +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from '../types/base.mjs'; +import {isFunction} from '../types/is.mjs'; + +export {Comparator} + +/** + * The comparator allows a comparison function to be abstracted. + * + * ``` + * <script type="module"> + * import {Comparator} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/util/comparator.mjs'; + * console.log(new Comparator()) + * </script> + * ``` + * + * The following are some examples of the application of the class. + * + * ``` + * new Comparator().lessThanOrEqual(2, 5) // ↦ true + * new Comparator().greaterThan(4, 2) // ↦ true + * new Comparator().equal(4, 4) // ↦ true + * new Comparator().equal(4, 5) // ↦ false + * ``` + * + * You can also pass your own comparison function, and thus define the comparison function. + * + * ``` + * new Comparator(function (a, b) { + * if (a.v === b.v) return 0; + * return a.v < b.v ? -1 : 1; + * }).equal({v: 2}, {v: 2}); // ↦ true + * ``` + * + * @example + * + * import {Comparator} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/util/comparator.mjs'; + * + * console.log(new Comparator().lessThanOrEqual(2, 5)) + * // ↦ true + * console.log(new Comparator().greaterThan(4, 2)) + * // ↦ true + * console.log(new Comparator().equal(4, 4)) + * // ↦ true + * console.log(new Comparator().equal(4, 5)) + * // ↦ false + * + * @since 1.3.0 + * @memberOf Monster.Util + */ +class Comparator extends Base { + + /** + * create new comparator + * + * @param {Monster.Util~exampleCallback} [callback] Comparator callback + * @throw {TypeError} unsupported type + * @throw {TypeError} impractical comparison + */ + constructor(callback) { + super(); + + if (isFunction(callback)) { + this.compare = callback + } else if (callback !== undefined) { + throw new TypeError("unsupported type") + } else { + // default compare function + + /** + * + * @param {*} a + * @param {*} b + * @return {integer} -1, 0 or 1 + */ + this.compare = function (a, b) { + + if (typeof a !== typeof b) { + throw new TypeError("impractical comparison", "types/comparator.mjs") + } + + if (a === b) { + return 0; + } + return a < b ? -1 : 1; + }; + } + + } + + /** + * changes the order of the operators + * + * @return {Comparator} + */ + reverse() { + const original = this.compare; + this.compare = (a, b) => original(b, a); + return this; + } + + /** + * Checks if two variables are equal. + * + * @param {*} a + * @param {*} b + * + * @return {boolean} + */ + equal(a, b) { + return this.compare(a, b) === 0; + } + + + /** + * Checks if variable `a` is greater than `b` + * + * @param {*} a + * @param {*} b + * + * @return {boolean} + */ + greaterThan(a, b) { + return this.compare(a, b) > 0; + } + + /** + * Checks if variable `a` is greater than or equal to `b` + * + * @param {*} a + * @param {*} b + * + * @return {boolean} + */ + greaterThanOrEqual(a, b) { + return this.greaterThan(a, b) || this.equal(a, b); + } + + /** + * Checks if variable `a` is less than or equal to `b` + * + * @param {*} a + * @param {*} b + * + * @return {boolean} + */ + lessThanOrEqual(a, b) { + return this.lessThan(a, b) || this.equal(a, b); + } + + /** + * Checks if variable a is less than b + * + * @param {*} a + * @param {*} b + * + * @return {boolean} + */ + lessThan(a, b) { + return this.compare(a, b) < 0; + } + + +} + + +/** + * This is the description for the callback function used by the operator + * + * ``` + * new Comparator(function (a, b) { + * if (a.v === b.v) return 0; + * return a.v < b.v ? -1 : 1; + * }).equal({v: 2}, {v: 2}); // ↦ true + * ``` + * + * @callback Monster.Util~exampleCallback + * @param {*} a + * @param {*} b + * @return {integer} -1, 0 or 1 + * @memberOf Monster.Util + * @see Monster.Util.Comparator + */ + +'use strict'; + +/** + * Namespace for utilities. + * + * @namespace Monster.Util + * @memberOf Monster + * @author schukai GmbH + */ +const ns = {};'use strict'; + +/** + * @author schukai GmbH + */ + + +import {getGlobal} from '../types/global.mjs'; +import {isArray, isFunction, isObject, isPrimitive} from '../types/is.mjs'; +import {typeOf} from "../types/typeof.mjs"; +import {validateObject} from "../types/validate.mjs"; + +export {clone} + +/** + * With this function, objects can be cloned. + * The entire object tree is run through. + * + * Proxy, Element, HTMLDocument and DocumentFragment instances are not cloned. + * Global objects such as windows are also not cloned, + * + * If an object has a method `getClone()`, this method is used to create the clone. + * + * ``` + * <script type="module"> + * import {clone} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/util/clone.mjs'; + * clone({}) + * </script> + * ``` + * + * @param {*} obj object to be cloned + * @returns {*} + * @since 1.0.0 + * @memberOf Monster.Util + * @copyright schukai GmbH + * @throws {Error} unable to clone obj! its type isn't supported. + */ +function clone(obj) { + + // typeof null results in 'object'. https://2ality.com/2013/10/typeof-null.html + if (null === obj) { + return obj; + } + + // Handle the two simple types, null and undefined + if (isPrimitive(obj)) { + return obj; + } + + // Handle the two simple types, null and undefined + if (isFunction(obj)) { + return obj; + } + + // Handle Array + if (isArray(obj)) { + let copy = []; + for (var i = 0, len = obj.length; i < len; i++) { + copy[i] = clone(obj[i]); + } + + return copy; + } + + if (isObject(obj)) { + + + // Handle Date + if (obj instanceof Date) { + let copy = new Date(); + copy.setTime(obj.getTime()); + return copy; + } + + /** Do not clone DOM nodes */ + if (typeof Element !== 'undefined' && obj instanceof Element) return obj; + if (typeof HTMLDocument !== 'undefined' && obj instanceof HTMLDocument) return obj; + if (typeof DocumentFragment !== 'undefined' && obj instanceof DocumentFragment) return obj; + + /** Do not clone global objects */ + if (obj === getGlobal()) return obj; + if (typeof globalContext !== 'undefined' && obj === globalContext) return obj; + if (typeof window !== 'undefined' && obj === window) return obj; + if (typeof document !== 'undefined' && obj === document) return obj; + if (typeof navigator !== 'undefined' && obj === navigator) return obj; + if (typeof JSON !== 'undefined' && obj === JSON) return obj; + + // Handle Proxy-Object + try { + // try/catch because possible: TypeError: Function has non-object prototype 'undefined' in instanceof check + if (obj instanceof Proxy) { + return obj; + } + } catch (e) { + } + + return cloneObject(obj) + + } + + throw new Error("unable to clone obj! its type isn't supported."); +} + +/** + * + * @param {object} obj + * @returns {object} + * @private + */ +function cloneObject(obj) { + + validateObject(obj); + + const constructor = obj?.['constructor']; + + /** Object has clone method */ + if(typeOf(constructor)==='function') { + const prototype = constructor?.prototype; + if(typeof prototype==='object') { + if(prototype.hasOwnProperty('getClone')&& typeOf(obj.getClone) === 'function') { + return obj.getClone(); + } + } + } + + let copy = {}; + if (typeof obj.constructor === 'function' && + typeof obj.constructor.call === 'function') { + copy = new obj.constructor(); + } + + for (let key in obj) { + + if (!obj.hasOwnProperty(key)) { + continue; + } + + if (isPrimitive(obj[key])) { + copy[key] = obj[key]; + continue; + } + + copy[key] = clone(obj[key]); + } + + return copy; +} + +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../constants.mjs"; +import {Base} from "../types/base.mjs"; +import {getGlobalFunction} from "../types/global.mjs"; +import {isFunction, isInteger} from "../types/is.mjs"; +import {Queue} from "../types/queue.mjs"; +import {validateFunction, validateInteger} from "../types/validate.mjs"; + +export {Processing} + +/** + * @private + */ +class Callback { + + /** + * + * @param {function} callback + * @param {int|undefined} time + * @throws {TypeError} value is not a function + * @throws {TypeError} value is not an integer + * @private + */ + constructor(callback, time) { + this[internalSymbol] = { + callback: validateFunction(callback), + time: validateInteger(time ?? 0) + }; + } + + /** + * @private + * @param {*} data + * @return {Promise} + */ + run(data) { + const self = this; + return new Promise((resolve, reject) => { + + getGlobalFunction('setTimeout')(() => { + try { + resolve(self[internalSymbol].callback(data)); + } catch (e) { + reject(e); + } + + }, + self[internalSymbol].time); + + + }) + + } +} + +/** + * This class allows to execute several functions in order. + * + * Functions and timeouts can be passed. If a timeout is passed, it applies to all further functions. + * In the example + * + * `timeout1, function1, function2, function3, timeout2, function4` + * + * the timeout1 is valid for the functions 1, 2 and 3 and the timeout2 for the function4. + * + * So the execution time is timeout1+timeout1+timeout1+timeout2 + * + * The result of `run()` is a promise. + * + * ``` + * <script type="module"> + * import {Processing} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/util/processing.mjs'; + * new Processing(); + * </script> + * ``` + * + * @example + * import {Processing} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/util/processing.mjs'; + * + * let startTime = +new Date(); + * + * new Processing((url)=>{ + * return fetch(url) + * },(response)=>{ + * // do something with the response + * console.log(response.status, +new Date()-startTime) + * },200,()=>{ + * // this function is called 200 seconds after fetch is received. + * console.log('finished', +new Date()-startTime) + * return 'done' + * }).run('https://monsterjs.org/assets/world.json').then(r=>{ + * console.log(r) + * // ↦ "done" + * }) + * + * @copyright schukai GmbH + * @since 1.21.0 + * @memberOf Monster.Util + * @summary Class to be able to execute function chains + */ +class Processing extends Base { + + /** + * Create new Processing + * + * Functions and timeouts can be passed. If a timeout is passed, it applies to all further functions. + * In the example + * + * `timeout1, function1, function2, function3, timeout2, function4` + * + * the timeout1 is valid for the functions 1, 2 and 3 and the timeout2 for the function4. + * + * So the execution time is timeout1+timeout1+timeout1+timeout2 + * + * @param {int} timeout Timeout + * @param {function} callback Callback + * @throw {TypeError} the arguments must be either integer or functions + */ + constructor() { + super(); + + this[internalSymbol] = { + queue: new Queue + }; + + let time = 0 + + for (const [, arg] of Object.entries(arguments)) { + if (isInteger(arg) && arg >= 0) { + time = arg; + } else if (isFunction(arg)) { + this[internalSymbol].queue.add(new Callback(arg, time)) + } else { + throw new TypeError('the arguments must be either integer or functions') + } + } + + + } + + /** + * Adds a function with the desired timeout + * If no timeout is specified, the timeout of the previous function is used. + * + * @param {function} callback + * @param {int|undefined} time + * @throws {TypeError} value is not a function + * @throws {TypeError} value is not an integer + */ + add(callback, time) { + this[internalSymbol].queue.add(new Callback(callback, time)) + return this; + } + + + /** + * Executes the defined functions in order. + * + * @param {*} data + * @return {Promise} + */ + run(data) { + const self = this; + if (this[internalSymbol].queue.isEmpty()) { + return Promise.resolve(data); + } + + return this[internalSymbol].queue.poll().run(data).then((result) => { + return self.run(result); + }); + + } + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {isArray, isObject} from "../types/is.mjs"; +import {typeOf} from "../types/typeof.mjs"; + +export {extend} + +/** + * Extend copies all enumerable own properties from one or + * more source objects to a target object. It returns the modified target object. + * + * ``` + * <script type="module"> + * import {extend} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/extend.mjs'; + * extend(a, b) + * </script> + * ``` + * + * @param {object} target + * @param {object} + * @return {object} + * @since 1.10.0 + * @copyright schukai GmbH + * @memberOf Monster.Data + * @throws {Error} unsupported argument + * @throws {Error} type mismatch + */ +function extend() { + let o, i; + + for (i = 0; i < arguments.length; i++) { + let a = arguments[i]; + + if (!(isObject(a) || isArray(a))) { + throw new Error('unsupported argument ' + JSON.stringify(a)); + } + + if (o === undefined) { + o = a; + continue; + } + + for (let k in a) { + + let v = a?.[k]; + + if (v === o?.[k]) { + continue; + } + + if ((isObject(v)&&typeOf(v)==='object') || isArray(v)) { + + if (o[k] === undefined) { + if (isArray(v)) { + o[k] = []; + } else { + o[k] = {}; + } + } else { + if (typeOf(o[k]) !== typeOf(v)) { + throw new Error("type mismatch: " + JSON.stringify(o[k]) + "(" + typeOf(o[k]) + ") != " + JSON.stringify(v) + "(" + typeOf(v) + ")"); + } + } + + o[k] = extend(o[k], v); + + } else { + o[k] = v; + } + + } + } + + return o; +} +'use strict'; + +/** + * @author schukai GmbH + */ + + +import {Base} from '../types/base.mjs'; +import {validateString} from '../types/validate.mjs'; +import {Transformer} from './transformer.mjs'; + +export {Pipe} + +const DELIMITER = '|'; + +/** + * The pipe class makes it possible to combine several processing steps. + * + * ``` + * <script type="module"> + * import {Pipe} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/pipe.mjs'; + * new Pipe() + * </script> + * ``` + * + * A pipe consists of commands whose input and output are connected with the pipe symbol `|`. + * + * With the Pipe, processing steps can be combined. Here, the value of an object is accessed via the pathfinder (path command). + * the word is then converted to uppercase letters and a prefix Hello is added. the two backslash safe the space char. + * + * @example + * import {Pipe} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/pipe.mjs'; + * + * let obj = { + * a: { + * b: { + * c: { + * d: "world" + * } + * } + * } + * } + * + * console.log(new Pipe('path:a.b.c.d | toupper | prefix:Hello\\ ').run(obj)); + * // ↦ Hello WORLD + * + * @since 1.5.0 + * @copyright schukai GmbH + * @memberOf Monster.Data + */ +class Pipe extends Base { + + /** + * + * @param {string} pipe a pipe consists of commands whose input and output are connected with the pipe symbol `|`. + * @throws {TypeError} + */ + constructor(pipe) { + super(); + validateString(pipe); + + this.pipe = pipe.split(DELIMITER).map((v) => { + return new Transformer(v); + }); + + + } + + /** + * + * @param {string} name + * @param {function} callback + * @param {object} context + * @returns {Transformer} + * @throws {TypeError} value is not a string + * @throws {TypeError} value is not a function + */ + setCallback(name, callback, context) { + + for (const [, t] of Object.entries(this.pipe)) { + t.setCallback(name, callback, context); + } + + return this; + } + + /** + * run a pipe + * + * @param {*} value + * @returns {*} + */ + run(value) { + return this.pipe.reduce((accumulator, transformer, currentIndex, array) => { + return transformer.run(accumulator); + }, value); + } +} +'use strict'; + +/** + * @author schukai GmbH + */ + + +import {isFunction, isObject, isString} from "../types/is.mjs"; +import {validateString} from "../types/validate.mjs"; +import {clone} from "../util/clone.mjs"; +import {DELIMITER, Pathfinder, WILDCARD} from "./pathfinder.mjs"; + +export {buildMap, PARENT, assembleParts} + +/** + * @type {string} + * @memberOf Monster.Data + */ +const PARENT = '^'; + + +/** + * With the help of the function `buildMap()`, maps can be easily created from data objects. + * + * Either a simple definition `a.b.c` or a template `${a.b.c}` can be specified as the path. + * Key and value can be either a definition or a template. The key does not have to be defined. + * + * ``` + * <script type="module"> + * import {buildMap} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/buildmap.mjs'; + * console.log(buildMap()) + * </script> + * ``` + * + * The templates determine the appearance of the keys and the value of the map. Either a single value `id` can be taken or a composite key `${id} ${name}` can be used. + * + * If you want to access values of the parent data set, you have to use the `^` character `${id} ${^.name}`. + * + * @example + * + * import {buildMap} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/buildmap.mjs'; + * // a typical data structure as reported by an api + * + * let map; + * let obj = { + * "data": [ + * { + * "id": 10, + * "name": "Cassandra", + * "address": { + * "street": "493-4105 Vulputate Street", + * "city": "Saumur", + * "zip": "52628" + * } + * }, + * { + * "id": 20, + * "name": "Holly", + * "address": { + * "street": "1762 Eget Rd.", + * "city": "Schwalbach", + * "zip": "952340" + * } + * }, + * { + * "id": 30, + * "name": "Guy", + * "address": { + * "street": "957-388 Sollicitudin Avenue", + * "city": "Panchià", + * "zip": "420729" + * } + * } + * ] + * }; + * + * // The function is passed this data structure and with the help of the selector `'data.*'` the data to be considered are selected. + * // The key is given by a simple definition `'id'` and the value is given by a template `'${name} (${address.zip} ${address.city})'`. + * map = buildMap(obj, 'data.*', '${name} (${address.zip} ${address.city})', 'id'); + * console.log(map); + * + * // ↦ Map(3) { + * // '10' => 'Cassandra (52628 Saumur)', + * // '20' => 'Holly (952340 Schwalbach)', + * // '30' => 'Guy (420729 Panchià)' + * // } + * + * // If no key is specified, the key from the selection, here the array index, is taken. + * map = buildMap(obj, 'data.*', '${name} (${address.zip} ${address.city})'); + * console.log(map); + * + * // ↦ Map(3) { + * // '0' => 'Cassandra (52628 Saumur)', + * // '1' => 'Holly (952340 Schwalbach)', + * // '2' => 'Guy (420729 Panchià)' + * // } + * + * // a filter (function(value, key) {}) can be specified to accept only defined entries. + * map = buildMap(obj, 'data.*', '${name} (${address.zip} ${address.city})', 'id', function (value, key) { + * return (value['id'] >= 20) ? true : false + * }); + * console.log(map); + * + * // ↦ Map(2) { + * // 20 => 'Holly (952340 Schwalbach)', + * // 30 => 'Guy (420729 Panchià)' + * // } + * + * @param {*} subject + * @param {string|Monster.Data~exampleSelectorCallback} selector + * @param {string} [valueTemplate] + * @param {string} [keyTemplate] + * @param {Monster.Data~exampleFilterCallback} [filter] + * @return {*} + * @memberOf Monster.Data + * @throws {TypeError} value is neither a string nor a function + * @throws {TypeError} the selector callback must return a map + */ +function buildMap(subject, selector, valueTemplate, keyTemplate, filter) { + return assembleParts(subject, selector, filter, function (v, k, m) { + k = build(v, keyTemplate, k); + v = build(v, valueTemplate); + this.set(k, v); + }); + +} + + +/** + * @private + * @param {*} subject + * @param {string|Monster.Data~exampleSelectorCallback} selector + * @param {Monster.Data~exampleFilterCallback} [filter] + * @param {function} callback + * @return {Map} + * @throws {TypeError} selector is neither a string nor a function + */ +function assembleParts(subject, selector, filter, callback) { + + const result = new Map(); + + let map; + if (isFunction(selector)) { + map = selector(subject) + if (!(map instanceof Map)) { + throw new TypeError('the selector callback must return a map'); + } + } else if (isString(selector)) { + map = new Map; + buildFlatMap.call(map, subject, selector); + } else { + throw new TypeError('selector is neither a string nor a function') + } + + if (!(map instanceof Map)) { + return result; + } + + map.forEach((v, k, m) => { + if (isFunction(filter)) { + if (filter.call(m, v, k) !== true) return; + } + + callback.call(result, v, k, m); + + }); + + return result; +} + +/** + * @private + * @param subject + * @param selector + * @param key + * @param parentMap + * @return {*} + */ +function buildFlatMap(subject, selector, key, parentMap) { + + const result = this; + const currentMap = new Map; + + const resultLength = result.size; + + if (key === undefined) key = []; + + let parts = selector.split(DELIMITER); + let current = "", currentPath = []; + do { + + current = parts.shift(); + currentPath.push(current); + + if (current === WILDCARD) { + + let finder = new Pathfinder(subject); + let map; + + try { + map = finder.getVia(currentPath.join(DELIMITER)); + } catch (e) { + let a = e; + map = new Map(); + } + + for (const [k, o] of map) { + + let copyKey = clone(key); + + currentPath.map((a) => { + copyKey.push((a === WILDCARD) ? k : a) + }) + + let kk = copyKey.join(DELIMITER); + let sub = buildFlatMap.call(result, o, parts.join(DELIMITER), copyKey, o); + + if (isObject(sub) && parentMap !== undefined) { + sub[PARENT] = parentMap; + } + + currentMap.set(kk, sub); + } + + } + + + } while (parts.length > 0); + + // no set in child run + if (resultLength === result.size) { + for (const [k, o] of currentMap) { + result.set(k, o); + } + } + + return subject; + +} + + +/** + * With the help of this filter callback, values can be filtered out. Only if the filter function returns true, the value is taken for the map. + * + * @callback Monster.Data~exampleFilterCallback + * @param {*} value Value + * @param {string} key Key + * @memberOf Monster.Data + * @see {@link Monster.Data.buildMap} + */ + +/** + * Alternatively to a string selector a callback can be specified. this must return a map. + * + * @example + * import {buildMap} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/buildmap.mjs'; + * + * let obj = { + * "data": [ + * { + * "id": 10, + * "name": "Cassandra", + * "enrichment": { + * variants: [ + * { + * sku: 1, label: "XXS", price: [ + * {vk: '12.12 €'}, + * {vk: '12.12 €'} + * ] + * }, + * { + * sku: 2, label: "XS", price: [ + * {vk: '22.12 €'}, + * {vk: '22.12 €'} + * ] + * }, + * { + * sku: 3, label: "S", price: [ + * {vk: '32.12 €'}, + * {vk: '32.12 €'} + * ] + * }, + * { + * sku: 4, label: "L", price: [ + * {vk: '42.12 €'}, + * {vk: '42.12 €'} + * ] + * } + * ] + * + * } + * }, + * { + * "id": 20, + * "name": "Yessey!", + * "enrichment": { + * variants: [ + * { + * sku: 1, label: "XXS", price: [ + * {vk: '12.12 €'}, + * {vk: '12.12 €'} + * ] + * }, + * { + * sku: 2, label: "XS", price: [ + * {vk: '22.12 €'}, + * {vk: '22.12 €'} + * ] + * }, + * { + * sku: 3, label: "S", price: [ + * {vk: '32.12 €'}, + * {vk: '32.12 €'} + * ] + * }, + * { + * sku: 4, label: "L", price: [ + * {vk: '42.12 €'}, + * {vk: '42.12 €'} + * ] + * } + * ] + * + * } + * } + * ] + * }; + * + * let callback = function (subject) { + * let m = new Map; + * + * for (const [i, b] of Object.entries(subject.data)) { + * + * let key1 = i; + * + * for (const [j, c] of Object.entries(b.enrichment.variants)) { + * let key2 = j; + * + * for (const [k, d] of Object.entries(c.price)) { + * + * let key3 = k; + * + * d.name = b.name; + * d.label = c.label; + * d.id = [key1, key2, key3].join('.'); + * + * m.set(d.id, d); + * } + * + * } + * } + * return m; + * } + * + * let map = buildMap(obj, callback, '${name} ${vk}', '${id}') + * + * // ↦ Map(3) { + * // "0.0.0":"Cassandra 12.12 €", + * // "0.0.1":"Cassandra 12.12 €", + * // "0.1.0":"Cassandra 22.12 €", + * // "0.1.1":"Cassandra 22.12 €", + * // "0.2.0":"Cassandra 32.12 €", + * // "0.2.1":"Cassandra 32.12 €", + * // "0.3.0":"Cassandra 42.12 €", + * // "0.3.1":"Cassandra 42.12 €", + * // "1.0.0":"Yessey! 12.12 €", + * // "1.0.1":"Yessey! 12.12 €", + * // "1.1.0":"Yessey! 22.12 €", + * // "1.1.1":"Yessey! 22.12 €", + * // "1.2.0":"Yessey! 32.12 €", + * // "1.2.1":"Yessey! 32.12 €", + * // "1.3.0":"Yessey! 42.12 €", + * // "1.3.1":"Yessey! 42.12 €" + * // } + * + * @callback Monster.Data~exampleSelectorCallback + * @param {*} subject subject + * @return Map + * @since 1.17.0 + * @memberOf Monster.Data + * @see {@link Monster.Data.buildMap} + */ + +/** + * @private + * @param {*} subject + * @param {string|undefined} definition + * @param {*} defaultValue + * @return {*} + */ +function build(subject, definition, defaultValue) { + if (definition === undefined) return defaultValue ? defaultValue : subject; + validateString(definition); + + const regexp = /(?<placeholder>\${(?<path>[a-z\^A-Z.\-_0-9]*)})/gm + const array = [...definition.matchAll(regexp)]; + + let finder = new Pathfinder(subject); + + if (array.length === 0) { + return finder.getVia(definition); + } + + array.forEach((a) => { + let groups = a?.['groups']; + let placeholder = groups?.['placeholder'] + if (placeholder === undefined) return; + + let path = groups?.['path'] + + let v = finder.getVia(path); + if (v === undefined) v = defaultValue; + + definition = definition.replaceAll(placeholder, v); + + + }) + + return definition; + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../constants.mjs"; + +import {Base} from "../types/base.mjs"; +import {parseDataURL} from "../types/dataurl.mjs"; +import {isString} from "../types/is.mjs"; +import {ProxyObserver} from "../types/proxyobserver.mjs"; +import {validateObject} from "../types/validate.mjs"; +import {extend} from "./extend.mjs"; +import {Pathfinder} from "./pathfinder.mjs"; + +export {Datasource} + +/** + * @private + * @type {symbol} + * @memberOf Monster.Data + * @since 1.24.0 + */ +const internalDataSymbol = Symbol('internalData'); + +/** + * The datasource class is the basis for dealing with different data sources. + * It provides a unified interface for accessing data + * + * ``` + * <script type="module"> + * import {Datasource} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/datasource.mjs'; + * new Datasource() + * </script> + * ``` + * + * @example + * + * import {Datasource} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/datasource.mjs' + * + * class MyDatasource extends Datasource { + * + * } + * + * const ds = new MyDatasource(); + * + * @since 1.22.0 + * @copyright schukai GmbH + * @memberOf Monster.Data + * @summary The datasource class encapsulates the access to data objects. + */ +class Datasource extends Base { + + /** + * + */ + constructor() { + super(); + this[internalSymbol] = new ProxyObserver({ + 'options': extend({}, this.defaults) + }); + + this[internalDataSymbol] = new ProxyObserver({ + + }); + + + } + + /** + * attach a new observer + * + * @param {Observer} observer + * @returns {Datasource} + */ + attachObserver(observer) { + this[internalDataSymbol].attachObserver(observer) + return this; + } + + /** + * detach a observer + * + * @param {Observer} observer + * @returns {Datasource} + */ + detachObserver(observer) { + this[internalDataSymbol].detachObserver(observer) + return this; + } + + /** + * @param {Observer} observer + * @returns {boolean} + */ + containsObserver(observer) { + return this[internalDataSymbol].containsObserver(observer); + } + + /** + * Derived classes can override and extend this method as follows. + * + * ``` + * get defaults() { + * return Object.assign({}, super.defaults, { + * myValue:true + * }); + * } + * ``` + */ + get defaults() { + return {}; + } + + /** + * Set option + * + * @param {string} path + * @param {*} value + * @return {Datasource} + */ + setOption(path, value) { + new Pathfinder(this[internalSymbol].getSubject()['options']).setVia(path, value); + return this; + } + + /** + * @param {string|object} options + * @return {Datasource} + * @throws {Error} the options does not contain a valid json definition + */ + setOptions(options) { + + if (isString(options)) { + options = parseOptionsJSON(options) + } + + const self = this; + extend(self[internalSymbol].getSubject()['options'], self.defaults, options); + + return self; + } + + /** + * nested options can be specified by path `a.b.c` + * + * @param {string} path + * @param {*} defaultValue + * @return {*} + */ + getOption(path, defaultValue) { + let value; + + try { + value = new Pathfinder(this[internalSymbol].getRealSubject()['options']).getVia(path); + } catch (e) { + + } + + if (value === undefined) return defaultValue; + return value; + } + + /** + * @throws {Error} this method must be implemented by derived classes. + * @return {Promise} + */ + read() { + throw new Error("this method must be implemented by derived classes") + } + + /** + * @throws {Error} this method must be implemented by derived classes. + * @return {Promise} + */ + write() { + throw new Error("this method must be implemented by derived classes") + } + + + /** + * Returns real object + * + * @return {Object|Array} + */ + get() { + const self = this; + return self[internalDataSymbol].getRealSubject(); + } + + /** + * @param {Object|Array} data + * @return {Datasource} + */ + set(data) { + const self = this; + self[internalDataSymbol].setSubject(data); + return self; + } + +} + +/** + * @private + * @param data + * @return {Object} + * @throws {Error} the options does not contain a valid json definition + */ +function parseOptionsJSON(data) { + if (isString(data)) { + + // the configuration can be specified as a data url. + try { + let dataUrl = parseDataURL(data); + data = dataUrl.content; + } catch (e) { + + } + + + try { + let obj = JSON.parse(data); + validateObject(obj); + return obj; + } catch (e) { + throw new Error('the options does not contain a valid json definition (actual: ' + data + ').'); + } + } + + return {}; +} +'use strict'; + +/** + * In this namespace you will find classes and methods for handling data. + * + * @namespace Monster.Data + * @memberOf Monster + * @author schukai GmbH + */ +const ns = {};'use strict'; + +/** + * @author schukai GmbH + */ + + +import {isArray, isObject} from "../types/is.mjs"; +import {Node} from "../types/node.mjs"; +import {NodeList} from "../types/nodelist.mjs"; +import {assembleParts} from "./buildmap.mjs"; +import {extend} from "./extend.mjs"; + +export {buildTree} + +/** + * @private + * @type {symbol} + */ +const parentSymbol = Symbol('parent'); + +/** + * @private + * @type {symbol} + */ +const rootSymbol = Symbol('root'); + +/** + * @typedef {Object} buildTreeOptions + * @property {array} options.rootReferences=[null, undefined] defines the values for elements without parents + * @property {Monster.Data~exampleFilterCallback} options.filter filtering of the values + * @memberOf Monster.Data + */ + +/** + * With the help of the function `buildTree()`, nodes can be easily created from data objects. + * + * ``` + * <script type="module"> + * import {buildTree} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/buildtree.mjs'; + * buildTree() + * </script> + * ``` + * + * @param {*} subject + * @param {string|Monster.Data~exampleSelectorCallback} selector + * @param {string} idKey + * @param {string} parentIDKey + * @param {buildTreeOptions} [options] + * @return {*} + * @memberOf Monster.Data + * @throws {TypeError} value is neither a string nor a function + * @throws {TypeError} the selector callback must return a map + * @throws {Error} the object has no value for the specified id + * @since 1.26.0 + */ +function buildTree(subject, selector, idKey, parentIDKey, options) { + + const nodes = new Map; + + if (!isObject(options)) { + options = {} + } + + options = extend({}, { + rootReferences: [null, undefined], + filter: undefined + }, options) + + const filter = options?.filter; + let rootReferences = options.rootReferences; + if (!isArray(rootReferences)) { + rootReferences = [rootReferences]; + } + + const childMap = assembleParts(subject, selector, filter, function (o, k, m) { + + const key = o?.[idKey] + let ref = o?.[parentIDKey] + if (rootReferences.indexOf(ref) !== -1) ref = rootSymbol; + + if (key === undefined) { + throw new Error('the object has no value for the specified id') + } + + o[parentSymbol] = ref; + + const node = new Node(o); + this.has(ref) ? this.get(ref).add(node) : this.set(ref, new NodeList().add(node)); + nodes.set(key, node); + + }) + + nodes.forEach(node => { + + let id = node?.['value']?.[idKey]; + + if (childMap.has(id)) { + node.childNodes = childMap.get(id); + childMap.delete(id) + } + }) + + const list = new NodeList; + + childMap.forEach((s) => { + if (s instanceof Set) { + s.forEach((n) => { + list.add(n); + }) + } + }) + + return list; +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from '../types/base.mjs'; +import {getGlobal, getGlobalObject} from "../types/global.mjs"; +import {ID} from '../types/id.mjs'; +import {isArray, isObject, isString} from '../types/is.mjs'; +import { + validateFunction, + validateInteger, + validateObject, + validatePrimitive, + validateString +} from '../types/validate.mjs'; +import {clone} from "../util/clone.mjs"; +import {Pathfinder} from "./pathfinder.mjs"; + +export {Transformer} + +/** + * The transformer class is a swiss army knife for manipulating values. especially in combination with the pipe, processing chains can be built up. + * + * ``` + * <script type="module"> + * import {Transformer} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/transformer.mjs'; + * new Transformer() + * </script> + * ``` + * + * A simple example is the conversion of all characters to lowercase. for this purpose the command `tolower` must be used. + * + * ``` + * let t = new Transformer('tolower').run('ABC'); // ↦ abc + * ``` + * + * **all commands** + * + * in the following table all commands, parameters and existing aliases are described. + * + * | command | parameter | alias | description | + * |:-------------|:---------------------------|:------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + * | to-base64 | | base64, btob | Converts the value to base64 | + * | from-base64 | | atob | Converts the value from base64 | + * | call | function:param1:param2:... | | Calling a callback function. The function can be defined in three places: either globally, in the context `addCallback` or in the passed object | + * | default | value:type | ?? | If the value is undefined the first argument is returned, otherwise the value. The third optional parameter specifies the desired type. If no type is specified, string is used. Valid types are bool, string, int, float, undefined and object. An object default value must be specified as a base64 encoded json string. (since 1.12.0) | + * | debug | | | the passed value is output (console) and returned | + * | empty | | | Return empty String "" | + * | first-key | default | | Can be applied to objects and returns the value of the first key. All keys of the object are fetched and sorted. (since 1.23.0) | + * | fromjson | | | Type conversion from a JSON string (since 1.12.0) | + * | if | statement1:statement2 | ? | Is the ternary operator, the first parameter is the valid statement, the second is the false part. To use the current value in the queue, you can set the value keyword. On the other hand, if you want to have the static string "value", you have to put one backslash \\ in front of it and write value. the follow values are true: 'on', true, 'true'. If you want to have a space, you also have to write \\ in front of the space. | + * | index | key:default | property, key | Fetches a value from an object, an array, a map or a set | + * | last-key | default | | Can be applied to objects and returns the value of the last key. All keys of the object are fetched and sorted. (since 1.23.0) | + * | length | | count | Length of the string or entries of an array or object | + * | nop | | | Do nothing | + * | nth-key | index:default | | Can be applied to objects and returns the value of the nth key. All keys of the object are fetched and sorted. (since 1.23.0) | + * | nth-last-key | index:default | | Can be applied to objects and returns the value of the nth key from behind. All keys of the object are fetched and sorted. (since 1.23.0) | + * | path | path | | The access to an object is done via a Pathfinder object | + * | path-exists | path | | Check if the specified path is available in the value (since 1.24.0) | + * | plaintext | | plain | All HTML tags are removed (*) | + * | prefix | text | | Adds a prefix | + * | rawurlencode | | | URL coding | + * | static | | none | The Arguments value is used and passed to the value. Special characters \ <space> and : can be quotet by a preceding \. | + * | substring | start:length | | Returns a substring | + * | suffix | text | | Adds a suffix | + * | tointeger | | | Type conversion to an integer value | + * | tojson | | | Type conversion to a JSON string (since 1.8.0) | + * | tolower | | strtolower, tolowercase | The input value is converted to lowercase letters | + * | tostring | | | Type conversion to a string. | + * | toupper | | strtoupper, touppercase | The input value is converted to uppercase letters | + * | trim | | | Remove spaces at the beginning and end | + * | ucfirst | | | First character large | + * | ucwords | | | Any word beginning large | + * | undefined | | | Return undefined | + * | uniqid | | | Creates a string with a unique value (**) + * + * (*) for this functionality the extension [jsdom](https://www.npmjs.com/package/jsdom) must be loaded in the nodejs context. + * + * ``` + * // polyfill + * if (typeof window !== "object") { + * const {window} = new JSDOM('', { + * url: 'http://example.com/', + * pretendToBeVisual: true + * }); + * + * [ + * 'self', + * 'document', + * 'Node', + * 'Element', + * 'HTMLElement', + * 'DocumentFragment', + * 'DOMParser', + * 'XMLSerializer', + * 'NodeFilter', + * 'InputEvent', + * 'CustomEvent' + * ].forEach(key => (global[key] = window[key])); + * } + * ``` + * + * (**) for this command the crypt library is necessary in the nodejs context. + * + * ``` + * import * as Crypto from "@peculiar/webcrypto"; + * global['crypto'] = new Crypto.Crypto(); + * ``` + * + * @example + * + * import {Transformer} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/transformer.mjs'; + * + * const transformer = new Transformer("tolower") + * + * console.log(transformer.run("HELLO")) + * // ↦ hello + * + * console.log(transformer.run("WORLD")) + * // ↦ world + * + * @since 1.5.0 + * @copyright schukai GmbH + * @memberOf Monster.Data + */ +class Transformer extends Base { + /** + * + * @param {string} definition + */ + constructor(definition) { + super(); + this.args = disassemble(definition); + this.command = this.args.shift() + this.callbacks = new Map(); + + } + + /** + * + * @param {string} name + * @param {function} callback + * @param {object} context + * @returns {Transformer} + * @throws {TypeError} value is not a string + * @throws {TypeError} value is not a function + */ + setCallback(name, callback, context) { + validateString(name) + validateFunction(callback) + + if (context !== undefined) { + validateObject(context); + } + + this.callbacks.set(name, { + callback: callback, + context: context, + }); + + return this; + } + + /** + * + * @param {*} value + * @returns {*} + * @throws {Error} unknown command + * @throws {TypeError} unsupported type + * @throws {Error} type not supported + */ + run(value) { + return transform.apply(this, [value]) + } +} + +/** + * + * @param {string} command + * @returns {array} + * @private + */ +function disassemble(command) { + + validateString(command); + + let placeholder = new Map; + const regex = /((?<pattern>\\(?<char>.)){1})/mig; + + // The separator for args must be escaped + // undefined string which should not occur normally and is also not a regex + let result = command.matchAll(regex) + + for (let m of result) { + let g = m?.['groups']; + if (!isObject(g)) { + continue; + } + + let p = g?.['pattern']; + let c = g?.['char']; + + if (p && c) { + let r = '__' + new ID().toString() + '__'; + placeholder.set(r, c); + command = command.replace(p, r); + } + + } + let parts = command.split(':'); + + parts = parts.map(function (value) { + let v = value.trim(); + for (let k of placeholder) { + v = v.replace(k[0], k[1]); + } + return v; + + + }); + + return parts +} + +/** + * tries to make a string out of value and if this succeeds to return it back + * + * @param {*} value + * @returns {string} + * @private + */ +function convertToString(value) { + + if (isObject(value) && value.hasOwnProperty('toString')) { + value = value.toString(); + } + + validateString(value) + return value; +} + +/** + * + * @param {*} value + * @returns {*} + * @private + * @throws {Error} unknown command + * @throws {TypeError} unsupported type + * @throws {Error} type not supported + * @throws {Error} missing key parameter + */ +function transform(value) { + + const console = getGlobalObject('console'); + + let args = clone(this.args); + let key, defaultValue; + + switch (this.command) { + + case 'static': + return this.args.join(':'); + + case 'tolower': + case 'strtolower': + case 'tolowercase': + validateString(value) + return value.toLowerCase(); + + case 'toupper': + case 'strtoupper': + case 'touppercase': + validateString(value) + return value.toUpperCase(); + + case 'tostring': + return "" + value; + + case 'tointeger': + let n = parseInt(value); + validateInteger(n); + return n + + case 'tojson': + return JSON.stringify(value); + + case 'fromjson': + return JSON.parse(value); + + case 'trim': + validateString(value) + return value.trim(); + + case 'rawurlencode': + validateString(value) + return encodeURIComponent(value) + .replace(/!/g, '%21') + .replace(/'/g, '%27') + .replace(/\(/g, '%28') + .replace(/\)/g, '%29') + .replace(/\*/g, '%2A'); + + + case 'call': + + /** + * callback-definition + * function callback(value, ...args) { + * return value; + * } + */ + + let callback; + let callbackName = args.shift(); + let context = getGlobal(); + + if (isObject(value) && value.hasOwnProperty(callbackName)) { + callback = value[callbackName]; + } else if (this.callbacks.has(callbackName)) { + let s = this.callbacks.get(callbackName); + callback = s?.['callback']; + context = s?.['context']; + } else if (typeof window === 'object' && window.hasOwnProperty(callbackName)) { + callback = window[callbackName]; + } + validateFunction(callback); + + args.unshift(value); + return callback.call(context, ...args); + + case 'plain': + case 'plaintext': + validateString(value); + let doc = new DOMParser().parseFromString(value, 'text/html'); + return doc.body.textContent || ""; + + case 'if': + case '?': + + validatePrimitive(value); + + let trueStatement = (args.shift() || undefined); + let falseStatement = (args.shift() || undefined); + + if (trueStatement === 'value') { + trueStatement = value; + } + if (trueStatement === '\\value') { + trueStatement = 'value'; + } + if (falseStatement === 'value') { + falseStatement = value; + } + if (falseStatement === '\\value') { + falseStatement = 'value'; + } + + let condition = ((value !== undefined && value !== '' && value !== 'off' && value !== 'false' && value !== false) || value === 'on' || value === 'true' || value === true); + return condition ? trueStatement : falseStatement; + + + case 'ucfirst': + validateString(value); + + let firstchar = value.charAt(0).toUpperCase(); + return firstchar + value.substr(1); + case 'ucwords': + validateString(value); + + return value.replace(/^([a-z\u00E0-\u00FC])|\s+([a-z\u00E0-\u00FC])/g, function (v) { + return v.toUpperCase(); + }); + + case 'count': + case 'length': + + if ((isString(value) || isObject(value) || isArray(value)) && value.hasOwnProperty('length')) { + return value.length; + } + + throw new TypeError("unsupported type " + typeof value); + + case 'to-base64': + case 'btoa': + case 'base64': + return btoa(convertToString(value)); + + case 'atob': + case 'from-base64': + return atob(convertToString(value)); + + case 'empty': + return ''; + + case 'undefined': + return undefined; + + case 'debug': + + if (isObject(console)) { + console.log(value); + } + + return value; + + case 'prefix': + validateString(value); + let prefix = args?.[0]; + return prefix + value; + + case 'suffix': + validateString(value); + let suffix = args?.[0]; + return value + suffix; + + case 'uniqid': + return (new ID()).toString(); + + case 'first-key': + case 'last-key': + case 'nth-last-key': + case 'nth-key': + + if (!isObject(value)) { + throw new Error("type not supported") + } + + const keys = Object.keys(value).sort() + + if (this.command === 'first-key') { + key = 0; + } else if (this.command === 'last-key') { + key = keys.length - 1; + } else { + + key = validateInteger(parseInt(args.shift())); + + if (this.command === 'nth-last-key') { + key = keys.length - key - 1; + } + } + + defaultValue = (args.shift() || ''); + + let useKey = keys?.[key]; + + if (value?.[useKey]) { + return value?.[useKey]; + } + + return defaultValue; + + + case 'key': + case 'property': + case 'index': + + key = args.shift() || undefined; + + if (key === undefined) { + throw new Error("missing key parameter") + } + + defaultValue = (args.shift() || undefined); + + if (value instanceof Map) { + if (!value.has(key)) { + return defaultValue; + } + return value.get(key); + } + + if (isObject(value) || isArray(value)) { + + if (value?.[key]) { + return value?.[key]; + } + + return defaultValue; + } + + throw new Error("type not supported") + + case 'path-exists': + + key = args.shift(); + if (key === undefined) { + throw new Error("missing key parameter") + } + + return new Pathfinder(value).exists(key); + + case 'path': + + key = args.shift(); + if (key === undefined) { + throw new Error("missing key parameter") + } + + let pf = new Pathfinder(value); + + if (!pf.exists(key)) { + return undefined; + } + + return pf.getVia(key); + + + case 'substring': + + validateString(value); + + let start = parseInt(args[0]) || 0; + let end = (parseInt(args[1]) || 0) + start; + + return value.substring(start, end); + + case 'nop': + return value; + + case '??': + case 'default': + if (value !== undefined && value !== null) { + return value; + } + + defaultValue = args.shift(); + let defaultType = args.shift(); + if (defaultType === undefined) { + defaultType = 'string'; + } + + switch (defaultType) { + case 'int': + case 'integer': + return parseInt(defaultValue); + case 'float': + return parseFloat(defaultValue); + case 'undefined': + return undefined + case 'bool': + case 'boolean': + defaultValue = defaultValue.toLowerCase() + return ((defaultValue !== 'undefined' && defaultValue !== '' && defaultValue !== 'off' && defaultValue !== 'false' && defaultValue !== 'false') || defaultValue === 'on' || defaultValue === 'true' || defaultValue === 'true'); + case 'string': + return "" + defaultValue; + case "object": + return JSON.parse(atob(defaultValue)); + } + + throw new Error("type not supported") + + + default: + throw new Error("unknown command " + this.command) + } + + return value; +} + +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from '../types/base.mjs'; +import {isArray, isInteger, isObject, isPrimitive} from '../types/is.mjs'; +import {Stack} from "../types/stack.mjs"; +import {validateInteger, validateString} from '../types/validate.mjs'; + +export {Pathfinder, DELIMITER, WILDCARD} + +/** + * path separator + * + * @private + * @type {string} + */ +const DELIMITER = '.'; + +/** + * @private + * @type {string} + */ +const WILDCARD = '*'; + +/** + * Pathfinder is a class to find a path to an object. + * + * ``` + * <script type="module"> + * import {Pathfinder} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/pathfinder.mjs'; + * console.log(new Pathfinder()) + * </script> + * ``` + * + * With the help of the pathfinder, values can be read and written from an object construct. + * + * ``` + * new Pathfinder({ + * a: { + * b: { + * f: [ + * { + * g: false, + * } + * ], + * } + * } + * }).getVia("a.b.f.0.g"); // ↦ false + * ``` + * + * if a value is not present or has the wrong type, a corresponding exception is thrown. + * + * ``` + * new Pathfinder({}).getVia("a.b.f.0.g"); // ↦ Error + * ``` + * + * The `Pathfinder.exists()` method can be used to check whether access to the path is possible. + * + * ``` + * new Pathfinder({}).exists("a.b.f.0.g"); // ↦ false + * ``` + * + * pathfinder can also be used to build object structures. to do this, the `Pathfinder.setVia()` method must be used. + * + * ``` + * obj = {}; + * new Pathfinder(obj).setVia('a.b.0.c', true); // ↦ {a:{b:[{c:true}]}} + * ``` + * + * @example + * + * import {Pathfinder} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/pathfinder.mjs'; + * + * let value = new Pathfinder({ + * a: { + * b: { + * f: [ + * { + * g: false, + * } + * ], + * } + * } + * }).getVia("a.b.f.0.g"); + * + * console.log(value); + * // ↦ false + * + * try { + * new Pathfinder({}).getVia("a.b.f.0.g"); + * } catch(e) { + * console.log(e.toString()); + * // ↦ Error: the journey is not at its end (b.f.0.g) + * } + * + * @example + * + * import {Pathfinder} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/pathfinder.mjs'; + * + * let p = new Pathfinder({ + * a: { + * x: [ + * {c: 1}, {c: 2} + * ], + * y: true + * }, + * b: { + * x: [ + * {c: 1, d: false}, {c: 2} + * ], + * y: true + * }, + * }); + * + * let r = p.getVia("*.x.*.c"); + * console.log(r); + * + * @since 1.4.0 + * @copyright schukai GmbH + * @memberOf Monster.Data + */ +class Pathfinder extends Base { + + /** + * @param {array|object|Map|Set} value + * @since 1.4.0 + * @throws {Error} the parameter must not be a simple type + **/ + constructor(object) { + super(); + + if (isPrimitive(object)) { + throw new Error('the parameter must not be a simple type'); + } + + this.object = object; + this.wildCard = WILDCARD; + } + + /** + * set wildcard + * + * @param {string} wildcard + * @return {Pathfinder} + * @since 1.7.0 + */ + setWildCard(wildcard) { + validateString(wildcard); + this.wildCard = wildcard; + return this; + } + + /** + * + * @param {string} path + * @since 1.4.0 + * @returns {*} + * @throws {TypeError} unsupported type + * @throws {Error} the journey is not at its end + * @throws {TypeError} value is not a string + * @throws {TypeError} value is not an integer + * @throws {Error} unsupported action for this data type + */ + getVia(path) { + return getValueViaPath.call(this, this.object, validateString(path)); + } + + /** + * + * @param {string} path + * @param {*} value + * @returns {Pathfinder} + * @since 1.4.0 + * @throws {TypeError} unsupported type + * @throws {TypeError} value is not a string + * @throws {TypeError} value is not an integer + * @throws {Error} unsupported action for this data type + */ + setVia(path, value) { + validateString(path); + setValueViaPath.call(this, this.object, path, value); + return this; + } + + /** + * Delete Via Path + * + * @param {string} path + * @returns {Pathfinder} + * @since 1.6.0 + * @throws {TypeError} unsupported type + * @throws {TypeError} value is not a string + * @throws {TypeError} value is not an integer + * @throws {Error} unsupported action for this data type + */ + deleteVia(path) { + validateString(path); + deleteValueViaPath.call(this, this.object, path); + return this; + } + + /** + * + * @param {string} path + * @return {bool} + * @throws {TypeError} unsupported type + * @throws {TypeError} value is not a string + * @throws {TypeError} value is not an integer + * @since 1.4.0 + */ + exists(path) { + validateString(path); + try { + getValueViaPath.call(this, this.object, path, true); + return true; + } catch (e) { + + } + + return false; + } + +} + + +/** + * + * @param {*} subject + * @param {string} path + * @param {string} check + * @return {Map} + * @throws {TypeError} unsupported type + * @throws {Error} the journey is not at its end + * @throws {Error} unsupported action for this data type + * @private + */ +function iterate(subject, path, check) { + + const result = new Map; + + if (isObject(subject) || isArray(subject)) { + for (const [key, value] of Object.entries(subject)) { + result.set(key, getValueViaPath.call(this, value, path, check)) + } + } else { + let key = path.split(DELIMITER).shift(); + result.set(key, getValueViaPath.call(this, subject, path, check)); + } + + return result; + + +} + +/** + * + * @param {*} subject + * @param [string} path + * @param [boolean} check + * @returns {*} + * @throws {TypeError} unsupported type + * @throws {Error} the journey is not at its end + * @throws {Error} unsupported action for this data type + * @private + */ +function getValueViaPath(subject, path, check) { + + if (path === "") { + return subject; + } + + let parts = path.split(DELIMITER) + let current = parts.shift(); + + if (current === this.wildCard) { + return iterate.call(this, subject, parts.join(DELIMITER), check); + } + + if (isObject(subject) || isArray(subject)) { + + let anchor; + if (subject instanceof Map || subject instanceof WeakMap) { + anchor = subject.get(current); + + } else if (subject instanceof Set || subject instanceof WeakSet) { + current = parseInt(current); + validateInteger(current) + anchor = [...subject]?.[current]; + + } else if (typeof WeakRef === 'function' && subject instanceof WeakRef) { + throw Error('unsupported action for this data type'); + + } else if (isArray(subject)) { + current = parseInt(current); + validateInteger(current) + anchor = subject?.[current]; + } else { + anchor = subject?.[current]; + } + + if (isObject(anchor) || isArray(anchor)) { + return getValueViaPath.call(this, anchor, parts.join(DELIMITER), check) + } + + if (parts.length > 0) { + throw Error("the journey is not at its end (" + parts.join(DELIMITER) + ")"); + } + + + if (check === true) { + const descriptor = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(subject), current); + + if (!subject.hasOwnProperty(current) && descriptor === undefined) { + throw Error('unknown value'); + } + + } + + return anchor; + + } + + throw TypeError("unsupported type " + typeof subject) + +} + +/** + * + * @param object + * @param path + * @param value + * @returns {void} + * @throws {TypeError} unsupported type + * @throws {TypeError} unsupported type + * @throws {Error} the journey is not at its end + * @throws {Error} unsupported action for this data type + * @private + */ +function setValueViaPath(object, path, value) { + + validateString(path); + + let parts = path.split(DELIMITER) + let last = parts.pop(); + let subpath = parts.join(DELIMITER); + + let stack = new Stack() + let current = subpath; + while (true) { + + try { + getValueViaPath.call(this, object, current, true) + break; + } catch (e) { + + } + + stack.push(current); + parts.pop(); + current = parts.join(DELIMITER); + + if (current === "") break; + } + + while (!stack.isEmpty()) { + current = stack.pop(); + let obj = {}; + + if (!stack.isEmpty()) { + let n = stack.peek().split(DELIMITER).pop(); + if (isInteger(parseInt(n))) { + obj = []; + } + + } + + setValueViaPath.call(this, object, current, obj); + } + + let anchor = getValueViaPath.call(this, object, subpath); + + if (!isObject(object) && !isArray(object)) { + throw TypeError("unsupported type: " + typeof object); + } + + if (anchor instanceof Map || anchor instanceof WeakMap) { + anchor.set(last, value); + } else if (anchor instanceof Set || anchor instanceof WeakSet) { + anchor.append(value) + + } else if (typeof WeakRef === 'function' && anchor instanceof WeakRef) { + throw Error('unsupported action for this data type'); + + } else if (isArray(anchor)) { + last = parseInt(last); + validateInteger(last) + assignProperty(anchor, last, value); + } else { + assignProperty(anchor, last, value); + } + + +} + +/** + * @private + * @param {object} object + * @param {string} key + * @param {*} value + */ +function assignProperty(object, key, value) { + + if (!object.hasOwnProperty(key)) { + object[key] = value; + return; + } + + if (value === undefined) { + delete object[key]; + } + + object[key] = value; + +} + +/** + * + * @param object + * @param path + * @returns {void} + * @throws {TypeError} unsupported type + * @throws {TypeError} unsupported type + * @throws {Error} the journey is not at its end + * @throws {Error} unsupported action for this data type + * @since 1.6.0 + * @private + */ +function deleteValueViaPath(object, path) { + + const parts = path.split(DELIMITER) + let last = parts.pop(); + const subpath = parts.join(DELIMITER); + + const anchor = getValueViaPath.call(this, object, subpath); + + if (anchor instanceof Map) { + anchor.delete(last); + } else if (anchor instanceof Set || anchor instanceof WeakMap || anchor instanceof WeakSet || (typeof WeakRef === 'function' && anchor instanceof WeakRef)) { + throw Error('unsupported action for this data type'); + + } else if (isArray(anchor)) { + last = parseInt(last); + validateInteger(last) + delete anchor[last]; + } else { + delete anchor[last]; + } + + +} +'use strict'; + +/** + * @author schukai GmbH + */ + + +import {isArray, isObject} from "../types/is.mjs"; +import {typeOf} from "../types/typeof.mjs"; + +export {diff} + +/** + * With the diff function you can perform the change of one object to another. The result shows the changes of the second object to the first object. + * + * The operator `add` means that something has been added to the second object. `delete` means that something has been deleted from the second object compared to the first object. + * + * ``` + * <script type="module"> + * import {Diff} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/diff.mjs'; + * Diff(a, b) + * </script> + * ``` + * + * @example + * + * import {Diff} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/diff.mjs'; + * + * // given are two objects x and y. + * + * let x = { + * a: 1, + * b: "Hello!" + * } + * + * let y = { + * a: 2, + * c: true + * } + * + * // These two objects can be compared with each other. + * + * console.log(Diff(x, y)); + * + * // the result is then the following + * + * // + * // [ + * // { + * // operator: 'update', + * // path: [ 'a' ], + * // first: { value: 1, type: 'number' }, + * // second: { value: 2, type: 'number' } + * // }, + * // { + * // operator: 'delete', + * // path: [ 'b' ], + * // first: { value: 'Hello!', type: 'string' } + * // }, + * // { + * // operator: 'add', + * // path: [ 'c' ], + * // second: { value: true, type: 'boolean' } + * // } + * // ] + * + * @param {*} first + * @param {*} second + * @return {array} + * @since 1.6.0 + * @copyright schukai GmbH + * @memberOf Monster.Data + */ +function diff(first, second) { + return doDiff(first, second) +} + +/** + * @private + * @param a + * @param b + * @param type + * @return {Set<string>|Set<number>} + */ +function getKeys(a, b, type) { + if (isArray(type)) { + const keys = a.length > b.length ? new Array(a.length) : new Array(b.length); + keys.fill(0); + return new Set(keys.map((_, i) => i)); + } + + return new Set(Object.keys(a).concat(Object.keys(b))); +} + +/** + * @private + * @param a + * @param b + * @param path + * @param diff + * @return {array} + */ +function doDiff(a, b, path, diff) { + + let typeA = typeOf(a) + let typeB = typeOf(b) + + const currPath = path || []; + const currDiff = diff || []; + + if (typeA === typeB && (typeA === 'object' || typeA ==='array')) { + + getKeys(a, b, typeA).forEach((v) => { + + if (!(Object.prototype.hasOwnProperty.call(a, v))) { + currDiff.push(buildResult(a[v], b[v], 'add', currPath.concat(v))); + } else if (!(Object.prototype.hasOwnProperty.call(b, v))) { + currDiff.push(buildResult(a[v], b[v], 'delete', currPath.concat(v))); + } else { + doDiff(a[v], b[v], currPath.concat(v), currDiff); + } + }); + + } else { + + const o = getOperator(a, b, typeA, typeB); + if (o !== undefined) { + currDiff.push(buildResult(a, b, o, path)); + } + + } + + return currDiff; + +} + +/** + * + * @param {*} a + * @param {*} b + * @param {string} operator + * @param {array} path + * @return {{path: array, operator: string}} + * @private + */ +function buildResult(a, b, operator, path) { + + const result = { + operator, + path, + }; + + if (operator !== 'add') { + result.first = { + value: a, + type: typeof a + }; + + if (isObject(a)) { + const name = Object.getPrototypeOf(a)?.constructor?.name; + if (name !== undefined) { + result.first.instance = name; + } + } + } + + if (operator === 'add' || operator === 'update') { + result.second = { + value: b, + type: typeof b + }; + + if (isObject(b)) { + const name = Object.getPrototypeOf(b)?.constructor?.name; + if (name !== undefined) { + result.second.instance = name; + } + } + + } + + return result; +} + +/** + * @private + * @param {*} a + * @param {*} b + * @return {boolean} + */ +function isNotEqual(a, b) { + + if (typeof a !== typeof b) { + return true; + } + + if (a instanceof Date && b instanceof Date) { + return a.getTime() !== b.getTime(); + } + + return a !== b; +} + +/** + * @private + * @param {*} a + * @param {*} b + * @return {string|undefined} + */ +function getOperator(a, b) { + + /** + * @type {string|undefined} + */ + let operator; + + /** + * @type {string} + */ + let typeA = typeof a; + + /** + * @type {string} + */ + let typeB = typeof b; + + if (typeA === 'undefined' && typeB !== 'undefined') { + operator = 'add'; + } else if (typeA !== 'undefined' && typeB === 'undefined') { + operator = 'delete'; + } else if (isNotEqual(a, b)) { + operator = 'update'; + } + + return operator; + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../../constants.mjs"; +import {isObject} from "../../types/is.mjs"; +import {Datasource} from "../datasource.mjs"; +import {Pathfinder} from "../pathfinder.mjs"; +import {Pipe} from "../pipe.mjs"; +import {WriteError} from "./restapi/writeerror.mjs"; + +export {RestAPI} + +/** + * The RestAPI is a class that enables a REST API server. + * + * ``` + * <script type="module"> + * import {RestAPI} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/datasource/restapi.mjs'; + * new RestAPI() + * </script> + * ``` + * + * @example + * + * import {RestAPI} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/datasource/restapi.mjs'; + * + * const ds = new RestAPI({ + * url: 'https://httpbin.org/get' + * },{ + * url: 'https://httpbin.org/post' + * }); + * + * ds.set({flag:true}) + * ds.write().then(()=>console.log('done')); + * ds.read().then(()=>console.log('done')); + * + * @since 1.22.0 + * @copyright schukai GmbH + * @memberOf Monster.Data.Datasource + * @summary The LocalStorage class encapsulates the access to data objects. + */ +class RestAPI extends Datasource { + + /** + * + * @param {Object} [readDefinition] An options object containing any custom settings that you want to apply to the read request. + * @param {Object} [writeDefinition] An options object containing any custom settings that you want to apply to the write request. + * @throws {TypeError} value is not a string + */ + constructor(readDefinition, writeDefinition) { + super(); + + const options = {} + + if (isObject(readDefinition)) options.read = readDefinition; + if (isObject(writeDefinition)) options.write = writeDefinition; + + this.setOptions(options); + + } + + /** + * @property {string} url=undefined Defines the resource that you wish to fetch. + * @property {Object} write={} Options + * @property {Object} write.init={} An options object containing any custom settings that you want to apply to the request. The parameters are identical to those of the {@link https://developer.mozilla.org/en-US/docs/Web/API/Request/Request|Request constructor} + * @property {string} write.init.method=POST + * @property {string} write.acceptedStatus=[200,201] + * @property {string} write.url URL + * @property {Object} write.mapping the mapping is applied before writing. + * @property {String} write.mapping.transformer Transformer to select the appropriate entries + * @property {Object} write.report + * @property {String} write.report.path Path to validations + * @property {Monster.Data.Datasource~exampleCallback[]} write.mapping.callback with the help of the callback, the structures can be adjusted before writing. + * @property {Object} read.init={} An options object containing any custom settings that you want to apply to the request. The parameters are identical to those of the {@link https://developer.mozilla.org/en-US/docs/Web/API/Request/Request|Request constructor} + * @property {string} read.init.method=GET + * @property {string} read.acceptedStatus=[200] + * @property {string} read.url URL + * @property {Object} read.mapping the mapping is applied after reading. + * @property {String} read.mapping.transformer Transformer to select the appropriate entries + * @property {Monster.Data.Datasource~exampleCallback[]} read.mapping.callback with the help of the callback, the structures can be adjusted after reading. + */ + get defaults() { + return Object.assign({}, super.defaults, { + write: { + init: { + method: 'POST', + }, + acceptedStatus: [200, 201], + url: undefined, + mapping: { + transformer: undefined, + callbacks: [] + }, + report: { + path: undefined + } + }, + read: { + init: { + method: 'GET' + }, + acceptedStatus: [200], + url: undefined, + mapping: { + transformer: undefined, + callbacks: [] + }, + }, + + }); + } + + /** + * @return {Promise} + * @throws {Error} the options does not contain a valid json definition + * @throws {TypeError} value is not a object + * @throws {Error} the data cannot be read + */ + read() { + const self = this; + let response; + + let init = self.getOption('read.init'); + if (!isObject(init)) init = {}; + + return fetch(self.getOption('read.url'), init).then(resp => { + response = resp; + + const acceptedStatus = self.getOption('read.acceptedStatus', [200]); + + if (acceptedStatus.indexOf(resp.status) === -1) { + throw Error('the data cannot be read (response ' + resp.status + ')') + } + + return resp.text() + }).then(body => { + + let obj; + + try { + obj = JSON.parse(body); + + } catch (e) { + + if (body.length > 100) { + body = body.substring(0, 97) + '...'; + } + + throw new Error('the response does not contain a valid json (actual: ' + body + ').'); + } + + let transformation = self.getOption('read.mapping.transformer'); + if (transformation !== undefined) { + const pipe = new Pipe(transformation); + obj = pipe.run(obj); + } + + self.set(obj); + return response; + }) + } + + /** + * @return {Promise} + * @throws {WriteError} the data cannot be written + */ + write() { + const self = this; + + + let init = self.getOption('write.init'); + if (!isObject(init)) init = {}; + if (typeof init['headers'] !== 'object') { + init['headers'] = { + 'Content-Type': 'application/json' + } + } + + let obj = self.get(); + let transformation = self.getOption('write.mapping.transformer'); + if (transformation !== undefined) { + const pipe = new Pipe(transformation); + obj = pipe.run(obj); + } + + let sheathingObject = self.getOption('write.sheathing.object'); + let sheathingPath = self.getOption('write.sheathing.path'); + let reportPath = self.getOption('write.report.path'); + + if (sheathingObject && sheathingPath) { + const sub = obj; + obj = sheathingObject; + (new Pathfinder(obj)).setVia(sheathingPath, sub); + } + + init['body'] = JSON.stringify(obj); + + return fetch(self.getOption('write.url'), init).then(response => { + + const acceptedStatus = self.getOption('write.acceptedStatus', [200, 2001]); + + if (acceptedStatus.indexOf(response.status) === -1) { + + return response.text().then((body) => { + + let obj, validation; + try { + obj = JSON.parse(body); + validation = new Pathfinder(obj).getVia(reportPath) + + } catch (e) { + + if (body.length > 100) { + body = body.substring(0, 97) + '...'; + } + + throw new Error('the response does not contain a valid json (actual: ' + body + ').'); + } + + throw new WriteError('the data cannot be written (response ' + response.status + ')', response, validation) + + }) + + + } + + return response; + }); + } + + + /** + * @return {RestAPI} + */ + getClone() { + const self = this; + return new RestAPI(self[internalSymbol].getRealSubject()['options'].read, self[internalSymbol].getRealSubject()['options'].write); + } + +} + + +/** + * This callback can be passed to a datasource and is used to adapt data structures. + * + * @callback Monster.Data.Datasource~exampleCallback + * @param {*} value Value + * @param {string} key Key + * @memberOf Monster.Data + * @see Monster.Data.Datasource + */ +'use strict'; + +/** + * Namespace for datasources + * + * @namespace Monster.Data.Datasource + * @memberOf Monster.Data + * @author schukai GmbH + */ +const ns = {};'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../../../constants.mjs"; +import {getGlobalObject} from "../../../types/global.mjs"; +import {Datasource} from "../../datasource.mjs"; +import {Storage, storageObjectSymbol} from "../storage.mjs"; + +export {SessionStorage} + +/** + * The SessionStorage class provides a data source that uses the SessionStorage API on the client. + * + * ``` + * <script type="module"> + * import {SessionStorage} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/datasource/storage/sessionstorage.mjs'; + * new SessionStorage() + * </script> + * ``` + * + * @since 1.22.0 + * @copyright schukai GmbH + * @memberOf Monster.Data.Datasource.Storage + * @summary The LocalStorage class encapsulates the access to data objects. + */ +class SessionStorage extends Storage { + + /** + * @throws {Error} this method must be implemented by derived classes. + * @return {external:sessionStorage} + * @private + */ + [storageObjectSymbol]() { + return getGlobalObject('sessionStorage'); + } + + /** + * Create Clone + * + * @return {SessionStorage} + */ + getClone() { + const self = this; + return new SessionStorage(self[internalSymbol].getRealSubject()['options'].key); + } + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../../../constants.mjs"; +import {getGlobalObject} from "../../../types/global.mjs"; +import {Datasource} from "../../datasource.mjs"; +import {Storage, storageObjectSymbol} from "../storage.mjs"; + +export {LocalStorage} + +/** + * The LocalStorage Datasource provides a data store in the browser localStorage. + * + * ``` + * <script type="module"> + * import {LocalStorage} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/datasource/storage/localstorage.mjs'; + * new LocalStorage() + * </script> + * ``` + * + * @since 1.22.0 + * @copyright schukai GmbH + * @memberOf Monster.Data.Datasource.Storage + * @summary The LocalStorage class encapsulates the access to data objects. + */ +class LocalStorage extends Storage { + + /** + * @throws {Error} this method must be implemented by derived classes. + * @return {external:localStorage} + * @private + */ + [storageObjectSymbol]() { + return getGlobalObject('localStorage'); + } + + /** + * Create clone + * @return {LocalStorage} + */ + getClone() { + const self = this; + return new LocalStorage(self[internalSymbol].getRealSubject()['options'].key); + } + + +} +'use strict'; + +/** + * Namespace for storages + * + * @namespace Monster.Data.Datasource.Storage + * @memberOf Monster.Data.Datasource + * @author schukai GmbH + */ +const ns = {}; +'use strict'; + +/** + * Namespace for storages + * + * @namespace Monster.Data.Datasource.RestAPI + * @memberOf Monster.Data.Datasource + * @author schukai GmbH + */ +const ns = {}; +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../../../constants.mjs"; + +export {WriteError} + +/** + * Error message for API requests with extension of request and validation. + * + * @since 1.24.0 + * @copyright schukai GmbH + * @memberOf Monster.Data.Datasource.RestAPI + * @summary the error is thrown by the rest api in case of error + */ +class WriteError extends Error { + /** + * + * @param {string} message + * @param {Response} response + */ + constructor(message, response, validation) { + super(message); + this[internalSymbol] = { + response: response, + validation: validation + }; + } + + /** + * @return {Response} + */ + getResponse() { + return this[internalSymbol]['response'] + } + + /** + * @return {Object} + */ + getValidation() { + return this[internalSymbol]['validation'] + } +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../../constants.mjs"; +import {validateString} from "../../types/validate.mjs"; +import {Datasource} from "../datasource.mjs"; + +export {Storage, storageObjectSymbol} + +/** + * @private + * @type {symbol} + */ +const storageObjectSymbol = Symbol('storageObject'); + +/** + * The class represents a record. + * + * ``` + * <script type="module"> + * import {Storage} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/datasource/storage.mjs'; + * new Storage() + * </script> + * ``` + * + * @example + * + * import {Storage} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/datasource/storage.mjs'; + * + * new Datasource(); + * + * @since 1.22.0 + * @copyright schukai GmbH + * @memberOf Monster.Data.Datasource + * @summary The Storage class encapsulates the access to data objects over WebStorageAPI. + */ +class Storage extends Datasource { + + /** + * + * @param {string} key LocalStorage Key + * @throws {TypeError} value is not a string + */ + constructor(key) { + super(); + this.setOption('key', validateString(key)); + } + + /** + * @property {string} key=undefined LocalStorage Key + */ + get defaults() { + return Object.assign({}, super.defaults, { + key: undefined, + }); + } + + /** + * @throws {Error} this method must be implemented by derived classes. + * @return {external:Storage} + * @private + */ + [storageObjectSymbol]() { + throw new Error("this method must be implemented by derived classes") + } + + /** + * @return {Promise} + * @throws {Error} the options does not contain a valid json definition + * @throws {TypeError} value is not a object + * @throws {Error} the data cannot be read + */ + read() { + const self = this; + + const storage = self[storageObjectSymbol](); + + return new Promise(function (resolve) { + const data = JSON.parse(storage.getItem(self.getOption('key'))); + self.set(data??{}); + resolve(); + }) + + } + + /** + * @return {Storage} + * @throws {Error} the data cannot be written + */ + write() { + const self = this; + + const storage = self[storageObjectSymbol](); + + return new Promise(function (resolve) { + + const data = self.get(); + if (data === undefined) { + storage.removeItem(self.getOption('key')); + } else { + storage.setItem(self.getOption('key'), JSON.stringify(data)); + } + + resolve(); + }) + } + + /** + * @return {Storage} + */ + getClone() { + const self=this; + return new Storage(self[internalSymbol].getRealSubject()['options'].key); + } + +} +'use strict'; + +/** + * @author schukai GmbH + */ + + +import {getGlobal} from '../types/global.mjs'; + +export {random} + +/** + * this function uses crypt and returns a random number. + * + * ``` + * <script type="module"> + * import {random} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/math/random.mjs'; + * random(1,10) + * // ↦ 5 + * </script> + * ``` + * + * @param {number} min starting value of the definition set (default is 0) + * @param {number} max end value of the definition set (default is 1000000000) + * @returns {number} + * @memberOf Monster.Math + * @throws {Error} missing crypt + * @throws {Error} we cannot generate numbers larger than 53 bits. + * @throws {Error} the distance is too small to create a random number. + + * @since 1.0.0 + * @copyright schukai GmbH + */ + function random(min, max) { + + if (min === undefined) { + min = 0; + } + if (max === undefined) { + max = MAX; + } + + if (max < min) { + throw new Error("max must be greater than min"); + } + + return Math.round(create(min, max)); + +} + +/** + * @private + * @type {number} + */ +var MAX = 1000000000; + + +Math.log2 = Math.log2 || function (n) { + return Math.log(n) / Math.log(2); +}; + +/** + * + * @param {number} min + * @param {number} max + * @returns {number} + * @private + * @throws {Error} missing crypt + * @throws {Error} we cannot generate numbers larger than 53 bits. + * @throws {Error} the distance is too small to create a random number. + */ +function create(min, max) { + let crypt; + let globalReference = getGlobal(); + + crypt = globalReference?.['crypto'] || globalReference?.['msCrypto'] || globalReference?.['crypto'] || undefined; + + if (typeof crypt === "undefined") { + throw new Error("missing crypt") + } + + let rval = 0; + const range = max - min; + if (range < 2) { + throw new Error('the distance is too small to create a random number.') + } + + const bitsNeeded = Math.ceil(Math.log2(range)); + if (bitsNeeded > 53) { + throw new Error("we cannot generate numbers larger than 53 bits."); + } + const bytesNeeded = Math.ceil(bitsNeeded / 8); + const mask = Math.pow(2, bitsNeeded) - 1; + + const byteArray = new Uint8Array(bytesNeeded); + crypt.getRandomValues(byteArray); + + let p = (bytesNeeded - 1) * 8; + for (var i = 0; i < bytesNeeded; i++) { + rval += byteArray[i] * Math.pow(2, p); + p -= 8; + } + + rval = rval & mask; + + if (rval >= range) { + return create(min, max); + } + + if (rval < min) { + rval += min; + } + + return rval; + +} +'use strict'; + +/** + * Namespace for math. + * + * @namespace Monster.Math + * @memberOf Monster + * @author schukai GmbH + */ +const ns = {};'use strict'; + +/** + * @author schukai GmbH + */ + +import {isObject} from "../types/is.mjs"; +import {AbstractConstraint} from "./abstract.mjs"; + +export {IsObject} + +/** + * Constraints are used to define conditions that must be met by the value of a variable. + * + * The uniform API of the constraints allows chains to be formed. + * + * ``` + * <script type="module"> + * import {IsObject} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/isobject.mjs'; + * console.log(new IsObject()) + * </script> + * ``` + * + * @example + * + * import {IsObject} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/isobject.mjs'; + * + * new IsObject() + * .isValid({}) + * .then(()=>console.log(true)); + * // ↦ true + * + * + * new IsObject() + * .isValid(99) + * .catch(e=>console.log(e)); + * // ↦ 99 + * + * @since 1.3.0 + * @copyright schukai GmbH + * @memberOf Monster.Constraints + * @summary A constraint to check if a value is an object + */ +class IsObject extends AbstractConstraint { + + /** + * this method return a promise containing the result of the check. + * + * @param {*} value + * @returns {Promise} + */ + isValid(value) { + if (isObject(value)) { + return Promise.resolve(value); + } + + return Promise.reject(value); + } + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {AbstractConstraint} from "./abstract.mjs"; + +export {Invalid} + +/** + * Constraints are used to define conditions that must be met by the value of a variable. + * + * The uniform API of the constraints allows chains to be formed. + * + * The invalid constraint allows an always invalid query to be performed. this constraint is mainly intended for testing. + * + * ``` + * <script type="module"> + * import {Invalid} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/invalid.mjs'; + * new Invalid(); + * </script> + * ``` + * + * @example + * + * import {Invalid} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/invalid.mjs'; + * + * new Invalid().isValid() + * .then(()=>console.log(true)) + * .catch(()=>console.log(false)); + * // ↦ false + * + * @since 1.3.0 + * @copyright schukai GmbH + * @memberOf Monster.Constraints + * @summary A constraint that always invalid + */ +class Invalid extends AbstractConstraint { + + /** + * this method return a rejected promise + * + * @param {*} value + * @returns {Promise} + */ + isValid(value) { + return Promise.reject(value); + } + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {AbstractOperator} from "./abstractoperator.mjs"; + +export {AndOperator} + +/** + * Constraints are used to define conditions that must be met by the value of a variable. + * + * The uniform API of the constraints allows chains to be formed. + * + * The AndOperator is used to link several contraints. The constraint is fulfilled if all constraints of the operators are fulfilled. + * + * ``` + * <script type="module"> + * import {AndOperator} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/andoperator.mjs'; + * new AndOperator(); + * </script> + * ``` + * + * @example + * + * import {Valid} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/valid.mjs'; + * import {Invalid} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/invalid.mjs'; + * import {AndOperator} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/andoperator.mjs'; + * + * new AndOperator( + * new Valid(), new Valid()).isValid() + * .then(()=>console.log(true)) + * .catch(()=>console.log(false)); + * // ↦ true + * + * new AndOperator( + * new Invalid(), new Valid()).isValid() + * .then(()=>console.log(true)) + * .catch(()=>console.log(false)); + * // ↦ false + * + * @since 1.3.0 + * @copyright schukai GmbH + * @memberOf Monster.Constraints + * @summary A and operator constraint + */ +class AndOperator extends AbstractOperator { + + /** + * this method return a promise containing the result of the check. + * + * @param {*} value + * @returns {Promise} + */ + isValid(value) { + return Promise.all([this.operantA.isValid(value), this.operantB.isValid(value)]); + } + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {AbstractOperator} from "./abstractoperator.mjs"; + +export {OrOperator} + +/** + * Constraints are used to define conditions that must be met by the value of a variable. + * + * The uniform API of the constraints allows chains to be formed. + * + * The OrOperator is used to link several constraints. The constraint is fulfilled if one of the constraints is fulfilled. + * + * ``` + * <script type="module"> + * import {OrOperator} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraint/oroperator.mjs'; + * new OrOperator(); + * </script> + * ``` + * + * @example + * + * import {Valid} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/valid.mjs'; + * import {Invalid} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/invalid.mjs'; + * import {OrOperator} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/oroperator.mjs'; + * + * new OrOperator( + * new Valid(), new Invalid()).isValid() + * .then(()=>console.log(true)) + * .catch(()=>console.log(false)); + * // ↦ true + * + * new OrOperator( + * new Invalid(), new Invalid()).isValid() + * .then(()=>console.log(true)) + * .catch(()=>console.log(false)); + * // ↦ false + * + * @since 1.3.0 + * @copyright schukai GmbH + * @memberOf Monster.Constraints + * @summary A or operator + */ +class OrOperator extends AbstractOperator { + + /** + * this method return a promise containing the result of the check. + * + * @param {*} value + * @returns {Promise} + */ + isValid(value) { + var self = this; + + return new Promise(function (resolve, reject) { + let a, b; + + self.operantA.isValid(value) + .then(function () { + resolve(); + }).catch(function () { + a = false; + /** b has already been evaluated and was not true */ + if (b === false) { + reject(); + } + }); + + self.operantB.isValid(value) + .then(function () { + resolve(); + }).catch(function () { + b = false; + /** b has already been evaluated and was not true */ + if (a === false) { + reject(); + } + }); + }); + } + + +} +'use strict'; + +/** + * @author schukai GmbH + */ + + +import {Base} from '../types/base.mjs'; + +export {AbstractConstraint} + +/** + * Constraints are used to define conditions that must be met by the value of a variable. + * + * The uniform API of the constraints allows chains to be formed. + * + * The abstract constraint defines the api for all constraints. mainly the method isValid() is defined. + * + * derived classes must implement the method isValid(). + * + * @since 1.3.0 + * @copyright schukai GmbH + * @memberOf Monster.Constraints + * @summary The abstract constraint + */ +class AbstractConstraint extends Base { + + /** + * + */ + constructor() { + super(); + } + + /** + * this method must return a promise containing the result of the check. + * + * @param {*} value + * @returns {Promise} + */ + isValid(value) { + return Promise.reject(value); + } +} +'use strict'; + +/** + * Constraints are used to define conditions that must be met by the value of a variable so that the value can be transferred to the system. + * + * @namespace Monster.Constraints + * @memberOf Monster + * @author schukai GmbH + */ +const ns = {};'use strict'; + +/** + * @author schukai GmbH + */ + +import {isArray} from "../types/is.mjs"; +import {AbstractConstraint} from "./abstract.mjs"; + +export {IsArray} + +/** + * Constraints are used to define conditions that must be met by the value of a variable. + * + * The uniform API of the constraints allows chains to be formed. + * + * ``` + * <script type="module"> + * import {IsArray} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/isarray.mjs'; + * console.log(new IsArray()) + * </script> + * ``` + * + * @example + * + * import {IsArray} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/isarray.mjs'; + * + * new IsArray() + * .isValid([]) + * .then(()=>console.log(true)); + * // ↦ true + * + * new IsArray() + * .isValid(99) + * .catch(e=>console.log(e)); + * // ↦ 99 + * + * @since 1.3.0 + * @copyright schukai GmbH + * @memberOf Monster.Constraints + * @summary A constraint to check if a value is an array + */ +class IsArray extends AbstractConstraint { + + /** + * this method return a promise containing the result of the check. + * + * @param {*} value + * @returns {Promise} + */ + isValid(value) { + if (isArray(value)) { + return Promise.resolve(value); + } + + return Promise.reject(value); + } + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {AbstractConstraint} from "./abstract.mjs"; + +export {AbstractOperator} + +/** + * Constraints are used to define conditions that must be met by the value of a variable. + * + * The uniform API of the constraints allows chains to be formed. + * + * Operators allow you to link constraints together. for example, you can check whether a value is an object or an array. each operator has two operands that are linked together. + * + * @since 1.3.0 + * @copyright schukai GmbH + * @memberOf Monster.Constraints + * @summary The abstract operator constraint + */ +class AbstractOperator extends AbstractConstraint { + + /** + * + * @param {AbstractConstraint} operantA + * @param {AbstractConstraint} operantB + * @throws {TypeError} "parameters must be from type AbstractConstraint" + */ + constructor(operantA, operantB) { + super(); + + if (!(operantA instanceof AbstractConstraint) || !(operantB instanceof AbstractConstraint)) { + throw new TypeError("parameters must be from type AbstractConstraint") + } + + this.operantA = operantA; + this.operantB = operantB; + + } + + +} + +'use strict'; + +/** + * @author schukai GmbH + */ + +import {AbstractConstraint} from "./abstract.mjs"; + +export {Valid} + +/** + * Constraints are used to define conditions that must be met by the value of a variable. + * + * The uniform API of the constraints allows chains to be formed. + * + * The valid constraint allows an always valid query to be performed. this constraint is mainly intended for testing. + * + * ``` + * <script type="module"> + * import {Valid} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/valid.mjs'; + * new Valid(); + * </script> + * ``` + * + * @example + * + * import {Valid} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/constraints/valid.mjs'; + * + * new Valid().isValid() + * .then(()=>console.log(true)) + * .catch(()=>console.log(false)); + * // ↦ true + * + * @since 1.3.0 + * @copyright schukai GmbH + * @memberOf Monster.Constraints + * @summary A constraint that always valid + */ +class Valid extends AbstractConstraint { + + /** + * this method return a promise containing the result of the check. + * + * @param {*} value + * @returns {Promise} + */ + isValid(value) { + return Promise.resolve(value); + } + +} +/** + * @license + * Copyright 2021 schukai GmbH + * SPDX-License-Identifier: AGPL-3.0-only or COMMERCIAL + * @author schukai GmbH + */ + + +/** + * Main namespace for Monster. + * + * @namespace Monster + * @author schukai GmbH + */ +'use strict'; + +import './constants.mjs'; +// find packages/monster/source/ -type f -name "*.mjs" -not -name "*namespace*" -not -iname "monster.mjs" +import './constraints/isobject.mjs'; +import './constraints/valid.mjs'; +import './constraints/invalid.mjs'; +import './constraints/abstractoperator.mjs'; +import './constraints/oroperator.mjs'; +import './constraints/andoperator.mjs'; +import './constraints/abstract.mjs'; +import './constraints/isarray.mjs'; +import './logging/logger.mjs'; +import './logging/handler.mjs'; +import './logging/logentry.mjs'; +import './logging/handler/console.mjs'; +import './text/formatter.mjs'; +import './dom/resource/script.mjs'; +import './dom/resource/data.mjs'; +import './dom/resource/link/stylesheet.mjs'; +import './dom/resource/link.mjs'; +import './dom/resource.mjs'; +import './dom/updater.mjs'; +import './dom/attributes.mjs'; +import './dom/template.mjs'; +import './dom/util.mjs'; +import './dom/ready.mjs'; +import './dom/resourcemanager.mjs'; +import './dom/locale.mjs'; +import './dom/customcontrol.mjs'; +import './dom/constants.mjs'; +import './dom/assembler.mjs'; +import './dom/theme.mjs'; +import './dom/worker/factory.mjs'; +import './dom/focusmanager.mjs'; +import './dom/events.mjs'; +import './dom/customelement.mjs'; +import './i18n/formatter.mjs'; +import './i18n/providers/fetch.mjs'; +import './i18n/translations.mjs'; +import './i18n/locale.mjs'; +import './i18n/provider.mjs'; +import './types/queue.mjs'; +import './types/binary.mjs'; +import './types/regex.mjs'; +import './types/observer.mjs'; +import './types/observerlist.mjs'; +import './types/basewithoptions.mjs'; +import './types/is.mjs'; +import './types/proxyobserver.mjs'; +import './types/uniquequeue.mjs'; +import './types/node.mjs'; +import './types/tokenlist.mjs'; +import './types/typeof.mjs'; +import './types/uuid.mjs'; +import './types/mediatype.mjs'; +import './types/dataurl.mjs'; +import './types/base.mjs'; +import './types/version.mjs'; +import './types/nodelist.mjs'; +import './types/id.mjs'; +import './types/randomid.mjs'; +import './types/noderecursiveiterator.mjs'; +import './types/validate.mjs'; +import './types/stack.mjs'; +import './util/deadmansswitch.mjs'; +import './util/comparator.mjs'; +import './util/trimspaces.mjs'; +import './util/clone.mjs'; +import './util/freeze.mjs'; +import './util/processing.mjs'; +import './constants.mjs'; +import './data/pathfinder.mjs'; +import './data/pipe.mjs'; +import './data/extend.mjs'; +import './data/diff.mjs'; +import './data/buildmap.mjs'; +import './data/datasource.mjs'; +import './data/buildtree.mjs'; +import './data/transformer.mjs'; +import './data/datasource/storage.mjs'; +import './data/datasource/restapi.mjs'; +import './data/datasource/storage/sessionstorage.mjs'; +import './data/datasource/storage/localstorage.mjs'; +import './data/datasource/restapi/writeerror.mjs'; +import './math/random.mjs'; + +export {Monster} + +/** + * This class has no other purpose than to exist. + * + * @since 2.0.0 + * @copyright schukai GmbH + * @memberOf Monster + */ +class Monster { + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Handler} from '../logging/handler.mjs'; +import {LogEntry} from '../logging/logentry.mjs'; + +import {Base} from '../types/base.mjs'; +import {validateInteger, validateObject, validateString} from '../types/validate.mjs'; + +export {Logger, ALL, TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF}; + +/** + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF + * @type {number} + * @memberOf Monster.Logging + */ +const ALL = 255; +/** + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF + * @type {number} + * @memberOf Monster.Logging + */ +const TRACE = 64; +/** + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF + * @type {number} + * @memberOf Monster.Logging + */ +const DEBUG = 32; +/** + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF + * @type {number} + * @memberOf Monster.Logging + */ +const INFO = 16; +/** + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF + * @type {number} + * @memberOf Monster.Logging + */ +const WARN = 8; +/** + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF + * @type {number} + * @memberOf Monster.Logging + */ +const ERROR = 4; +/** + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF + * @type {number} + * @memberOf Monster.Logging + */ +const FATAL = 2; +/** + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF + * @type {number} + * @memberOf Monster.Logging + */ +const OFF = 0; + +/** + * The logger is a class that takes care of logging. + * + * ``` + * <script type="module"> + * import {ID} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/logging/logger.mjs'; + * new Logger() + * </script> + * ``` + * + * @since 1.5.0 + * @copyright schukai GmbH + * @memberOf Monster.Logging + */ +class Logger extends Base { + + /** + * + */ + constructor() { + super(); + this.handler = new Set; + } + + /** + * + * @param {Handler} handler + * @returns {Logger} + * @throws {Error} the handler must be an instance of Handler + */ + addHandler(handler) { + validateObject(handler) + if (!(handler instanceof Handler)) { + throw new Error("the handler must be an instance of Handler") + } + + this.handler.add(handler) + return this; + } + + /** + * + * @param {Handler} handler + * @returns {Logger} + * @throws {Error} the handler must be an instance of Handler + */ + removeHandler(handler) { + validateObject(handler) + if (!(handler instanceof Handler)) { + throw new Error("the handler must be an instance of Handler") + } + + this.handler.delete(handler); + return this; + } + + /** + * log Trace message + * + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF (ALL = 0xff;OFF = 0x00; + * + * @param {*} arguments + * @returns {Logger} + * @since 1.5.0 + */ + logTrace() { + triggerLog.apply(this, [TRACE, ...arguments]); + return this; + }; + + /** + * log Debug message + * + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF (ALL = 0xff;OFF = 0x00; + * + * @param {*} arguments + * @returns {Logger} + * @since 1.5.0 + */ + logDebug() { + triggerLog.apply(this, [DEBUG, ...arguments]); + return this; + }; + + /** + * log Info message + * + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF (ALL = 0xff;OFF = 0x00; + * + * + * @param {*} arguments + * @returns {Logger} + * @since 1.5.0 + */ + logInfo() { + triggerLog.apply(this, [INFO, ...arguments]); + return this; + }; + + /** + * log Warn message + * + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF (ALL = 0xff;OFF = 0x00; + * + * @param {*} arguments + * @returns {Logger} + * @since 1.5.0 + */ + logWarn() { + triggerLog.apply(this, [WARN, ...arguments]); + return this; + }; + + /** + * log Error message + * + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF (ALL = 0xff;OFF = 0x00; + * + * @param {*} arguments + * @returns {Logger} + * @since 1.5.0 + */ + logError() { + triggerLog.apply(this, [ERROR, ...arguments]); + return this; + }; + + /** + * log Fatal message + * + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF (ALL = 0xff;OFF = 0x00; + * + * @param {*} arguments + * @returns {Logger} + * @since 1.5.0 + */ + logFatal() { + triggerLog.apply(this, [FATAL, ...arguments]); + return this; + }; + + + /** + * Labels + * + * @param {integer} level + * @returns {string} + */ + getLabel(level) { + validateInteger(level); + + if (level === ALL) return 'ALL'; + if (level === TRACE) return 'TRACE'; + if (level === DEBUG) return 'DEBUG'; + if (level === INFO) return 'INFO'; + if (level === WARN) return 'WARN'; + if (level === ERROR) return 'ERROR'; + if (level === FATAL) return 'FATAL'; + if (level === OFF) return 'OFF'; + + return 'unknown'; + }; + + /** + * Level + * + * @param {string} label + * @returns {integer} + */ + getLevel(label) { + validateString(label); + + if (label === 'ALL') return ALL; + if (label === 'TRACE') return TRACE; + if (label === 'DEBUG') return DEBUG; + if (label === 'INFO') return INFO; + if (label === 'WARN') return WARN; + if (label === 'ERROR') return ERROR; + if (label === 'FATAL') return FATAL; + if (label === 'OFF') return OFF; + + return 0; + }; + + +} + + +/** + * Log triggern + * + * @param {integer} loglevel + * @param {*} args + * @returns {Logger} + * @private + */ +function triggerLog(loglevel, ...args) { + var logger = this; + + for (let handler of logger.handler) { + handler.log(new LogEntry(loglevel, args)) + } + + return logger; + +} +'use strict'; + +/** + * @author schukai GmbH + */ + + +import {Base} from '../types/base.mjs'; +import {validateInteger} from '../types/validate.mjs'; + +export {LogEntry} + +/** + * A log entry for the logger + * + * ``` + * <script type="module"> + * import {ID} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/logging/logentry.mjs'; + * console.log(new LogEntry()) + * </script> + * ``` + * + * @since 1.5.0 + * @copyright schukai GmbH + * @memberOf Monster.Logging + */ + class LogEntry extends Base { + /** + * + * @param {Integer} loglevel + * @param {...*} args + */ + constructor(loglevel, ...args) { + super(); + validateInteger(loglevel); + + this.loglevel = loglevel + this.arguments = args + } + + /** + * + * @returns {integerr} + */ + getLogLevel() { + return this.loglevel + } + + /** + * + * @returns {array} + */ + getArguments() { + return this.arguments + } + +} +'use strict'; + +/** + * Namespace for logging. + * + * @namespace Monster.Logging + * @memberOf Monster + * @author schukai GmbH + */ +const ns = {};'use strict'; + +/** + * @namespace Monster.Logging.Handler + * @memberOf Monster.Logging + * @author schukai GmbH + */ +const ns = {}; +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from '../../types/base.mjs'; +import {getGlobalObject} from "../../types/global.mjs"; +import {Handler} from '../handler.mjs'; +import {LogEntry} from "../logentry.mjs"; + +export {ConsoleHandler} + +/** + * You can create an object of the class simply by using the namespace `new Monster.Logging.Handler.ConsoleHandler()`. + * + * ``` + * <script type="module"> + * import {ConsoleHandler} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/logging/handler/console.mjs'; + * console.log(new ConsoleHandler()) + * </script> + * ``` + * + * @since 1.5.0 + * @copyright schukai GmbH + * @memberOf Monster.Logging.Handler + */ + class ConsoleHandler extends Handler { + constructor() { + super(); + } + + + /** + * This is the central log function. this method must be + * overwritten by derived handlers with their own logic. + * + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF (ALL = 0xff;OFF = 0x00; + * + * @param {LogEntry} entry + * @returns {boolean} + */ + log(entry) { + if (super.log(entry)) { + let console = getGlobalObject('console'); + if (!console) return false; + console.log(entry.toString()); + return true; + } + + return false; + } + +} + + +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from '../types/base.mjs'; +import {validateInstance, validateInteger} from "../types/validate.mjs"; +import {LogEntry} from "./logentry.mjs"; +import {ALL, DEBUG, ERROR, FATAL, INFO, OFF, TRACE, WARN} from "./logger.mjs"; + +export {Handler} + +/** + * The log handler is the interface between the log entries and the log listeners. + * + * ``` + * <script type="module"> + * import {ID} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/logging/handler.mjs'; + * console.log(new Handler()) + * </script> + * ``` + * + * @since 1.5.0 + * @copyright schukai GmbH + * @memberOf Monster.Logging + */ + class Handler extends Base { + constructor() { + super(); + + /** + * Loglevel + * + * @type {integer} + */ + this.loglevel = OFF; + } + + /** + * This is the central log function. this method must be + * overwritten by derived handlers with their own logic. + * + * ALL > TRACE > DEBUG > INFO > WARN > ERROR > FATAL > OFF (ALL = 0xff;OFF = 0x00; + * + * @param {LogEntry} entry + * @returns {boolean} + */ + log(entry) { + validateInstance(entry, LogEntry); + + if (this.loglevel < entry.getLogLevel()) { + return false; + } + + return true; + } + + /** + * set loglevel + * + * @param {integer} loglevel + * @returns {Handler} + * @since 1.5.0 + */ + setLogLevel(loglevel) { + validateInteger(loglevel) + this.loglevel = loglevel; + return this; + } + + /** + * get loglevel + * + * @returns {integer} + * @since 1.5.0 + */ + getLogLevel() { + return this.loglevel; + } + + /** + * Set log level to All + * + * @returns {Handler} + * @since 1.5.0 + */ + setAll() { + this.setLogLevel(ALL); + return this; + }; + + /** + * Set log level to Trace + * + * @returns {Handler} + * @since 1.5.0 + */ + setTrace() { + this.setLogLevel(TRACE); + return this; + }; + + /** + * Set log level to Debug + * + * @returns {Handler} + * @since 1.5.0 + */ + setDebug() { + this.setLogLevel(DEBUG); + return this; + }; + + /** + * Set log level to Info + * + * @returns {Handler} + * @since 1.5.0 + */ + setInfo() { + this.setLogLevel(INFO); + return this; + }; + + /** + * Set log level to Warn + * + * @returns {undefined} + * @since 1.5.0 + */ + setWarn() { + this.setLogLevel(WARN); + return this; + }; + + /** + * Set log level to Error + * + * @returns {Handler} + * @since 1.5.0 + */ + setError() { + this.setLogLevel(ERROR); + return this; + }; + + /** + * Set log level to Fatal + * + * @returns {Handler} + * @since 1.5.0 + */ + setFatal() { + this.setLogLevel(FATAL); + return this; + }; + + + /** + * Set log level to Off + * + * @returns {Handler} + * @since 1.5.0 + */ + setOff() { + this.setLogLevel(OFF); + return this; + }; + + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../constants.mjs"; +import {extend} from "../data/extend.mjs"; +import {Pipe} from "../data/pipe.mjs"; + +import {BaseWithOptions} from "../types/basewithoptions.mjs"; +import {isObject, isString} from "../types/is.mjs"; +import {validateArray, validateString} from "../types/validate.mjs"; +import {getMonsterVersion} from "../types/version.mjs"; + +export {Formatter} + +/** + * @private + * @type {symbol} + */ +const internalObjectSymbol = Symbol('internalObject'); + +/** + * @private + * @type {symbol} + */ +const watchdogSymbol = Symbol('watchdog'); + +/** + * @private + * @type {symbol} + */ +const markerOpenIndexSymbol = Symbol('markerOpenIndex'); + +/** + * @private + * @type {symbol} + */ +const markerCloseIndexSymbol = Symbol('markercloseIndex'); + +/** + * @private + * @type {symbol} + */ +const workingDataSymbol = Symbol('workingData'); + + +/** + * Messages can be formatted with the formatter. To do this, an object with the values must be passed to the formatter. The message can then contain placeholders. + * + * Look at the example below. The placeholders use the logic of Pipe. + * + * ``` + * <script type="module"> + * import {Formatter} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/text/formatter.mjs'; + * new Formatter() + * </script> + * ``` + * + * ## Marker in marker + * + * Markers can be nested. Here, the inner marker is resolved first `${subkey} ↦ 1 = ${mykey2}` and then the outer marker `${mykey2}`. + * + * ``` + * const text = '${mykey${subkey}}'; + * let obj = { + * mykey2: "1", + * subkey: "2" + * }; + * + * new Formatter(obj).format(text); + * // ↦ 1 + * ``` + * + * ## Callbacks + * + * The values in a formatter can be adjusted via the commands of the `Transformer` or the`Pipe`. + * There is also the possibility to use callbacks. + * + * const formatter = new Formatter({x: '1'}, { + * callbacks: { + * quote: (value) => { + * return '"' + value + '"' + * } + * } + * }); + * + * formatter.format('${x | call:quote}')) + * // ↦ "1" + * + * ## Marker with parameter + * + * A string can also bring its own values. These must then be separated from the key by a separator `::`. + * The values themselves must be specified in key/value pairs. The key must be separated from the value by a separator `=`. + * + * When using a pipe, you must pay attention to the separators. + * + * @example + * + * import {Formatter} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/text/formatter.mjs'; + * + * new Formatter({ + * a: { + * b: { + * c: "Hello" + * }, + * d: "world", + * } + * }).format("${a.b.c} ${a.d | ucfirst}!"); // with pipe + * + * // ↦ Hello World! + * + * @since 1.12.0 + * @copyright schukai GmbH + * @memberOf Monster.Text + */ + class Formatter extends BaseWithOptions { + + /** + * Default values for the markers are `${` and `}` + * + * @param {object} object + * @throws {TypeError} value is not a object + */ + constructor(object, options) { + super(options); + this[internalObjectSymbol] = object || {} + this[markerOpenIndexSymbol] = 0; + this[markerCloseIndexSymbol] = 0; + } + + /** + * @property {object} marker + * @property {array} marker.open=["${"] + * @property {array} marker.close=["${"] + * @property {object} parameter + * @property {string} parameter.delimiter="::" + * @property {string} parameter.assignment="=" + * @property {object} callbacks={} + */ + get defaults() { + return extend({}, super.defaults, { + marker: { + open: ['${'], + close: ['}'] + }, + parameter: { + delimiter: '::', + assignment: '=' + }, + callbacks: {}, + }) + } + + + /** + * Set new Parameter Character + * + * Default values for the chars are `::` and `=` + * + * ``` + * formatter.setParameterChars('#'); + * formatter.setParameterChars('[',']'); + * formatter.setParameterChars('i18n{','}'); + * ``` + * + * @param {string} delimiter + * @param {string} assignment + * @return {Formatter} + * @since 1.24.0 + * @throws {TypeError} value is not a string + */ + setParameterChars(delimiter, assignment) { + + if (delimiter !== undefined) { + this[internalSymbol]['parameter']['delimiter'] = validateString(delimiter); + } + + if (assignment !== undefined) { + this[internalSymbol]['parameter']['assignment'] = validateString(assignment); + } + + return this; + } + + /** + * Set new Marker + * + * Default values for the markers are `${` and `}` + * + * ``` + * formatter.setMarker('#'); // open and close are both # + * formatter.setMarker('[',']'); + * formatter.setMarker('i18n{','}'); + * ``` + * + * @param {array|string} open + * @param {array|string|undefined} close + * @return {Formatter} + * @since 1.12.0 + * @throws {TypeError} value is not a string + */ + setMarker(open, close) { + + if (close === undefined) { + close = open; + } + + if (isString(open)) open = [open]; + if (isString(close)) close = [close]; + + this[internalSymbol]['marker']['open'] = validateArray(open); + this[internalSymbol]['marker']['close'] = validateArray(close); + return this; + } + + /** + * + * @param {string} text + * @return {string} + * @throws {TypeError} value is not a string + * @throws {Error} too deep nesting + */ + format(text) { + this[watchdogSymbol] = 0; + this[markerOpenIndexSymbol] = 0; + this[markerCloseIndexSymbol] = 0; + this[workingDataSymbol] = {}; + return format.call(this, text); + } + +} + +/** + * @private + * @return {string} + */ +function format(text) { + const self = this; + + self[watchdogSymbol]++; + if (this[watchdogSymbol] > 20) { + throw new Error('too deep nesting') + } + + let openMarker = self[internalSymbol]['marker']['open']?.[this[markerOpenIndexSymbol]]; + let closeMarker = self[internalSymbol]['marker']['close']?.[this[markerCloseIndexSymbol]]; + + // contains no placeholders + if (text.indexOf(openMarker) === -1 || text.indexOf(closeMarker) === -1) { + return text; + } + + let result = tokenize.call(this, validateString(text), openMarker, closeMarker) + + if (self[internalSymbol]['marker']['open']?.[this[markerOpenIndexSymbol] + 1]) { + this[markerOpenIndexSymbol]++; + } + + if (self[internalSymbol]['marker']['close']?.[this[markerCloseIndexSymbol] + 1]) { + this[markerCloseIndexSymbol]++; + } + + result = format.call(self, result) + + return result; +} + +/** + * @private + * @since 1.12.0 + * @param text + * @return {string} + */ +function tokenize(text, openMarker, closeMarker) { + const self = this; + + let formatted = []; + + const parameterAssignment = self[internalSymbol]['parameter']['assignment'] + const parameterDelimiter = self[internalSymbol]['parameter']['delimiter'] + const callbacks = self[internalSymbol]['callbacks']; + + while (true) { + + let startIndex = text.indexOf(openMarker); + // no marker + if (startIndex === -1) { + formatted.push(text); + break; + } else if (startIndex > 0) { + formatted.push(text.substring(0, startIndex)) + text = text.substring(startIndex) + } + + let endIndex = text.substring(openMarker.length).indexOf(closeMarker); + if (endIndex !== -1) endIndex += openMarker.length; + let insideStartIndex = text.substring(openMarker.length).indexOf(openMarker); + if (insideStartIndex !== -1) { + insideStartIndex += openMarker.length; + if (insideStartIndex < endIndex) { + let result = tokenize.call(self, text.substring(insideStartIndex), openMarker, closeMarker); + text = text.substring(0, insideStartIndex) + result + endIndex = text.substring(openMarker.length).indexOf(closeMarker); + if (endIndex !== -1) endIndex += openMarker.length; + } + } + + if (endIndex === -1) { + throw new Error("syntax error in formatter template") + return; + } + + let key = text.substring(openMarker.length, endIndex); + let parts = key.split(parameterDelimiter); + let currentPipe = parts.shift(); + + self[workingDataSymbol] = extend({}, self[internalObjectSymbol], self[workingDataSymbol]); + + for (const kv of parts) { + const [k, v] = kv.split(parameterAssignment); + self[workingDataSymbol][k] = v; + } + + const t1 = key.split('|').shift().trim(); // pipe symbol + const t2 = t1.split('::').shift().trim(); // key value delimiter + const t3 = t2.split('.').shift().trim(); // path delimiter + let prefix = self[workingDataSymbol]?.[t3] ? 'path:' : 'static:'; + + let command = ""; + if (prefix && key.indexOf(prefix) !== 0 + && key.indexOf('path:') !== 0 + && key.indexOf('static:') !== 0) { + command = prefix; + } + + command += currentPipe; + + const pipe = new Pipe(command); + + if (isObject(callbacks)) { + for (const [name, callback] of Object.entries(callbacks)) { + pipe.setCallback(name, callback); + } + } + + formatted.push(validateString(pipe.run(self[workingDataSymbol]))); + + text = text.substring(endIndex + closeMarker.length); + + } + + return formatted.join(''); +} +'use strict'; + +/** + * Namespace for texts. + * + * @namespace Monster.Text + * @memberOf Monster + * @author schukai GmbH + */ +const ns = {}; +'use strict'; + +/** + * @author schukai GmbH + */ + +import {extend} from "../../../data/extend.mjs"; +import {Link} from "../link.mjs"; + +export {Stylesheet} + +/** + * This class is used by the resource manager to embed external resources. + * + * ``` + * <script type="module"> + * import {Style} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/resource/link/stylesheet.mjs'; + * new Stylesheet() + * </script> + * ``` + * + * @since 1.25.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM.Resource + * @summary A Resource class + * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link + */ +class Stylesheet extends Link { + + /** + * @property {string} rel {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-rel} + */ + get defaults() { + return extend({}, super.defaults, { + rel: 'stylesheet' + }) + } + +} + +'use strict'; + +/** + * In this namespace you will find classes and methods for links + * + * @namespace Monster.DOM.Resource.Link + * @memberOf Monster + * @author schukai GmbH + */ +const ns = {};'use strict'; + +/** + * In this namespace you will find classes and methods for handling resources. + * + * @namespace Monster.DOM.Resource + * @memberOf Monster + * @author schukai GmbH + */ +const ns = {}; +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalStateSymbol} from "../../constants.mjs"; +import {extend} from "../../data/extend.mjs"; +import {getGlobalFunction} from "../../types/global.mjs"; +import { + ATTRIBUTE_CLASS, + ATTRIBUTE_ERRORMESSAGE, + ATTRIBUTE_ID, + ATTRIBUTE_SRC, + ATTRIBUTE_TITLE, + ATTRIBUTE_TYPE, + TAG_SCRIPT +} from "../constants.mjs"; +import {KEY_DOCUMENT, KEY_QUERY, referenceSymbol, Resource} from "../resource.mjs"; + +export {Data} + +/** + * This class is used by the resource manager to embed data. + * + * ``` + * <script type="module"> + * import {Data} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/resource/data.mjs'; + * new Data() + * </script> + * ``` + * + * @since 1.25.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM.Resource + * @summary A Data Resource class + */ +class Data extends Resource { + + /** + * @property {string} mode=cors https://developer.mozilla.org/en-US/docs/Web/API/fetch + * @property {string} credentials=same-origin https://developer.mozilla.org/en-US/docs/Web/API/fetch + * @property {string} type=application/json {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-type} + */ + get defaults() { + return extend({}, super.defaults, { + mode: 'cors', + credentials: 'same-origin', + type: 'application/json', + }) + } + + /** + * + * @return {Monster.DOM.Resource.Data} + */ + create() { + createElement.call(this); + return this; + } + + /** + * This method appends the HTMLElement to the specified document + * + * throws {Error} target not found + * @return {Monster.DOM.Resource} + */ + connect() { + + if (!(this[referenceSymbol] instanceof HTMLElement)) { + this.create(); + } + + appendToDocument.call(this); + return this; + } + + /** + * @return {string} + */ + static getURLAttribute() { + return ATTRIBUTE_SRC + } + +} + +/** + * @private + * @return {Monster.DOM.Resource.Data} + */ +function createElement() { + const self = this; + + const document = self.getOption(KEY_DOCUMENT); + self[referenceSymbol] = document.createElement(TAG_SCRIPT); + + for (let key of [ATTRIBUTE_TYPE, ATTRIBUTE_ID, ATTRIBUTE_CLASS, ATTRIBUTE_TITLE]) { + if (self.getOption(key) !== undefined) { + self[referenceSymbol][key] = self.getOption(key); + } + } + + return self; +} + + +/** + * @private + * @return {Promise} + * throws {Error} target not found + */ +function appendToDocument() { + const self = this; + + const targetNode = document.querySelector(self.getOption(KEY_QUERY, 'head')) + if (!(targetNode instanceof HTMLElement)) { + throw new Error('target not found') + } + + targetNode.appendChild(self[referenceSymbol]); + + getGlobalFunction('fetch')(self.getOption(ATTRIBUTE_SRC), { + method: 'GET', // *GET, POST, PUT, DELETE, etc. + mode: self.getOption('mode', 'cors'), // no-cors, *cors, same-origin + cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached + credentials: self.getOption('credentials', 'same-origin'), // include, *same-origin, omit + headers: { + 'Accept': self.getOption('type', 'application/json') + }, + redirect: 'follow', // manual, *follow, error + referrerPolicy: 'no-referrer', // no-referrer, + }).then(response => { + + return response.text() + + + }).then(text => { + + const textNode = document.createTextNode(text); + self[referenceSymbol].appendChild(textNode); + + self[internalStateSymbol].getSubject()['loaded'] = true; + + + }).catch(e => { + self[internalStateSymbol].setSubject({ + loaded: true, + error: e.toString(), + }) + + targetNode.setAttribute(ATTRIBUTE_ERRORMESSAGE, e.toString()); + }) + + return self; +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {extend} from "../../data/extend.mjs"; +import { + ATTRIBUTE_CLASS, + ATTRIBUTE_HREF, + ATTRIBUTE_ID, + ATTRIBUTE_NONCE, ATTRIBUTE_SRC, + ATTRIBUTE_TITLE, ATTRIBUTE_TYPE, + TAG_LINK +} from "../constants.mjs"; +import {KEY_DOCUMENT, referenceSymbol, Resource} from "../resource.mjs"; + +export {Link} + +/** + * This class is used by the resource manager to embed external resources. + * + * ``` + * <script type="module"> + * import {Link} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/resource/link.mjs'; + * new Link() + * </script> + * ``` + * + * @since 1.25.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM.Resource + * @summary A Resource class + * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link + */ +class Link extends Resource { + + /** + * @property {string} as {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-as} + * @property {string} crossOrigin=anonymous {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-crossorigin} + * @property {boolean} disabled + * @property {string} href {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-href} + * @property {string} hreflang {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-hreflang} + * @property {string} imagesizes {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-imagesizes} + * @property {string} imagesrcset {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-imagesrcset} + * @property {string} integrity {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-integrity} + * @property {string} media {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-media} + * @property {string} prefetch {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-prefetch} + * @property {string} referrerpolicy {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-referrerpolicy} + * @property {string} rel {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-rel} + * @property {string} type {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-type} + * @property {string} sizes {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-sizes} + * @property {string} nonce {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-nonce} + */ + get defaults() { + return extend({}, super.defaults, { + as: undefined, + crossOrigin: 'anonymous', + disabled: undefined, + href: undefined, + hreflang: undefined, + imagesizes: undefined, + imagesrcset: undefined, + integrity: undefined, + media: undefined, + prefetch: undefined, + referrerpolicy: undefined, + rel: undefined, + sizes: undefined, + type: undefined, + nonce: undefined + }) + } + + /** + * + * @return {Monster.DOM.Resource.Link} + */ + create() { + createElement.call(this); + return this; + } + + /** + * @return {string} + */ + static getURLAttribute() { + return ATTRIBUTE_HREF + } + +} + +/** + * @private + * @return {Monster.DOM.Resource.Link} + */ +function createElement() { + const self = this; + + const document = self.getOption(KEY_DOCUMENT); + self[referenceSymbol] = document.createElement(TAG_LINK); + + for (let key of ['as','crossOrigin','disabled','href','hreflang','imagesizes','imagesrcset','integrity','media','prefetch','referrerpolicy','sizes','rel','type',ATTRIBUTE_HREF,ATTRIBUTE_ID,ATTRIBUTE_CLASS,ATTRIBUTE_TITLE,ATTRIBUTE_NONCE]) { + if (self.getOption(key) !== undefined) { + self[referenceSymbol][key] = self.getOption(key); + } + } + + return self; +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {extend} from "../../data/extend.mjs"; +import { + ATTRIBUTE_CLASS, + ATTRIBUTE_ID, + ATTRIBUTE_NONCE, + ATTRIBUTE_SRC, + ATTRIBUTE_TITLE, + ATTRIBUTE_TYPE, + TAG_SCRIPT +} from "../constants.mjs"; +import {KEY_DOCUMENT, referenceSymbol, Resource} from "../resource.mjs"; + +export {Script} + +/** + * This class is used by the resource manager to embed scripts. + * + * ``` + * <script type="module"> + * import {Script} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/resource/script.mjs'; + * new Script() + * </script> + * ``` + * + * @since 1.25.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM.Resource + * @summary A Resource class + */ +class Script extends Resource { + + /** + * @property {boolean} async=true {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-async} + * @property {string} crossOrigin=anonymous {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-crossorigin} + * @property {boolean} defer=false {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-defer} + * @property {string} integrity {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-integrity} + * @property {boolean} nomodule {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-nomodule} + * @property {string} nonce {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-nonce} + * @property {string} referrerpolicy {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-referrerpolicy} + * @property {string} type {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attr-type} + */ + get defaults() { + return extend({}, super.defaults, { + async: true, + crossOrigin: 'anonymous', + defer: false, + integrity: undefined, + nomodule: false, + nonce: undefined, + referrerpolicy: undefined, + type: 'text/javascript', + }) + } + + /** + * + * @return {Monster.DOM.Resource.Script} + */ + create() { + createElement.call(this); + return this; + } + + /** + * @return {string} + */ + static getURLAttribute() { + return ATTRIBUTE_SRC + } + +} + +/** + * @private + * @return {Monster.DOM.Resource.Script} + */ +function createElement() { + const self = this; + + const document = self.getOption(KEY_DOCUMENT); + self[referenceSymbol] = document.createElement(TAG_SCRIPT); + + for (let key of ['crossOrigin', 'defer', 'async', 'integrity', 'nomodule', ATTRIBUTE_NONCE, 'referrerpolicy', ATTRIBUTE_TYPE, ATTRIBUTE_SRC, ATTRIBUTE_ID, ATTRIBUTE_CLASS, ATTRIBUTE_TITLE]) { + if (self.getOption(key) !== undefined) { + self[referenceSymbol][key] = self.getOption(key); + } + } + + + return self; +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../constants.mjs"; +import {diff} from "../data/diff.mjs"; +import {Pathfinder} from "../data/pathfinder.mjs"; +import {Pipe} from "../data/pipe.mjs"; +import { + ATTRIBUTE_ERRORMESSAGE, + ATTRIBUTE_UPDATER_ATTRIBUTES, + ATTRIBUTE_UPDATER_BIND, + ATTRIBUTE_UPDATER_INSERT, + ATTRIBUTE_UPDATER_INSERT_REFERENCE, + ATTRIBUTE_UPDATER_REMOVE, + ATTRIBUTE_UPDATER_REPLACE, + ATTRIBUTE_UPDATER_SELECT_THIS +} from "../dom/constants.mjs"; + +import {Base} from "../types/base.mjs"; +import {isArray, isInstance, isIterable} from "../types/is.mjs"; +import {Observer} from "../types/observer.mjs"; +import {ProxyObserver} from "../types/proxyobserver.mjs"; +import {validateArray, validateInstance} from "../types/validate.mjs"; +import {clone} from "../util/clone.mjs"; +import {trimSpaces} from "../util/trimspaces.mjs"; +import {findTargetElementFromEvent} from "./events.mjs"; +import {findDocumentTemplate} from "./template.mjs"; +import {getDocument} from "./util.mjs"; + +export {Updater} + +/** + * The updater class connects an object with the dom. In this way, structures and contents in the DOM can be programmatically adapted via attributes. + * + * For example, to include a string from an object, the attribute `data-monster-replace` can be used. + * a further explanation can be found under {@tutorial dom-based-templating-implementation}. + * + * Changes to attributes are made only when the direct values are changed. If you want to assign changes to other values + * as well, you have to insert the attribute `data-monster-select-this`. This should be done with care, as it can reduce performance. + * + * ``` + * <script type="module"> + * import {Updater} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/updater.mjs'; + * new Updater() + * </script> + * ``` + * + * @example + * + * import {Updater} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/updater.mjs'; + * + * // First we prepare the html document. + * // This is done here via script, but can also be inserted into the document as pure html. + * // To do this, simply insert the tag <h1 data-monster-replace="path:headline"></h1>. + * const body = document.querySelector('body'); + * const headline = document.createElement('h1'); + * headline.setAttribute('data-monster-replace','path:headline') + * body.appendChild(headline); + * + * // the data structure + * let obj = { + * headline: "Hello World", + * }; + * + * // Now comes the real magic. we pass the updater the parent HTMLElement + * // and the desired data structure. + * const updater = new Updater(body, obj); + * updater.run(); + * + * // Now you can change the data structure and the HTML will follow these changes. + * const subject = updater.getSubject(); + * subject['headline'] = "Hello World!" + * + * @since 1.8.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @throws {Error} the value is not iterable + * @throws {Error} pipes are not allowed when cloning a node. + * @throws {Error} no template was found with the specified key. + * @throws {Error} the maximum depth for the recursion is reached. + * @throws {TypeError} value is not a object + * @throws {TypeError} value is not an instance of HTMLElement + * @summary The updater class connects an object with the dom + */ +class Updater extends Base { + + /** + * @since 1.8.0 + * @param {HTMLElement} element + * @param {object|ProxyObserver|undefined} subject + * @throws {TypeError} value is not a object + * @throws {TypeError} value is not an instance of HTMLElement + * @see {@link Monster.DOM.findDocumentTemplate} + */ + constructor(element, subject) { + super(); + + /** + * @type {HTMLElement} + */ + if (subject === undefined) subject = {} + if (!isInstance(subject, ProxyObserver)) { + subject = new ProxyObserver(subject); + } + + this[internalSymbol] = { + element: validateInstance(element, HTMLElement), + last: {}, + callbacks: new Map(), + eventTypes: ['keyup', 'click', 'change', 'drop', 'touchend', 'input'], + subject: subject + } + + this[internalSymbol].callbacks.set('checkstate', getCheckStateCallback.call(this)); + + this[internalSymbol].subject.attachObserver(new Observer(() => { + + const s = this[internalSymbol].subject.getRealSubject(); + + const diffResult = diff(this[internalSymbol].last, s) + this[internalSymbol].last = clone(s); + + for (const [, change] of Object.entries(diffResult)) { + removeElement.call(this, change); + insertElement.call(this, change); + updateContent.call(this, change); + updateAttributes.call(this, change); + } + })); + + } + + /** + * Defaults: 'keyup', 'click', 'change', 'drop', 'touchend' + * + * @see {@link https://developer.mozilla.org/de/docs/Web/Events} + * @since 1.9.0 + * @param {Array} types + * @return {Updater} + */ + setEventTypes(types) { + this[internalSymbol].eventTypes = validateArray(types); + return this; + } + + /** + * With this method, the eventlisteners are hooked in and the magic begins. + * + * ``` + * updater.run().then(() => { + * updater.enableEventProcessing(); + * }); + * ``` + * + * @since 1.9.0 + * @return {Updater} + * @throws {Error} the bind argument must start as a value with a path + */ + enableEventProcessing() { + this.disableEventProcessing(); + + for (const type of this[internalSymbol].eventTypes) { + // @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener + this[internalSymbol].element.addEventListener(type, getControlEventHandler.call(this), { + capture: true, + passive: true + }); + } + + return this; + + } + + /** + * This method turns off the magic or who loves it more profane it removes the eventListener. + * + * @since 1.9.0 + * @return {Updater} + */ + disableEventProcessing() { + + for (const type of this[internalSymbol].eventTypes) { + this[internalSymbol].element.removeEventListener(type, getControlEventHandler.call(this)); + } + + return this; + + } + + /** + * The run method must be called for the update to start working. + * The method ensures that changes are detected. + * + * ``` + * updater.run().then(() => { + * updater.enableEventProcessing(); + * }); + * ``` + * + * @summary Let the magic begin + * @return {Promise} + */ + run() { + // the key __init__has no further meaning and is only + // used to create the diff for empty objects. + this[internalSymbol].last = {'__init__': true}; + return this[internalSymbol].subject.notifyObservers(); + } + + /** + * Gets the values of bound elements and changes them in subject + * + * @since 1.27.0 + * @return {Monster.DOM.Updater} + */ + retrieve() { + retrieveFromBindings.call(this); + return this; + } + + /** + * If you have passed a ProxyObserver in the constructor, you will get the object that the ProxyObserver manages here. + * However, if you passed a simple object, here you will get a proxy for that object. + * + * For changes the ProxyObserver must be used. + * + * @since 1.8.0 + * @return {Proxy} + */ + getSubject() { + return this[internalSymbol].subject.getSubject(); + } + + /** + * This method can be used to register commands that can be called via call: instruction. + * This can be used to provide a pipe with its own functionality. + * + * @param {string} name + * @param {function} callback + * @returns {Transformer} + * @throws {TypeError} value is not a string + * @throws {TypeError} value is not a function + */ + setCallback(name, callback) { + this[internalSymbol].callbacks.set(name, callback); + return this; + } + +} + +/** + * @private + * @since 1.9.0 + * @return {function + * @this Updater + */ +function getCheckStateCallback() { + const self = this; + + return function (current) { + + // this is a reference to the current object (therefore no array function here) + if (this instanceof HTMLInputElement) { + if (['radio', 'checkbox'].indexOf(this.type) !== -1) { + return (this.value + "" === current + "") ? 'true' : undefined + } + } else if (this instanceof HTMLOptionElement) { + + if (isArray(current) && current.indexOf(this.value) !== -1) { + return 'true' + } + + return undefined; + } + } +} + +/** + * @private + */ +const symbol = Symbol('EventHandler'); + +/** + * @private + * @return {function} + * @this Updater + * @throws {Error} the bind argument must start as a value with a path + */ +function getControlEventHandler() { + + const self = this; + + if (self[symbol]) { + return self[symbol]; + } + + /** + * @throws {Error} the bind argument must start as a value with a path. + * @throws {Error} unsupported object + * @param {Event} event + */ + self[symbol] = (event) => { + const element = findTargetElementFromEvent(event, ATTRIBUTE_UPDATER_BIND); + + if (element === undefined) { + return; + } + + retrieveAndSetValue.call(self, element); + + } + + return self[symbol]; + + +} + +/** + * @throws {Error} the bind argument must start as a value with a path + * @param {HTMLElement} element + * @return void + * @memberOf Monster.DOM + * @private + */ +function retrieveAndSetValue(element) { + + const self = this; + + const pathfinder = new Pathfinder(self[internalSymbol].subject.getSubject()); + + let path = element.getAttribute(ATTRIBUTE_UPDATER_BIND); + + if (path.indexOf('path:') !== 0) { + throw new Error('the bind argument must start as a value with a path'); + } + + path = path.substr(5); + + let value; + + if (element instanceof HTMLInputElement) { + switch (element.type) { + + case 'checkbox': + value = element.checked ? element.value : undefined; + break; + default: + value = element.value; + break; + + + } + } else if (element instanceof HTMLTextAreaElement) { + value = element.value; + + } else if (element instanceof HTMLSelectElement) { + + switch (element.type) { + case 'select-one': + value = element.value; + break; + case 'select-multiple': + value = element.value; + + let options = element?.selectedOptions; + if (options === undefined) options = element.querySelectorAll(":scope option:checked"); + value = Array.from(options).map(({value}) => value); + + break; + } + + + // values from customelements + } else if ((element?.constructor?.prototype && !!Object.getOwnPropertyDescriptor(element.constructor.prototype, 'value')?.['get']) || element.hasOwnProperty('value')) { + value = element?.['value']; + } else { + throw new Error("unsupported object"); + } + + const copy = clone(self[internalSymbol].subject.getRealSubject()); + const pf = new Pathfinder(copy); + pf.setVia(path, value); + + const diffResult = diff(copy, self[internalSymbol].subject.getRealSubject()); + + if (diffResult.length > 0) { + pathfinder.setVia(path, value); + } +} + +/** + * @since 1.27.0 + * @return void + * @private + */ +function retrieveFromBindings() { + const self = this; + + if (self[internalSymbol].element.matches('[' + ATTRIBUTE_UPDATER_BIND + ']')) { + retrieveAndSetValue.call(self, element) + } + + for (const [, element] of self[internalSymbol].element.querySelectorAll('[' + ATTRIBUTE_UPDATER_BIND + ']').entries()) { + retrieveAndSetValue.call(self, element) + } + +} + +/** + * @private + * @since 1.8.0 + * @param {object} change + * @return {void} + */ +function removeElement(change) { + const self = this; + + for (const [, element] of self[internalSymbol].element.querySelectorAll(':scope [' + ATTRIBUTE_UPDATER_REMOVE + ']').entries()) { + element.parentNode.removeChild(element); + } +} + +/** + * @private + * @since 1.8.0 + * @param {object} change + * @return {void} + * @throws {Error} the value is not iterable + * @throws {Error} pipes are not allowed when cloning a node. + * @throws {Error} no template was found with the specified key. + * @throws {Error} the maximum depth for the recursion is reached. + * @this Updater + */ +function insertElement(change) { + const self = this; + const subject = self[internalSymbol].subject.getRealSubject(); + const document = getDocument(); + + let mem = new WeakSet; + let wd = 0; + + const container = self[internalSymbol].element; + + while (true) { + let found = false; + wd++; + + let p = clone(change?.['path']); + if (!isArray(p)) return self; + + while (p.length > 0) { + const current = p.join('.'); + + let iterator = new Set; + const query = '[' + ATTRIBUTE_UPDATER_INSERT + '*="path:' + current + '"]'; + + const e = container.querySelectorAll(query); + + if (e.length > 0) { + iterator = new Set( + [...e] + ) + } + + if (container.matches(query)) { + iterator.add(container); + } + + for (const [, containerElement] of iterator.entries()) { + + if (mem.has(containerElement)) continue; + mem.add(containerElement) + + found = true; + + const attributes = containerElement.getAttribute(ATTRIBUTE_UPDATER_INSERT); + let def = trimSpaces(attributes); + let i = def.indexOf(' '); + let key = trimSpaces(def.substr(0, i)); + let refPrefix = key + '-'; + let cmd = trimSpaces(def.substr(i)); + + // this case is actually excluded by the query but is nevertheless checked again here + if (cmd.indexOf('|') > 0) { + throw new Error("pipes are not allowed when cloning a node."); + } + + let pipe = new Pipe(cmd); + self[internalSymbol].callbacks.forEach((f, n) => { + pipe.setCallback(n, f); + }) + + let value + try { + containerElement.removeAttribute(ATTRIBUTE_ERRORMESSAGE); + value = pipe.run(subject) + } catch (e) { + containerElement.setAttribute(ATTRIBUTE_ERRORMESSAGE, e.message); + } + + let dataPath = cmd.split(':').pop(); + + let insertPoint; + if (containerElement.hasChildNodes()) { + insertPoint = containerElement.lastChild; + } + + if (!isIterable(value)) { + throw new Error('the value is not iterable'); + } + + let available = new Set; + + for (const [i, obj] of Object.entries(value)) { + let ref = refPrefix + i; + let currentPath = dataPath + "." + i; + + available.add(ref); + let refElement = containerElement.querySelector('[' + ATTRIBUTE_UPDATER_INSERT_REFERENCE + '="' + ref + '"]'); + + if (refElement instanceof HTMLElement) { + insertPoint = refElement; + continue; + } + + appendNewDocumentFragment(containerElement, key, ref, currentPath); + } + + let nodes = containerElement.querySelectorAll('[' + ATTRIBUTE_UPDATER_INSERT_REFERENCE + '*="' + refPrefix + '"]'); + for (const [, node] of Object.entries(nodes)) { + if (!available.has(node.getAttribute(ATTRIBUTE_UPDATER_INSERT_REFERENCE))) { + try { + containerElement.removeChild(node); + } catch (e) { + containerElement.setAttribute(ATTRIBUTE_ERRORMESSAGE, (containerElement.getAttribute(ATTRIBUTE_ERRORMESSAGE) + ", " + e.message).trim()); + } + + } + } + } + + p.pop(); + } + + if (found === false) break; + if (wd++ > 200) { + throw new Error('the maximum depth for the recursion is reached.'); + } + + } + + +} + +/** + * + * @private + * @since 1.8.0 + * @param {HTMLElement} container + * @param {string} key + * @param {string} ref + * @param {string} path + * @throws {Error} no template was found with the specified key. + */ +function appendNewDocumentFragment(container, key, ref, path) { + + let template = findDocumentTemplate(key, container); + + let nodes = template.createDocumentFragment(); + for (const [, node] of Object.entries(nodes.childNodes)) { + if (node instanceof HTMLElement) { + + applyRecursive(node, key, path); + node.setAttribute(ATTRIBUTE_UPDATER_INSERT_REFERENCE, ref); + } + + container.appendChild(node); + } +} + +/** + * @private + * @since 1.10.0 + * @param {HTMLElement} node + * @param {string} key + * @param {string} path + * @return {void} + */ +function applyRecursive(node, key, path) { + + if (node instanceof HTMLElement) { + + if (node.hasAttribute(ATTRIBUTE_UPDATER_REPLACE)) { + let value = node.getAttribute(ATTRIBUTE_UPDATER_REPLACE); + node.setAttribute(ATTRIBUTE_UPDATER_REPLACE, value.replaceAll("path:" + key, "path:" + path)); + } + + if (node.hasAttribute(ATTRIBUTE_UPDATER_ATTRIBUTES)) { + let value = node.getAttribute(ATTRIBUTE_UPDATER_ATTRIBUTES); + node.setAttribute(ATTRIBUTE_UPDATER_ATTRIBUTES, value.replaceAll("path:" + key, "path:" + path)); + } + + for (const [, child] of Object.entries(node.childNodes)) { + applyRecursive(child, key, path); + } + } +} + +/** + * @private + * @since 1.8.0 + * @param {object} change + * @return {void} + * @this Updater + */ +function updateContent(change) { + const self = this; + const subject = self[internalSymbol].subject.getRealSubject(); + + let p = clone(change?.['path']); + runUpdateContent.call(this, this[internalSymbol].element, p, subject); + + const slots = this[internalSymbol].element.querySelectorAll('slot'); + if (slots.length > 0) { + for (const [, slot] of Object.entries(slots)) { + for (const [, element] of Object.entries(slot.assignedNodes())) { + runUpdateContent.call(this, element, p, subject); + } + } + } + + +} + +/** + * @private + * @since 1.8.0 + * @param {HTMLElement} container + * @param {array} parts + * @param {object} subject + * @return {void} + */ +function runUpdateContent(container, parts, subject) { + if (!isArray(parts)) return; + if (!(container instanceof HTMLElement)) return; + parts = clone(parts); + + let mem = new WeakSet; + + while (parts.length > 0) { + const current = parts.join('.'); + parts.pop(); + + // Unfortunately, static data is always changed as well, since it is not possible to react to changes here. + const query = '[' + ATTRIBUTE_UPDATER_REPLACE + '^="path:' + current + '"], [' + ATTRIBUTE_UPDATER_REPLACE + '^="static:"]'; + const e = container.querySelectorAll('' + query); + + const iterator = new Set([ + ...e + ]) + + if (container.matches(query)) { + iterator.add(container); + } + + /** + * @type {HTMLElement} + */ + for (const [element] of iterator.entries()) { + + if (mem.has(element)) return; + mem.add(element) + + const attributes = element.getAttribute(ATTRIBUTE_UPDATER_REPLACE) + let cmd = trimSpaces(attributes); + + let pipe = new Pipe(cmd); + this[internalSymbol].callbacks.forEach((f, n) => { + pipe.setCallback(n, f); + }) + + let value + try { + element.removeAttribute(ATTRIBUTE_ERRORMESSAGE); + value = pipe.run(subject) + } catch (e) { + element.setAttribute(ATTRIBUTE_ERRORMESSAGE, e.message); + } + + if (value instanceof HTMLElement) { + while (element.firstChild) { + element.removeChild(element.firstChild); + } + + try { + element.appendChild(value); + } catch (e) { + element.setAttribute(ATTRIBUTE_ERRORMESSAGE, (element.getAttribute(ATTRIBUTE_ERRORMESSAGE) + ", " + e.message).trim()); + } + + } else { + element.innerHTML = value; + } + + } + + + } + +} + +/** + * @private + * @since 1.8.0 + * @param {string} path + * @param {object} change + * @return {void} + */ +function updateAttributes(change) { + const subject = this[internalSymbol].subject.getRealSubject(); + let p = clone(change?.['path']); + runUpdateAttributes.call(this, this[internalSymbol].element, p, subject); +} + +/** + * @private + * @param {HTMLElement} container + * @param {array} parts + * @param {object} subject + * @return {void} + * @this Updater + */ +function runUpdateAttributes(container, parts, subject) { + + const self = this; + + if (!isArray(parts)) return; + parts = clone(parts); + + let mem = new WeakSet; + + while (parts.length > 0) { + const current = parts.join('.'); + parts.pop(); + + let iterator = new Set; + + const query = '[' + ATTRIBUTE_UPDATER_SELECT_THIS + '], [' + ATTRIBUTE_UPDATER_ATTRIBUTES + '*="path:' + current + '"], [' + ATTRIBUTE_UPDATER_ATTRIBUTES + '^="static:"]'; + + const e = container.querySelectorAll(query); + + if (e.length > 0) { + iterator = new Set( + [...e] + ) + } + + if (container.matches(query)) { + iterator.add(container); + } + + for (const [element] of iterator.entries()) { + + if (mem.has(element)) return; + mem.add(element) + + const attributes = element.getAttribute(ATTRIBUTE_UPDATER_ATTRIBUTES) + + for (let [, def] of Object.entries(attributes.split(','))) { + def = trimSpaces(def); + let i = def.indexOf(' '); + let name = trimSpaces(def.substr(0, i)); + let cmd = trimSpaces(def.substr(i)); + + let pipe = new Pipe(cmd); + + self[internalSymbol].callbacks.forEach((f, n) => { + pipe.setCallback(n, f, element); + }) + + let value + try { + element.removeAttribute(ATTRIBUTE_ERRORMESSAGE); + value = pipe.run(subject) + } catch (e) { + element.setAttribute(ATTRIBUTE_ERRORMESSAGE, e.message); + } + + + if (value === undefined) { + element.removeAttribute(name) + + } else if (element.getAttribute(name) !== value) { + element.setAttribute(name, value); + } + + handleInputControlAttributeUpdate.call(this, element, name, value); + + } + } + } + +} + +/** + * @private + * @param {HTMLElement|*} element + * @param {string} name + * @param {string|number|undefined} value + * @return {void} + * @this Updater + */ + +function handleInputControlAttributeUpdate(element, name, value) { + const self = this; + + if (element instanceof HTMLSelectElement) { + + + switch (element.type) { + case 'select-multiple': + + for (const [index, opt] of Object.entries(element.options)) { + if (value.indexOf(opt.value) !== -1) { + opt.selected = true; + } else { + opt.selected = false; + } + } + + break; + case 'select-one': + // Only one value may be selected + + for (const [index, opt] of Object.entries(element.options)) { + if (opt.value === value) { + element.selectedIndex = index; + break; + } + } + + break; + } + + + } else if (element instanceof HTMLInputElement) { + switch (element.type) { + + case 'radio': + if (name === 'checked') { + + if (value !== undefined) { + element.checked = true; + } else { + element.checked = false; + } + } + + break; + + case 'checkbox': + + if (name === 'checked') { + + if (value !== undefined) { + element.checked = true; + } else { + element.checked = false; + } + } + + break; + case 'text': + default: + if (name === 'value') { + element.value = (value === undefined ? "" : value); + } + + break; + + + } + } else if (element instanceof HTMLTextAreaElement) { + if (name === 'value') { + element.value = (value === undefined ? "" : value); + } + } + +} +'use strict'; + +import {extend} from "../data/extend.mjs"; +/** + * @author schukai GmbH + */ + +import {ATTRIBUTE_VALUE} from "./constants.mjs"; +import {CustomElement, attributeObserverSymbol} from "./customelement.mjs"; + +export {CustomControl} + +/** + * @private + * @type {symbol} + */ +const attachedInternalSymbol = Symbol('attachedInternal'); + +/** + * To define a new HTML control we need the power of CustomElement + * + * IMPORTANT: after defining a `CustomElement`, the `registerCustomElement` method must be called + * with the new class name. only then will the tag defined via the `getTag` method be made known to the DOM. + * + * <img src="./images/customcontrol-class.png"> + * + * This control uses `attachInternals()` to integrate the control into a form. + * If the target environment does not support this method, the [polyfill](https://www.npmjs.com/package/element-internals-polyfill ) can be used. + * + * You can create the object via the function `document.createElement()`. + * + * ``` + * <script type="module"> + * import {CustomControl} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source//monster.mjs'; + * document.createElement('monster-') + * </script> + * ``` + * + * @startuml customcontrol-class.png + * skinparam monochrome true + * skinparam shadowing false + * HTMLElement <|-- CustomElement + * CustomElement <|-- CustomControl + * @enduml + * + * @summary A base class for customcontrols based on CustomElement + * @see {@link https://www.npmjs.com/package/element-internals-polyfill} + * @see {@link https://github.com/WICG/webcomponents} + * @see {@link https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements} + * @since 1.14.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + */ +class CustomControl extends CustomElement { + + /** + * IMPORTANT: CustomControls instances are not created via the constructor, but either via a tag in the HTML or via <code>document.createElement()</code>. + * + * @throws {Error} the ElementInternals is not supported and a polyfill is necessary + * @summary create new Instance + */ + constructor() { + super(); + + if (typeof this['attachInternals'] === 'function') { + /** + * currently only supported by chrome + * @property {Object} + * @private + */ + this[attachedInternalSymbol] = this.attachInternals(); + } + + initObserver.call(this); + + } + + /** + * This method determines which attributes are to be monitored by `attributeChangedCallback()`. + * + * @return {string[]} + * @since 1.15.0 + */ + static get observedAttributes() { + const list = super.observedAttributes; + list.push(ATTRIBUTE_VALUE); + return list; + } + + /** + * + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/attachInternals} + * @since 1.14.0 + * @return {boolean} + */ + static get formAssociated() { + return true; + } + + /** + * Derived classes can override and extend this method as follows. + * + * ``` + * get defaults() { + * return extends{}, super.defaults, { + * myValue:true + * }); + * } + * ``` + * + * @see {@link https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements-face-example} + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/attachInternals} + * @return {object} + * @since 1.14.0 + */ + get defaults() { + return extend({}, super.defaults); + } + + /** + * Must be overridden by a derived class and return the value of the control. + * + * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals) + * + * @since 1.14.0 + * @throws {Error} the value getter must be overwritten by the derived class + */ + get value() { + throw Error('the value getter must be overwritten by the derived class'); + } + + /** + * Must be overridden by a derived class and return the value of the control. + * + * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals) + * + * @param {*} value + * @since 1.14.0 + * @throws {Error} the value setter must be overwritten by the derived class + */ + set value(value) { + throw Error('the value setter must be overwritten by the derived class'); + } + + /** + * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals) + * + * @return {NodeList} + * @since 1.14.0 + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/labels} + * @throws {Error} the ElementInternals is not supported and a polyfill is necessary + */ + get labels() { + return getInternal.call(this)?.labels; + } + + /** + * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals) + * + * @return {string|null} + */ + get name() { + return this.getAttribute('name'); + } + + /** + * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals) + * + * @return {string} + */ + get type() { + return this.constructor.getTag(); + } + + /** + * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals) + * + * @return {ValidityState} + * @throws {Error} the ElementInternals is not supported and a polyfill is necessary + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/ValidityState} + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/validity} + */ + get validity() { + return getInternal.call(this)?.validity; + } + + /** + * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals) + * + * @return {string} + * @since 1.14.0 + * @see https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/validationMessage + * @throws {Error} the ElementInternals is not supported and a polyfill is necessary + */ + get validationMessage() { + return getInternal.call(this)?.validationMessage; + } + + /** + * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals) + * + * @return {boolean} + * @since 1.14.0 + * @see https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/willValidate + * @throws {Error} the ElementInternals is not supported and a polyfill is necessary + */ + get willValidate() { + return getInternal.call(this)?.willValidate; + } + + /** + * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals) + * + * @return {CustomStateSet} + * @since 1.14.0 + * @see https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/states + * @throws {Error} the ElementInternals is not supported and a polyfill is necessary + */ + get states() { + return getInternal.call(this)?.states; + } + + /** + * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals) + * + * @return {HTMLFontElement|null} + * @since 1.14.0 + * @see https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/form + * @throws {Error} the ElementInternals is not supported and a polyfill is necessary + */ + get form() { + return getInternal.call(this)?.form; + } + + /** + * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals) + * + * ``` + * // Use the control's name as the base name for submitted data + * const n = this.getAttribute('name'); + * const entries = new FormData(); + * entries.append(n + '-first-name', this.firstName_); + * entries.append(n + '-last-name', this.lastName_); + * this.setFormValue(entries); + * ``` + * + * @param {File|string|FormData} value + * @param {File|string|FormData} state + * @since 1.14.0 + * @return {undefined} + * @throws {DOMException} NotSupportedError + * @throws {Error} the ElementInternals is not supported and a polyfill is necessary + * @see https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/setFormValue + */ + setFormValue(value, state) { + getInternal.call(this).setFormValue(value, state); + } + + /** + * + * @param {object} flags + * @param {string|undefined} message + * @param {HTMLElement} anchor + * @see https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/setValidity + * @since 1.14.0 + * @return {undefined} + * @throws {DOMException} NotSupportedError + * @throws {Error} the ElementInternals is not supported and a polyfill is necessary + */ + setValidity(flags, message, anchor) { + getInternal.call(this).setValidity(flags, message, anchor); + } + + /** + * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals) + * + * @see https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/checkValidity + * @since 1.14.0 + * @return {boolean} + * @throws {DOMException} NotSupportedError + * @throws {Error} the ElementInternals is not supported and a polyfill is necessary + */ + checkValidity() { + return getInternal.call(this)?.checkValidity(); + } + + /** + * This is a method of [internal api](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals) + * + * @return {boolean} + * @since 1.14.0 + * @see https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/reportValidity + * @throws {Error} the ElementInternals is not supported and a polyfill is necessary + * @throws {DOMException} NotSupportedError + */ + reportValidity() { + return getInternal.call(this)?.reportValidity(); + } + +} + +/** + * @private + * @return {object} + * @throws {Error} the ElementInternals is not supported and a polyfill is necessary + * @this CustomControl + */ +function getInternal() { + const self = this; + + if (!(attachedInternalSymbol in this)) { + throw new Error('ElementInternals is not supported and a polyfill is necessary'); + } + + return this[attachedInternalSymbol]; +} + +/** + * @private + * @return {object} + * @this CustomControl + */ +function initObserver() { + const self = this; + + // value + self[attributeObserverSymbol]['value'] = () => { + self.setOption('value', self.getAttribute('value')); + } + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {parseLocale} from "../i18n/locale.mjs"; + +import {getDocument} from "./util.mjs"; + +export {getLocaleOfDocument} + +/** + * @private + * @type {string} + */ +const DEFAULT_LANGUAGE = 'en'; + +/** + * With this function you can read the language version set by the document. + * For this the attribute `lang` in the html tag is read. If no attribute is set, `en` is used as default. + * + * ```html + * <html lang="en"> + * ``` + * + * You can call the function via the monster namespace `new Monster.DOM.getLocaleOfDocument()`. + * + * ``` + * <script type="module"> + * import {getLocaleOfDocument} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/locale.mjs'; + * new getLocaleOfDocument() + * </script> + * ``` + * + * @since 1.13.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @throws {TypeError} value is not a string + * @throws {Error} unsupported locale + * @summary Tries to determine the locale used + */ +function getLocaleOfDocument() { + + const document = getDocument(); + + let html = document.querySelector('html') + if (html instanceof HTMLElement && html.hasAttribute('lang')) { + let locale = html.getAttribute('lang'); + if (locale) { + return new parseLocale(locale) + } + } + + return parseLocale(DEFAULT_LANGUAGE); +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from '../types/base.mjs'; +import {getGlobalObject} from '../types/global.mjs'; +import {validateString} from "../types/validate.mjs"; +import {ATTRIBUTE_THEME_NAME, DEFAULT_THEME} from "./constants.mjs"; + +export {Theme, getDocumentTheme} + +/** + * The Theme class provides the functionality for the theme. + * + * ``` + * <script type="module"> + * import {Theme} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/theme.mjs'; + * console.log(new Theme()) + * </script> + * ``` + * + * @example + * + * import {getDocumentTheme} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/theme.mjs'; + * + * const theme = getDocumentTheme(); + * console.log(theme.getName()); + * // ↦ monster + * + * @since 1.7.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @summary A theme class + */ +class Theme extends Base { + + /** + * + * @param name + * @throws {TypeError} value is not a string + */ + constructor(name) { + super(); + validateString(name); + this.name = name; + } + + /** + * + * @returns {string} + */ + getName() { + return this.name; + } + +} + +/** + * The theming used in the document can be defined via the html-tag. + * The theming is specified via the attribute `data-monster-theme-name`. + * + * As name for a theme all characters are valid, which are also allowed for a HTMLElement-ID. + * + * ``` + * <html data-monster-theme-name="my-theme"> + * ``` + * + * the default theme name is `monster`. + * + * @return {Theme} + * @memberOf Monster.DOM + * @since 1.7.0 + */ +function getDocumentTheme() { + let document = getGlobalObject('document'); + let name = DEFAULT_THEME; + + let element = document.querySelector('html'); + if (element instanceof HTMLElement) { + let theme = element.getAttribute(ATTRIBUTE_THEME_NAME); + if (theme) { + name = theme; + } + } + + return new Theme(name); + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalStateSymbol, internalSymbol,} from "../constants.mjs"; +import {extend} from "../data/extend.mjs"; +import {BaseWithOptions} from "../types/basewithoptions.mjs"; +import {getGlobalObject} from "../types/global.mjs"; +import {ID} from "../types/id.mjs"; +import {isString} from "../types/is.mjs"; +import {Observer} from "../types/observer.mjs"; +import {ProxyObserver} from "../types/proxyobserver.mjs"; +import {ATTRIBUTE_CLASS, ATTRIBUTE_ID, ATTRIBUTE_TITLE} from "./constants.mjs"; + +export {Resource, KEY_DOCUMENT, KEY_QUERY, referenceSymbol} + +/** + * @private + * @type {string} + */ +const KEY_DOCUMENT = 'document'; + +/** + * @private + * @type {string} + */ +const KEY_QUERY = 'query'; + +/** + * @private + * @type {string} + */ +const KEY_TIMEOUT = 'timeout'; + +/** + * @private + * @type {symbol} + */ +const referenceSymbol = Symbol('reference'); + +/** + * This class is the base class for all resources to be loaded. + * + * ``` + * <script type="module"> + * import {Resource} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/resource.mjs'; + * new Resource() + * </script> + * ``` + * + * @since 1.25.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @summary A Resource class + */ +class Resource extends BaseWithOptions { + + /** + * + * @param {Object|undefined} options + */ + constructor(options) { + super(options); + + let uri = this.getOption(this.constructor.getURLAttribute()); + + if (uri === undefined) { + throw new Error('missing source') + } else if (uri instanceof URL) { + uri = uri.toString(); + } else if (!isString(uri)) { + throw new Error('unsupported url type') + } + + this[internalSymbol][this.constructor.getURLAttribute()] = uri; + this[internalStateSymbol] = new ProxyObserver({ + loaded: false, + error: undefined, + }) + + this[referenceSymbol] = undefined; + + } + + /** + * @return {boolean} + */ + isConnected() { + + if (this[referenceSymbol] instanceof HTMLElement) { + return this[referenceSymbol].isConnected; + } + + return false; + } + + /** + * This method is overridden by the special classes and creates the DOM object. + * This method is also called implicitly, if not yet done explicitly, by calling `connect()`. + * + * @throws {Error} this method must be implemented by derived classes + * @return {Monster.DOM.Resource} + */ + create() { + throw new Error("this method must be implemented by derived classes"); + } + + /** + * This method appends the HTMLElement to the specified document. + * If the element has not yet been created, `create()` is called implicitly. + * + * throws {Error} target not found + * @return {Monster.DOM.Resource} + */ + connect() { + + if (!(this[referenceSymbol] instanceof HTMLElement)) { + this.create(); + } + + appendToDocument.call(this); + return this; + } + + /** + * @property {Document} document the document object into which the node is to be appended + * @property {string} src/href url to the corresponding resource + * @property {string} query defines the location where the resource is to be hooked into the dom. + * @property {string} id element attribute id + * @property {string} title element attribute title + * @property {string} class element attribute class + * @property {int} timeout timeout + */ + get defaults() { + return extend({}, super.defaults, { + [this.constructor.getURLAttribute()]: undefined, + [KEY_DOCUMENT]: getGlobalObject('document'), + [KEY_QUERY]: 'head', + [KEY_TIMEOUT]: 10000, + [ATTRIBUTE_ID]: (new ID('resource')).toString(), + [ATTRIBUTE_CLASS]: undefined, + [ATTRIBUTE_TITLE]: undefined + }) + } + + /** + * With `available()` you can check if a resource is available. + * This is the case when the tag is included and the resource is loaded. + * + * @return {Promise} + */ + available() { + const self = this; + if (!(self[referenceSymbol] instanceof HTMLElement)) { + return Promise.reject('no element') + } + + if (!self.isConnected()) { + return Promise.reject('element not connected') + } + + if (self[internalStateSymbol].getSubject()['loaded'] === true) { + + if (self[internalStateSymbol].getSubject()['error'] !== undefined) { + return Promise.reject(self[internalStateSymbol].getSubject()['error']); + } + + return Promise.resolve(); + + } + + return new Promise(function (resolve, reject) { + + const timeout = setTimeout(() => { + reject('timeout'); + }, self.getOption('timeout')) + + const observer = new Observer(() => { + clearTimeout(timeout); + self[internalStateSymbol].detachObserver(observer); + resolve(); + }) + + self[internalStateSymbol].attachObserver(observer); + + }); + + }; + + /** + * @return {string} + */ + static getURLAttribute() { + throw new Error("this method must be implemented by derived classes"); + } + +} + + +/** + * @private + * @return {Promise} + * throws {Error} target not found + */ +function appendToDocument() { + const self = this; + + const targetNode = document.querySelector(self.getOption(KEY_QUERY, 'head')) + if (!(targetNode instanceof HTMLElement)) { + throw new Error('target not found') + } + + addEvents.call(self); + targetNode.appendChild(self[referenceSymbol]); + + return self; +} + +/** + * @private + * @return {addEvents} + */ +function addEvents() { + const self = this; + + const onError = () => { + + self[referenceSymbol].removeEventListener('error', onError); + self[referenceSymbol].removeEventListener('load', onLoad); + + self[internalStateSymbol].setSubject({ + loaded: true, + error: self[referenceSymbol][self.constructor.getURLAttribute()] + ' is not available', + }) + + return; + } + + const onLoad = () => { + self[referenceSymbol].removeEventListener('error', onError); + self[referenceSymbol].removeEventListener('load', onLoad); + self[internalStateSymbol].getSubject()['loaded'] = true; + return; + } + + self[referenceSymbol].addEventListener('load', onLoad, false); + self[referenceSymbol].addEventListener('error', onError, false); + + return self; + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {extend} from "../data/extend.mjs"; +import {BaseWithOptions} from "../types/basewithoptions.mjs"; +import {getGlobalObject} from "../types/global.mjs"; +import {isArray} from "../types/is.mjs"; +import {ATTRIBUTE_HREF, ATTRIBUTE_SRC} from "./constants.mjs"; +import {Resource} from "./resource.mjs"; +import {Data} from "./resource/data.mjs"; +import {Stylesheet} from "./resource/link/stylesheet.mjs"; +import {Script} from "./resource/script.mjs"; + +export {ResourceManager} + +/** + * The ResourceManager is a singleton that manages all resources. + * + * ``` + * <script type="module"> + * import {Resource} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/resourcemanager.mjs'; + * new ResourceManager() + * </script> + * ``` + * + * @since 1.25.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @summary A Resource class + */ + class ResourceManager extends BaseWithOptions { + + /** + * + * @param {Object} options + * throw {Error} unsupported document type + */ + constructor(options) { + super(options); + + if (!(this.getOption('document') instanceof Document)) { + throw new Error('unsupported document type') + } + + + } + + /** + * @property {string} baseurl + */ + getBaseURL() { + this.getOption('document')?.baseURL; + } + + /** + * + * @property {HTMLDocument} document=document Document + * @property {Object} resources + * @property {Array} resources.scripts=[] array with {@link Monster.DOM.Resource.Script} objects + * @property {Array} resources.stylesheets=[] array with {@link Monster.DOM.Resource.Link.Stylesheet} objects + * @property {Array} resources.data=[] array with {@link Monster.DOM.Resource.Data} objects + */ + get defaults() { + return Object.assign({}, super.defaults, { + document: getGlobalObject('document'), + resources: { + scripts: [], + stylesheets: [], + data: [] + } + }) + } + + /** + * Append Tags to DOM + * + * @return {Monster.DOM.ResourceManager} + * @throws {Error} unsupported resource definition + */ + connect() { + runResourceMethod.call(this, 'connect'); + return this; + } + + /** + * Check if available + * + * @return {Promise} + * @throws {Error} unsupported resource definition + */ + available() { + return Promise.all(runResourceMethod.call(this, 'available')); + } + + /** + * Add a script + * + * @param {string|URL} url + * @param [Object|undefined} options + * @return {Monster.DOM.ResourceManager} + * @see Monster.DOM.Resource.Script + */ + addScript(url, options) { + return addResource.call(this, 'scripts', url, options); + } + + + /** + * Add Stylesheet + * + * @param {string|URL} url + * @param [Object|undefined} options + * @return {Monster.DOM.ResourceManager} + * @see Monster.DOM.Resource.Link.Stylesheet + */ + addStylesheet(url, options) { + return addResource.call(this, 'stylesheets', url, options); + } + + /** + * Add Data Tag + * + * @param {string|URL} url + * @param [Object|undefined} options + * @return {Monster.DOM.ResourceManager} + * @see Monster.DOM.Resource.Data + */ + addData(url, options) { + return addResource.call(this, 'data', url, options); + } + + +} + +/** + * @private + * @param {string} method + * @return {Array} + */ +function runResourceMethod(method) { + const self = this; + + const result = []; + + for (const type of ['scripts', 'stylesheets', 'data']) { + const resources = self.getOption('resources.' + type); + if (!isArray(resources)) { + continue; + } + + for (const resource of resources) { + if (!(resource instanceof Resource)) { + throw new Error('unsupported resource definition') + } + + result.push(resource[method]()); + } + + } + + return result; +} + +/** + * + * @param {string} type + * @param {string|URL} url + * @param [Object|undefined} options + * @return {Monster.DOM.ResourceManager} + * @private + */ +function addResource(type, url, options) { + const self = this; + + if (url instanceof URL) { + url = url.toString(); + } + + options = options || {} + + let resource; + switch (type) { + case 'scripts': + resource = new Script(extend({}, options, {[ATTRIBUTE_SRC]: url})) + break; + case 'stylesheets': + resource = new Stylesheet(extend({}, options, {[ATTRIBUTE_HREF]: url})) + break; + case 'data': + resource = new Data(extend({}, options, {[ATTRIBUTE_SRC]: url})) + break; + default: + throw new Error('unsupported type ' + type) + } + + (self.getOption('resources')?.[type]).push(resource); + return self; +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {getGlobal} from "../types/global.mjs"; +import {validateString} from "../types/validate.mjs"; + +export {getDocument, getWindow, getDocumentFragmentFromString} + +/** + * this method fetches the document object + * + * ``` + * <script type="module"> + * import {getDocument} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/util.mjs'; + * console.log(getDocument()) + * </script> + * ``` + * + * in nodejs this functionality can be performed with [jsdom](https://www.npmjs.com/package/jsdom). + * + * ``` + * import {JSDOM} from "jsdom" + * if (typeof window !== "object") { + * const {window} = new JSDOM('', { + * url: 'http://example.com/', + * pretendToBeVisual: true + * }); + * + * [ + * 'self', + * 'document', + * 'Document', + * 'Node', + * 'Element', + * 'HTMLElement', + * 'DocumentFragment', + * 'DOMParser', + * 'XMLSerializer', + * 'NodeFilter', + * 'InputEvent', + * 'CustomEvent' + * ].forEach(key => (getGlobal()[key] = window[key])); + * } + * ``` + * + * @returns {object} + * @since 1.6.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @throws {Error} not supported environment + */ +function getDocument() { + let document = getGlobal()?.['document']; + if (typeof document !== 'object') { + throw new Error("not supported environment") + } + + return document; +} + +/** + * this method fetches the window object + * + * ``` + * <script type="module"> + * import {getWindow} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/util.mjs'; + * console.log(getWindow(null)) + * </script> + * ``` + * + * in nodejs this functionality can be performed with [jsdom](https://www.npmjs.com/package/jsdom). + * + * ``` + * import {JSDOM} from "jsdom" + * if (typeof window !== "object") { + * const {window} = new JSDOM('', { + * url: 'http://example.com/', + * pretendToBeVisual: true + * }); + * + * getGlobal()['window']=window; + * + * [ + * 'self', + * 'document', + * 'Document', + * 'Node', + * 'Element', + * 'HTMLElement', + * 'DocumentFragment', + * 'DOMParser', + * 'XMLSerializer', + * 'NodeFilter', + * 'InputEvent', + * 'CustomEvent' + * ].forEach(key => (getGlobal()[key] = window[key])); + * } + * ``` + * + * @returns {object} + * @since 1.6.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @throws {Error} not supported environment + */ +function getWindow() { + let window = getGlobal()?.['window']; + if (typeof window !== 'object') { + throw new Error("not supported environment") + } + + return window; +} + + +/** + * this method fetches the document object + * + * ``` + * <script type="module"> + * import {getDocumentFragmentFromString} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/util.mjs'; + * console.log(getDocumentFragmentFromString('<div></div>')) + * </script> + * ``` + * + * in nodejs this functionality can be performed with [jsdom](https://www.npmjs.com/package/jsdom). + * + * ``` + * import {JSDOM} from "jsdom" + * if (typeof window !== "object") { + * const {window} = new JSDOM('', { + * url: 'http://example.com/', + * pretendToBeVisual: true + * }); + * + * [ + * 'self', + * 'document', + * 'Document', + * 'Node', + * 'Element', + * 'HTMLElement', + * 'DocumentFragment', + * 'DOMParser', + * 'XMLSerializer', + * 'NodeFilter', + * 'InputEvent', + * 'CustomEvent' + * ].forEach(key => (getGlobal()[key] = window[key])); + * } + * ``` + * + * @returns {DocumentFragment} + * @since 1.6.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @throws {Error} not supported environment + * @throws {TypeError} value is not a string + */ +function getDocumentFragmentFromString(html) { + validateString(html); + + const document = getDocument(); + const template = document.createElement('template'); + template.innerHTML = html; + + return template.content; +} +'use strict'; + + +/** + * @author schukai GmbH + */ + +export { + DEFAULT_THEME, + ATTRIBUTE_PREFIX, + ATTRIBUTE_OPTIONS, + ATTRIBUTE_OPTIONS_SELECTOR, + ATTRIBUTE_THEME_PREFIX, + ATTRIBUTE_THEME_NAME, + ATTRIBUTE_UPDATER_ATTRIBUTES, + ATTRIBUTE_UPDATER_SELECT_THIS, + ATTRIBUTE_UPDATER_REPLACE, + ATTRIBUTE_UPDATER_INSERT, + ATTRIBUTE_UPDATER_INSERT_REFERENCE, + ATTRIBUTE_UPDATER_REMOVE, + ATTRIBUTE_UPDATER_BIND, + ATTRIBUTE_TEMPLATE_PREFIX, + ATTRIBUTE_ROLE, + ATTRIBUTE_DISABLED, + ATTRIBUTE_VALUE, + ATTRIBUTE_OBJECTLINK, + ATTRIBUTE_ERRORMESSAGE, + TAG_SCRIPT, + TAG_STYLE, + TAG_LINK, + ATTRIBUTE_ID, + ATTRIBUTE_CLASS, + ATTRIBUTE_TITLE, + ATTRIBUTE_SRC, + ATTRIBUTE_HREF, + ATTRIBUTE_TYPE, + ATTRIBUTE_NONCE, + ATTRIBUTE_TRANSLATE, + ATTRIBUTE_TABINDEX, + ATTRIBUTE_SPELLCHECK, + ATTRIBUTE_SLOT, + ATTRIBUTE_PART, + ATTRIBUTE_LANG, + ATTRIBUTE_ITEMTYPE, + ATTRIBUTE_ITEMSCOPE, + ATTRIBUTE_ITEMREF, + ATTRIBUTE_ITEMID, + ATTRIBUTE_ITEMPROP, + ATTRIBUTE_IS, + ATTRIBUTE_INPUTMODE, + ATTRIBUTE_ACCESSKEY, + ATTRIBUTE_AUTOCAPITALIZE, + ATTRIBUTE_AUTOFOCUS, + ATTRIBUTE_CONTENTEDITABLE, + ATTRIBUTE_DIR, + ATTRIBUTE_DRAGGABLE, + ATTRIBUTE_ENTERKEYHINT, + ATTRIBUTE_EXPORTPARTS, + ATTRIBUTE_HIDDEN, + objectUpdaterLinkSymbol, + +} + +/** + * default theme + * @memberOf Monster.DOM + * @type {string} + */ +const DEFAULT_THEME = 'monster'; + +/** + * @memberOf Monster.DOM + * @since 1.8.0 + * @type {string} + */ +const ATTRIBUTE_PREFIX = 'data-monster-'; + +/** + * This is the name of the attribute to pass options to a control + * + * @memberOf Monster.DOM + * @since 1.8.0 + * @type {string} + */ +const ATTRIBUTE_OPTIONS = ATTRIBUTE_PREFIX + 'options'; + +/** + * This is the name of the attribute to pass options to a control + * + * @memberOf Monster.DOM + * @since 1.30.0 + * @type {string} + */ +const ATTRIBUTE_OPTIONS_SELECTOR = ATTRIBUTE_PREFIX + 'options-selector'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.8.0 + */ +const ATTRIBUTE_THEME_PREFIX = ATTRIBUTE_PREFIX + 'theme-'; + +/** + * @memberOf Monster.DOM + * @type {string} + */ +const ATTRIBUTE_THEME_NAME = ATTRIBUTE_THEME_PREFIX + 'name'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.8.0 + */ +const ATTRIBUTE_UPDATER_ATTRIBUTES = ATTRIBUTE_PREFIX + 'attributes'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.27.1 + */ +const ATTRIBUTE_UPDATER_SELECT_THIS = ATTRIBUTE_PREFIX + 'select-this'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.8.0 + */ +const ATTRIBUTE_UPDATER_REPLACE = ATTRIBUTE_PREFIX + 'replace'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.8.0 + */ +const ATTRIBUTE_UPDATER_INSERT = ATTRIBUTE_PREFIX + 'insert'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.8.0 + */ +const ATTRIBUTE_UPDATER_INSERT_REFERENCE = ATTRIBUTE_PREFIX + 'insert-reference'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.8.0 + */ +const ATTRIBUTE_UPDATER_REMOVE = ATTRIBUTE_PREFIX + 'remove'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.9.0 + */ +const ATTRIBUTE_UPDATER_BIND = ATTRIBUTE_PREFIX + 'bind'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.27.0 + */ +const ATTRIBUTE_TEMPLATE_PREFIX = ATTRIBUTE_PREFIX + 'template-prefix'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.14.0 + */ +const ATTRIBUTE_ROLE = ATTRIBUTE_PREFIX + 'role'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.24.0 + */ +const ATTRIBUTE_DISABLED = 'disabled'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.24.0 + */ +const ATTRIBUTE_VALUE = 'value'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.9.0 + */ +const ATTRIBUTE_OBJECTLINK = ATTRIBUTE_PREFIX + 'objectlink'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.24.0 + */ +const ATTRIBUTE_ERRORMESSAGE = ATTRIBUTE_PREFIX + 'error'; + +/** + * @memberOf Monster.DOM + * @type {symbol} + * @since 1.24.0 + */ +const objectUpdaterLinkSymbol = Symbol('monsterUpdater'); + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const TAG_SCRIPT = 'script'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const TAG_STYLE = 'style'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const TAG_LINK = 'link'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ + +const ATTRIBUTE_ID = 'id'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ + +const ATTRIBUTE_CLASS = 'class'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_TITLE = 'title'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_SRC = 'src'; +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_HREF = 'href'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_TYPE = 'type'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_NONCE = 'nonce'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_TRANSLATE = 'translate'; + + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_TABINDEX = 'tabindex'; + + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_SPELLCHECK = 'spellcheck'; + + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_SLOT = 'slot'; + + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_PART = 'part'; + + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_LANG = 'lang'; + + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_ITEMTYPE = 'itemtype'; + + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_ITEMSCOPE = 'itemscope'; + + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_ITEMREF = 'itemref'; + + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_ITEMID = 'itemid'; + + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_ITEMPROP = 'itemprop'; + + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_IS = 'is'; + + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_INPUTMODE = 'inputmode'; + + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_ACCESSKEY = 'accesskey'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_AUTOCAPITALIZE = 'autocapitalize'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_AUTOFOCUS = 'autofocus'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_CONTENTEDITABLE = 'contenteditable'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_DIR = 'dir'; + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_DRAGGABLE = 'draggable'; + + +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_ENTERKEYHINT = 'enterkeyhint'; +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_EXPORTPARTS = 'exportparts'; +/** + * @memberOf Monster.DOM + * @type {string} + * @since 1.25.0 + */ +const ATTRIBUTE_HIDDEN = 'hidden'; +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../constants.mjs"; +import {extend} from "../data/extend.mjs"; +import {Pathfinder} from "../data/pathfinder.mjs"; + +import {parseDataURL} from "../types/dataurl.mjs"; +import {getGlobalObject} from "../types/global.mjs"; +import {isArray, isFunction, isObject, isString} from "../types/is.mjs"; +import {Observer} from "../types/observer.mjs"; +import {ProxyObserver} from "../types/proxyobserver.mjs"; +import {validateFunction, validateInstance, validateObject, validateString} from "../types/validate.mjs"; +import {clone} from "../util/clone.mjs"; +import {addAttributeToken, addToObjectLink, getLinkedObjects, hasObjectLink} from "./attributes.mjs"; +import { + ATTRIBUTE_DISABLED, + ATTRIBUTE_ERRORMESSAGE, + ATTRIBUTE_OPTIONS, + ATTRIBUTE_OPTIONS_SELECTOR, + objectUpdaterLinkSymbol +} from "./constants.mjs"; +import {findDocumentTemplate, Template} from "./template.mjs"; +import {Updater} from "./updater.mjs"; + +export {CustomElement, initMethodSymbol, assembleMethodSymbol, attributeObserverSymbol, registerCustomElement, assignUpdaterToElement} + +/** + * @memberOf Monster.DOM + * @type {symbol} + */ +const initMethodSymbol = Symbol('initMethodSymbol'); + +/** + * @memberOf Monster.DOM + * @type {symbol} + */ +const assembleMethodSymbol = Symbol('assembleMethodSymbol'); + +/** + * this symbol holds the attribute observer callbacks. The key is the attribute name. + * @memberOf Monster.DOM + * @type {symbol} + */ +const attributeObserverSymbol = Symbol('attributeObserver'); + + +/** + * HTMLElement + * @external HTMLElement + * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement + * + * @startuml customelement-sequencediagram.png + * skinparam monochrome true + * skinparam shadowing false + * + * autonumber + * + * Script -> DOM: element = document.createElement('my-element') + * DOM -> CustomElement: constructor() + * CustomElement -> CustomElement: [initMethodSymbol]() + * + * CustomElement --> DOM: Element + * DOM --> Script : element + * + * + * Script -> DOM: document.querySelector('body').append(element) + * + * DOM -> CustomElement : connectedCallback() + * + * note right CustomElement: is only called at\nthe first connection + * CustomElement -> CustomElement : [assembleMethodSymbol]() + * + * ... ... + * + * autonumber + * + * Script -> DOM: document.querySelector('monster-confirm-button').parentNode.removeChild(element) + * DOM -> CustomElement: disconnectedCallback() + * + * + * @enduml + * + * @startuml customelement-class.png + * skinparam monochrome true + * skinparam shadowing false + * HTMLElement <|-- CustomElement + * @enduml + */ + + +/** + * To define a new HTML element we need the power of CustomElement + * + * IMPORTANT: after defining a `CustomElement`, the `registerCustomElement` method must be called + * with the new class name. only then will the tag defined via the `getTag` method be made known to the DOM. + * + * <img src="./images/customelement-class.png"> + * + * You can create the object via the function `document.createElement()`. + * + * ``` + * <script type="module"> + * import {Monster} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source//monster.mjs'; + * document.createElement('monster-') + * </script> + * ``` + * + * ## Interaction + * + * <img src="./images/customelement-sequencediagram.png"> + * + * ## Styling + * + * For optimal display of custom-elements the pseudo-class :defined can be used. + * + * To prevent the custom elements from being displayed and flickering until the control is registered, it is recommended to create a css directive. + * + * In the simplest case, you can simply hide the control. + * + * ``` + * <style> + * + * my-custom-element:not(:defined) { + * display: none; + * } + * + * my-custom-element:defined { + * display: flex; + * } + * + * </style> + * ``` + * + * Alternatively you can also display a loader + * + * ``` + * my-custom-element:not(:defined) { + * display: flex; + * box-shadow: 0 4px 10px 0 rgba(33, 33, 33, 0.15); + * border-radius: 4px; + * height: 200px; + * position: relative; + * overflow: hidden; + * } + * + * my-custom-element:not(:defined)::before { + * content: ''; + * display: block; + * position: absolute; + * left: -150px; + * top: 0; + * height: 100%; + * width: 150px; + * background: linear-gradient(to right, transparent 0%, #E8E8E8 50%, transparent 100%); + * animation: load 1s cubic-bezier(0.4, 0.0, 0.2, 1) infinite; + * } + * + * @keyframes load { + * from { + * left: -150px; + * } + * to { + * left: 100%; + * } + * } + * + * my-custom-element:defined { + * display: flex; + * } + * ``` + * + * @example + * + * // In the example the the user can use his own template by creating a template in the DOM with the ID `my-custom-element`. + * // You can also specify a theme (for example `mytheme`), then it will search for the ID `my-custom-element-mytheme` and + * // if not available for the ID `my-custom-element`. + * + * class MyCustomElement extends CustomElement { + * + * static getTag() { + * return "my-custom-element" + * } + * + * } + * + * // ↦ <my-custom-element></my-custom-element> + * + * @see https://github.com/WICG/webcomponents + * @see https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements + * @since 1.7.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @extends external:HTMLElement + * @summary A base class for HTML5 customcontrols + */ +class CustomElement extends HTMLElement { + + /** + * A new object is created. First the `initOptions` method is called. Here the + * options can be defined in derived classes. Subsequently, the shadowRoot is initialized. + * + * @throws {Error} the options attribute does not contain a valid json definition. + * @since 1.7.0 + */ + constructor() { + super(); + this[internalSymbol] = new ProxyObserver({'options': extend({}, this.defaults)}); + this[attributeObserverSymbol] = {}; + initOptionObserver.call(this); + this[initMethodSymbol](); + } + + /** + * This method determines which attributes are to be monitored by `attributeChangedCallback()`. + * + * @return {string[]} + * @since 1.15.0 + */ + static get observedAttributes() { + return [ATTRIBUTE_OPTIONS, ATTRIBUTE_DISABLED]; + } + + /** + * Derived classes can override and extend this method as follows. + * + * ``` + * get defaults() { + * return Object.assign({}, super.defaults, { + * myValue:true + * }); + * } + * ``` + * + * To set the options via the html tag the attribute data-monster-options must be set. + * As value a JSON object with the desired values must be defined. + * + * Since 1.18.0 the JSON can be specified as a DataURI. + * + * ``` + * new Monster.Types.DataUrl(btoa(JSON.stringify({ + * shadowMode: 'open', + * delegatesFocus: true, + * templates: { + * main: undefined + * } + * })),'application/json',true).toString() + * ``` + * + * The attribute data-monster-options-selector can be used to access a script tag that contains additional configuration. + * + * As value a selector must be specified, which belongs to a script tag and contains the configuration as json. + * + * ``` + * <script id="id-for-this-config" type="application/json"> + * { + * "config-key": "config-value" + * } + * </script> + * ``` + * + * The individual configuration values can be found in the table. + * + * @property {boolean} disabled=false Object The Boolean disabled attribute, when present, makes the element not mutable, focusable, or even submitted with the form. + * @property {string} shadowMode=open `open` Elements of the shadow root are accessible from JavaScript outside the root, for example using. `close` Denies access to the node(s) of a closed shadow root from JavaScript outside it + * @property {Boolean} delegatesFocus=true A boolean that, when set to true, specifies behavior that mitigates custom element issues around focusability. When a non-focusable part of the shadow DOM is clicked, the first focusable part is given focus, and the shadow host is given any available :focus styling. + * @property {Object} templates Templates + * @property {string} templates.main=undefined Main template + * + * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/attachShadow + * @since 1.8.0 + */ + get defaults() { + return { + ATTRIBUTE_DISABLED: this.getAttribute(ATTRIBUTE_DISABLED), + shadowMode: 'open', + delegatesFocus: true, + templates: { + main: undefined + } + }; + } + + /** + * There is no check on the name by this class. the developer is responsible for assigning an appropriate tag. + * if the name is not valid, registerCustomElement() will issue an error + * + * @link https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name + * @return {string} + * @throws {Error} the method getTag must be overwritten by the derived class. + * @since 1.7.0 + */ + static getTag() { + throw new Error("the method getTag must be overwritten by the derived class."); + } + + /** + * At this point a `CSSStyleSheet` object can be returned. If the environment does not + * support a constructor, then an object can also be built using the following detour. + * + * If `undefined` is returned then the shadowRoot does not get a stylesheet. + * + * ``` + * const doc = document.implementation.createHTMLDocument('title'); + * + * let style = doc.createElement("style"); + * style.innerHTML="p{color:red;}"; + * + * // WebKit Hack + * style.appendChild(document.createTextNode("")); + * // Add the <style> element to the page + * doc.head.appendChild(style); + * return doc.styleSheets[0]; + * ; + * ``` + * + * @return {CSSStyleSheet|CSSStyleSheet[]|string|undefined} + */ + static getCSSStyleSheet() { + return undefined; + } + + /** + * attach a new observer + * + * @param {Observer} observer + * @returns {CustomElement} + */ + attachObserver(observer) { + this[internalSymbol].attachObserver(observer) + return this; + } + + /** + * detach a observer + * + * @param {Observer} observer + * @returns {CustomElement} + */ + detachObserver(observer) { + this[internalSymbol].detachObserver(observer) + return this; + } + + /** + * @param {Observer} observer + * @returns {ProxyObserver} + */ + containsObserver(observer) { + return this[internalSymbol].containsObserver(observer) + } + + /** + * nested options can be specified by path `a.b.c` + * + * @param {string} path + * @param {*} defaultValue + * @return {*} + * @since 1.10.0 + */ + getOption(path, defaultValue) { + let value; + + try { + value = new Pathfinder(this[internalSymbol].getRealSubject()['options']).getVia(path); + } catch (e) { + + } + + if (value === undefined) return defaultValue; + return value; + } + + /** + * Set option and inform elements + * + * @param {string} path + * @param {*} value + * @return {CustomElement} + * @since 1.14.0 + */ + setOption(path, value) { + new Pathfinder(this[internalSymbol].getSubject()['options']).setVia(path, value); + return this; + } + + /** + * @since 1.15.0 + * @param {string|object} options + * @return {CustomElement} + */ + setOptions(options) { + + if (isString(options)) { + options = parseOptionsJSON.call(this, options) + } + + const self = this; + extend(self[internalSymbol].getSubject()['options'], self.defaults, options); + + return self; + } + + /** + * Is called once via the constructor + * + * @return {CustomElement} + * @since 1.8.0 + */ + [initMethodSymbol]() { + return this; + } + + /** + * Is called once when the object is included in the DOM for the first time. + * + * @return {CustomElement} + * @since 1.8.0 + */ + [assembleMethodSymbol]() { + + const self = this; + let elements, nodeList; + + const AttributeOptions = getOptionsFromAttributes.call(self); + if (isObject(AttributeOptions) && Object.keys(AttributeOptions).length > 0) { + self.setOptions(AttributeOptions); + } + + const ScriptOptions = getOptionsFromScriptTag.call(self); + if (isObject(ScriptOptions) && Object.keys(ScriptOptions).length > 0) { + self.setOptions(ScriptOptions); + } + + + if (self.getOption('shadowMode', false) !== false) { + try { + initShadowRoot.call(self); + elements = self.shadowRoot.childNodes; + + } catch (e) { + + } + + try { + initCSSStylesheet.call(this); + } catch (e) { + addAttributeToken(self, ATTRIBUTE_ERRORMESSAGE, e.toString()); + } + } + + if (!(elements instanceof NodeList)) { + if (!(elements instanceof NodeList)) { + initHtmlContent.call(this); + elements = this.childNodes; + } + } + + try { + nodeList = new Set([ + ...elements, + ...getSlottedElements.call(self) + ]) + } catch (e) { + nodeList = elements + } + + assignUpdaterToElement.call(self, nodeList, clone(self[internalSymbol].getRealSubject()['options'])); + return self; + } + + /** + * Called every time the element is inserted into the DOM. Useful for running setup code, such as + * fetching resources or rendering. Generally, you should try to delay work until this time. + * + * @return {void} + * @since 1.7.0 + */ + connectedCallback() { + let self = this; + if (!hasObjectLink(self, objectUpdaterLinkSymbol)) { + self[assembleMethodSymbol]() + } + } + + /** + * Called every time the element is removed from the DOM. Useful for running clean up code. + * + * @return {void} + * @since 1.7.0 + */ + disconnectedCallback() { + + } + + /** + * The custom element has been moved into a new document (e.g. someone called document.adoptNode(el)). + * + * @return {void} + * @since 1.7.0 + */ + adoptedCallback() { + + } + + /** + * Called when an observed attribute has been added, removed, updated, or replaced. Also called for initial + * values when an element is created by the parser, or upgraded. Note: only attributes listed in the observedAttributes + * property will receive this callback. + * + * @param {string} attrName + * @param {string} oldVal + * @param {string} newVal + * @return {void} + * @since 1.15.0 + */ + attributeChangedCallback(attrName, oldVal, newVal) { + const self = this; + + const callback = self[attributeObserverSymbol]?.[attrName]; + + if (isFunction(callback)) { + callback.call(self, newVal, oldVal); + } + + } + + /** + * + * @param {Node} node + * @return {boolean} + * @throws {TypeError} value is not an instance of + * @since 1.19.0 + */ + hasNode(node) { + const self = this; + + if (containChildNode.call(self, validateInstance(node, Node))) { + return true; + } + + if (!(self.shadowRoot instanceof ShadowRoot)) { + return false; + } + + return containChildNode.call(self.shadowRoot, node); + + } + +} + +/** + * @private + * @param {String|undefined} query + * @param {String|undefined|null} name name of the slot (if the parameter is undefined, all slots are searched, if the parameter has the value null, all slots without a name are searched. if a string is specified, the slots with this name are searched.) + * @return {*} + * @this CustomElement + * @since 1.23.0 + * @throws {Error} query must be a string + */ +function getSlottedElements(query, name) { + const self = this; + const result = new Set; + + if (!(self.shadowRoot instanceof ShadowRoot)) { + return result; + } + + let selector = 'slot'; + if (name !== undefined) { + if (name === null) { + selector += ':not([name])'; + } else { + selector += '[name=' + validateString(name) + ']'; + } + + } + + const slots = self.shadowRoot.querySelectorAll(selector); + + for (const [, slot] of Object.entries(slots)) { + slot.assignedElements().forEach(function (node) { + + if (!(node instanceof HTMLElement)) return; + + if (isString(query)) { + node.querySelectorAll(query).forEach(function (n) { + result.add(n); + }); + + if (node.matches(query)) { + result.add(node); + } + + } else if (query !== undefined) { + throw new Error('query must be a string') + } else { + result.add(node); + } + }) + } + + return result; +} + +/** + * @this CustomElement + * @private + * @param {Node} node + * @return {boolean} + */ +function containChildNode(node) { + const self = this; + + if (self.contains(node)) { + return true; + } + + for (const [, e] of Object.entries(self.childNodes)) { + if (e.contains(node)) { + return true; + } + + containChildNode.call(e, node); + } + + + return false; +} + +/** + * @since 1.15.0 + * @private + * @this CustomElement + */ +function initOptionObserver() { + const self = this; + + let lastDisabledValue = undefined; + self.attachObserver(new Observer(function () { + const flag = self.getOption('disabled'); + + if (flag === lastDisabledValue) { + return; + } + + lastDisabledValue = flag; + + if (!(self.shadowRoot instanceof ShadowRoot)) { + return; + } + + const query = 'button, command, fieldset, keygen, optgroup, option, select, textarea, input, [data-monster-objectlink]'; + const elements = self.shadowRoot.querySelectorAll(query); + + let nodeList; + try { + nodeList = new Set([ + ...elements, + ...getSlottedElements.call(self, query) + ]) + } catch (e) { + nodeList = elements + } + + for (const element of [...nodeList]) { + if (flag === true) { + element.setAttribute(ATTRIBUTE_DISABLED, ''); + } else { + element.removeAttribute(ATTRIBUTE_DISABLED); + } + } + + })); + + self.attachObserver(new Observer(function () { + + // not initialised + if (!hasObjectLink(self, objectUpdaterLinkSymbol)) { + return; + } + // inform every element + const updaters = getLinkedObjects(self, objectUpdaterLinkSymbol); + + for (const list of updaters) { + for (const updater of list) { + let d = clone(self[internalSymbol].getRealSubject()['options']); + Object.assign(updater.getSubject(), d); + } + } + + })); + + // disabled + self[attributeObserverSymbol][ATTRIBUTE_DISABLED] = () => { + if (self.hasAttribute(ATTRIBUTE_DISABLED)) { + self.setOption(ATTRIBUTE_DISABLED, true); + } else { + self.setOption(ATTRIBUTE_DISABLED, undefined); + } + } + + // data-monster-options + self[attributeObserverSymbol][ATTRIBUTE_OPTIONS] = () => { + const options = getOptionsFromAttributes.call(self); + if (isObject(options) && Object.keys(options).length > 0) { + self.setOptions(options); + } + } + + // data-monster-options-selector + self[attributeObserverSymbol][ATTRIBUTE_OPTIONS_SELECTOR] = () => { + const options = getOptionsFromScriptTag.call(self); + if (isObject(options) && Object.keys(options).length > 0) { + self.setOptions(options); + } + } + + +} + +/** + * @private + * @return {object} + * @throws {TypeError} value is not a object + */ +function getOptionsFromScriptTag() { + const self = this; + + if (!self.hasAttribute(ATTRIBUTE_OPTIONS_SELECTOR)) { + return {}; + } + + const node = document.querySelector(self.getAttribute(ATTRIBUTE_OPTIONS_SELECTOR)); + if (!(node instanceof HTMLScriptElement)) { + addAttributeToken(self, ATTRIBUTE_ERRORMESSAGE, 'the selector ' + ATTRIBUTE_OPTIONS_SELECTOR + ' for options was specified (' + self.getAttribute(ATTRIBUTE_OPTIONS_SELECTOR) + ') but not found.'); + return {}; + } + + let obj = {}; + + try { + obj = parseOptionsJSON.call(this, node.textContent.trim()) + } catch (e) { + addAttributeToken(self, ATTRIBUTE_ERRORMESSAGE, 'when analyzing the configuration from the script tag there was an error. ' + e); + } + + return obj; + +} + +/** + * @private + * @return {object} + */ +function getOptionsFromAttributes() { + const self = this; + + if (this.hasAttribute(ATTRIBUTE_OPTIONS)) { + try { + return parseOptionsJSON.call(self, this.getAttribute(ATTRIBUTE_OPTIONS)) + } catch (e) { + addAttributeToken(self, ATTRIBUTE_ERRORMESSAGE, 'the options attribute ' + ATTRIBUTE_OPTIONS + ' does not contain a valid json definition (actual: ' + this.getAttribute(ATTRIBUTE_OPTIONS) + ').' + e); + } + } + + return {}; +} + +/** + * @private + * @param data + * @return {Object} + */ +function parseOptionsJSON(data) { + + const self = this, obj = {}; + + if (!isString(data)) { + return obj; + } + + // the configuration can be specified as a data url. + try { + let dataUrl = parseDataURL(data); + data = dataUrl.content; + } catch (e) { + + } + + try { + let obj = JSON.parse(data); + return validateObject(obj); + } catch (e) { + throw e; + } + + + return obj; +} + +/** + * @private + * @return {initHtmlContent} + */ +function initHtmlContent() { + + try { + let template = findDocumentTemplate(this.constructor.getTag()); + this.appendChild(template.createDocumentFragment()); + } catch (e) { + + let html = this.getOption('templates.main', ''); + if (isString(html) && html.length > 0) { + this.innerHTML = html; + } + + } + + return this; + +} + +/** + * @private + * @return {CustomElement} + * @memberOf Monster.DOM + * @this CustomElement + * @since 1.16.0 + * @throws {TypeError} value is not an instance of + */ +function initCSSStylesheet() { + const self = this; + + if (!(this.shadowRoot instanceof ShadowRoot)) { + return self; + } + + const styleSheet = this.constructor.getCSSStyleSheet(); + + if (styleSheet instanceof CSSStyleSheet) { + if (styleSheet.cssRules.length > 0) { + this.shadowRoot.adoptedStyleSheets = [styleSheet]; + } + } else if (isArray(styleSheet)) { + const assign = []; + for (let s of styleSheet) { + + if (isString(s)) { + let trimedStyleSheet = s.trim() + if (trimedStyleSheet !== '') { + const style = document.createElement('style') + style.innerHTML = trimedStyleSheet; + self.shadowRoot.prepend(style); + } + continue; + } + + validateInstance(s, CSSStyleSheet); + + if (s.cssRules.length > 0) { + assign.push(s); + } + + } + + if (assign.length > 0) { + this.shadowRoot.adoptedStyleSheets = assign; + } + + } else if (isString(styleSheet)) { + + let trimedStyleSheet = styleSheet.trim() + if (trimedStyleSheet !== '') { + const style = document.createElement('style') + style.innerHTML = styleSheet; + self.shadowRoot.prepend(style); + } + + } + + return self; + +} + +/** + * @private + * @return {CustomElement} + * @throws {Error} html is not set. + * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/attachShadow + * @memberOf Monster.DOM + * @since 1.8.0 + */ +function initShadowRoot() { + + let template, html; + + try { + template = findDocumentTemplate(this.constructor.getTag()); + } catch (e) { + + html = this.getOption('templates.main', ''); + if (!isString(html) || html === undefined || html === "") { + throw new Error("html is not set."); + } + + } + + this.attachShadow({ + mode: this.getOption('shadowMode', 'open'), + delegatesFocus: this.getOption('delegatesFocus', true) + }); + + if (template instanceof Template) { + this.shadowRoot.appendChild(template.createDocumentFragment()); + return this; + } + + this.shadowRoot.innerHTML = html; + return this; +} + +/** + * This method registers a new element. The string returned by `CustomElement.getTag()` is used as the tag. + * + * @param {CustomElement} element + * @return {void} + * @since 1.7.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @throws {DOMException} Failed to execute 'define' on 'CustomElementRegistry': is not a valid custom element name + */ +function registerCustomElement(element) { + validateFunction(element); + getGlobalObject('customElements').define(element.getTag(), element); +} + + +/** + * + * @param element + * @param object + * @return {Promise[]} + * @since 1.23.0 + * @memberOf Monster.DOM + */ +function assignUpdaterToElement(elements, object) { + + const updaters = new Set; + + if (elements instanceof NodeList) { + elements = new Set([ + ...elements + ]) + } + + let result = []; + + elements.forEach((element) => { + if (!(element instanceof HTMLElement)) return; + if ((element instanceof HTMLTemplateElement)) return; + + const u = new Updater(element, object) + updaters.add(u); + + result.push(u.run().then(() => { + return u.enableEventProcessing(); + })); + + }); + + if (updaters.size > 0) { + addToObjectLink(this, objectUpdaterLinkSymbol, updaters); + } + + return result; +} +'use strict'; + +/** + * In this namespace you will find classes and methods for handling the DOM. + * + * @namespace Monster.DOM + * @memberOf Monster + * @author schukai GmbH + */ +const ns = {};'use strict'; + +/** + * @author schukai GmbH + */ + + +import {getGlobalFunction} from "../types/global.mjs"; +import {TokenList} from "../types/tokenlist.mjs"; +import {validateInstance, validateString, validateSymbol} from "../types/validate.mjs"; +import {ATTRIBUTE_OBJECTLINK} from "./constants.mjs"; + +export { + findClosestObjectLink, + addToObjectLink, + removeObjectLink, + hasObjectLink, + getLinkedObjects, + toggleAttributeToken, + addAttributeToken, + removeAttributeToken, + containsAttributeToken, + replaceAttributeToken, + clearAttributeTokens, + findClosestByAttribute, + findClosestByClass +} + +/** + * Get the closest object link of a node + * + * if a node is specified without a object link, a recursive search upwards is performed until the corresponding + * object link is found, or undefined is returned. + * + * ``` + * <script type="module"> + * import {getUpdaterFromNode} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/updater.mjs'; + * console.log(findClosestObjectLink()) + * </script> + * ``` + * + * @param {HTMLElement} element + * @return {HTMLElement|undefined} + * @since 1.10.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @throws {TypeError} value is not an instance of HTMLElement + */ +function findClosestObjectLink(element) { + return findClosestByAttribute(element, ATTRIBUTE_OBJECTLINK); +} + +/** + * Adds a class attribute to an element. + * + * ``` + * <script type="module"> + * import {addToObjectLink} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/attributes.mjs'; + * addToObjectLink(); + * </script> + * ``` + * + * @since 1.9.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @param {HTMLElement} element + * @param {Symbol} symbol + * @param {Object} object + * @return {boolean} + */ +function addToObjectLink(element, symbol, object) { + + validateInstance(element, HTMLElement); + validateSymbol(symbol) + + if (element?.[symbol] === undefined) { + element[symbol] = new Set; + } + + addAttributeToken(element, ATTRIBUTE_OBJECTLINK, symbol.toString()); + element[symbol].add(object); + return element; + +} + +/** + * Removes an object from an element + * + * ``` + * <script type="module"> + * import {removeObjectLink} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/attributes.mjs'; + * removeObjectLink(); + * </script> + * ``` + * + * @since 1.9.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @param {HTMLElement} element + * @param {Symbol} symbol + * @return {boolean} + */ +function removeObjectLink(element, symbol) { + + validateInstance(element, HTMLElement); + validateSymbol(symbol) + + if (element?.[symbol] === undefined) { + return element + } + + removeAttributeToken(element, ATTRIBUTE_OBJECTLINK, symbol.toString()); + delete element[symbol]; + return element; + +} + + +/** + * Checks if an element has an object link + * + * ``` + * <script type="module"> + * import {hasObjectLink} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/attributes.mjs'; + * hasObjectLink(); + * </script> + * ``` + * + * @since 1.9.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @param {HTMLElement} element + * @param {Symbol} symbol + * @return {boolean} + */ +function hasObjectLink(element, symbol) { + + validateInstance(element, HTMLElement); + validateSymbol(symbol) + + if (element?.[symbol] === undefined) { + return false + } + + return containsAttributeToken(element, ATTRIBUTE_OBJECTLINK, symbol.toString()); + +} + +/** + * The ObjectLink can be used to attach objects to HTMLElements. The elements are kept in a set under a unique + * symbol and can be read via an iterator {@see {@link getLinkedObjects}}. + * + * In addition, elements with an objectLink receive the attribute `data-monster-objectlink`. + * + * With the method {@see {@link addToObjectLink}} the objects can be added. + * + * ``` + * <script type="module"> + * import {getLinkedObjects} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/attributes.mjs'; + * getLinkedObjects(); + * </script> + * ``` + * + * @since 1.9.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @param {HTMLElement} element + * @param {Symbol} symbol + * @return {Iterator} + * @throws {Error} there is no object link for symbol + */ +function getLinkedObjects(element, symbol) { + + validateInstance(element, HTMLElement); + validateSymbol(symbol) + + if (element?.[symbol] === undefined) { + throw new Error('there is no object link for ' + symbol.toString()); + } + + return element?.[symbol][Symbol.iterator](); + +} + + +/** + * With this method tokens in an attribute can be switched on or off. For example, classes can be switched on and off in the elements class attribute. + * + * Tokens are always separated by a space. + * + * ``` + * <script type="module"> + * import {toggleAttributeToken} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/attributes.mjs'; + * toggleAttributeToken(); + * </script> + * ``` + * + * @since 1.9.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @param {HTMLElement} element + * @param {string} key + * @param {string} token + * @return {HTMLElement} + */ +function toggleAttributeToken(element, key, token) { + validateInstance(element, HTMLElement); + validateString(token) + validateString(key) + + if (!element.hasAttribute(key)) { + element.setAttribute(key, token); + return element; + } + + element.setAttribute(key, new TokenList(element.getAttribute(key)).toggle(token).toString()); + + return element +} + +/** + * This method can be used to add a token to an attribute. Tokens are always separated by a space. + * + * ``` + * <script type="module"> + * import {addAttributeToken} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/attributes.mjs'; + * addAttributeToken(); + * </script> + * ``` + * + * @since 1.9.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @param {HTMLElement} element + * @param {string} key + * @param {string} token + * @return {HTMLElement} + */ +function addAttributeToken(element, key, token) { + validateInstance(element, HTMLElement); + validateString(token) + validateString(key) + + if (!element.hasAttribute(key)) { + element.setAttribute(key, token); + return element; + } + + element.setAttribute(key, new TokenList(element.getAttribute(key)).add(token).toString()); + + return element +} + +/** + * This function can be used to remove tokens from an attribute. + * + * Tokens are always separated by a space. + * + * ``` + * <script type="module"> + * import {removeAttributeToken} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/attributes.mjs'; + * removeAttributeToken(); + * </script> + * ``` + * + * @since 1.9.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @param {HTMLElement} element + * @param {string} key + * @param {string} token + * @return {HTMLElement} + */ +function removeAttributeToken(element, key, token) { + validateInstance(element, HTMLElement); + validateString(token) + validateString(key) + + if (!element.hasAttribute(key)) { + return element; + } + + element.setAttribute(key, new TokenList(element.getAttribute(key)).remove(token).toString()); + + return element +} + +/** + * This method can be used to determine whether an attribute has a token. + * + * Tokens are always separated by a space. + * + * ``` + * <script type="module"> + * import {containsAttributeToken} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/attributes.mjs'; + * containsAttributeToken(); + * </script> + * ``` + * + * @since 1.9.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @param {HTMLElement} element + * @param {string} key + * @param {string} token + * @return {boolean} + */ +function containsAttributeToken(element, key, token) { + validateInstance(element, HTMLElement); + validateString(token) + validateString(key) + + if (!element.hasAttribute(key)) { + return false; + } + + return new TokenList(element.getAttribute(key)).contains(token); + +} + +/** + * Tokens are always separated by a space. + * + * ``` + * <script type="module"> + * import {replaceAttributeToken} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/attributes.mjs'; + * replaceAttributeToken(); + * </script> + * ``` + * + * @since 1.9.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @param {HTMLElement} element + * @param {string} key + * @param {string} from + * @param {string} to + * @return {HTMLElement} + */ +function replaceAttributeToken(element, key, from, to) { + validateInstance(element, HTMLElement); + validateString(from) + validateString(to) + validateString(key) + + if (!element.hasAttribute(key)) { + return element; + } + + element.setAttribute(key, new TokenList(element.getAttribute(key)).replace(from, to).toString()); + + return element +} + +/** + * Tokens are always separated by a space. + * + * ``` + * <script type="module"> + * import {clearAttributeTokens} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/attributes.mjs'; + * clearAttributeTokens(); + * </script> + * ``` + * + * @since 1.9.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @param {HTMLElement} element + * @param {string} key + * @return {HTMLElement} + */ +function clearAttributeTokens(element, key) { + validateInstance(element, HTMLElement); + validateString(key) + + if (!element.hasAttribute(key)) { + return element; + } + + element.setAttribute(key, ""); + + return element +} + +/** + * This function searches, starting from an `HTMLElemement`, for the next element that has a certain attribute. + * + * ```html + * <div data-my-attribute="2" id="2"> + * <div id="1"></div> + * </div> + * ``` + * + * ```javascript + * // if no value is specified (undefined), then only the attribute is checked. + * findClosestByAttribute(document.getElementById('1'),'data-my-attribute'); // ↦ node with id 2 + * findClosestByAttribute(document.getElementById('2'),'data-my-attribute'); // ↦ node with id 2 + * + * // if a value is specified, for example an empty string, then the name and the value are checked. + * findClosestByAttribute(document.getElementById('1'),'data-my-attribute', ''); // ↦ undefined + * findClosestByAttribute(document.getElementById('1'),'data-my-attribute', '2'); // ↦ node with id 2 + * ``` + * + * ``` + * <script type="module"> + * import {findClosestByAttribute} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/attributes.mjs'; + * findClosestByAttribute(); + * </script> + * ``` + * + * @since 1.14.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @param {HTMLElement} element + * @param {string} key + * @param {string|undefined} value + * @return {HTMLElement|undefined} + * @summary find closest node + */ +function findClosestByAttribute(element, key, value) { + validateInstance(element, getGlobalFunction('HTMLElement')); + + if (element.hasAttribute(key)) { + if (value === undefined) { + return element; + } + + if (element.getAttribute(key) === value) { + return element; + } + + } + + let selector = validateString(key); + if (value !== undefined) selector += "=" + validateString(value); + let result = element.closest('[' + selector + ']'); + if (result instanceof HTMLElement) { + return result; + } + return undefined; +} + +/** + * This function searches, starting from an `HTMLElemement`, for the next element that has a certain attribute. + * + * ```html + * <div class="myclass" id="2"> + * <div id="1"></div> + * </div> + * ``` + * + * ```javascript + * // if no value is specified (undefined), then only the attribute is checked. + * findClosestByClass(document.getElementById('1'),'myclass'); // ↦ node with id 2 + * findClosestByClass(document.getElementById('2'),'myclass'); // ↦ node with id 2 + * ``` + * + * ``` + * <script type="module"> + * import {findClosestByClass} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/attributes.mjs'; + * findClosestByClass(); + * </script> + * ``` + * + * @since 1.27.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @param {HTMLElement} element + * @param {string} className + * @return {HTMLElement|undefined} + * @summary find closest node + */ +function findClosestByClass(element, className) { + validateInstance(element, getGlobalFunction('HTMLElement')); + + if (element?.classList?.contains(validateString(className))) { + return element; + } + + let result = element.closest('.' + className); + if (result instanceof HTMLElement) { + return result; + } + + return undefined; +} +'use strict'; + + +/** + * @author schukai GmbH + */ + +import {isArray, isObject} from "../types/is.mjs"; +import {validateInstance, validateString} from "../types/validate.mjs"; +import {getDocument} from "./util.mjs"; + +export {fireEvent, fireCustomEvent, findTargetElementFromEvent} + +/** + * The function sends an event + * + * ``` + * <script type="module"> + * import {fireEvent} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/events.mjs'; + * fireEvent() + * </script> + * ``` + * + * @param {HTMLElement|HTMLCollection|NodeList} element + * @param {string} type + * @return {void} + * @since 1.10.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @throws {TypeError} value is not an instance of HTMLElement or HTMLCollection + * @summary Construct and send and event + */ +function fireEvent(element, type) { + + const document = getDocument(); + + if (element instanceof HTMLElement) { + + if (type === 'click') { + element.click(); + return; + } + + let event = new Event(validateString(type), { + bubbles: true, + cancelable: true, + }); + + element.dispatchEvent(event); + + } else if (element instanceof HTMLCollection || element instanceof NodeList) { + for (let e of element) { + fireEvent(e, type); + } + } else { + throw new TypeError('value is not an instance of HTMLElement or HTMLCollection') + } + +} + +/** + * You can call the function via the monster namespace `new Monster.DOM.fireCustomEvent()`. + * + * ``` + * <script type="module"> + * import {fireCustomEvent} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/events.mjs'; + * fireCustomEvent() + * </script> + * ``` + * + * @param {HTMLElement|HTMLCollection|NodeList} element + * @param {string} type + * @return {void} + * @since 1.29.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @throws {TypeError} value is not an instance of HTMLElement or HTMLCollection + * @summary Construct and send and event + */ +function fireCustomEvent(element, type, detail) { + + const document = getDocument(); + + if (element instanceof HTMLElement) { + + if (!isObject(detail)) { + detail = {detail}; + } + + let event = new CustomEvent(validateString(type), { + bubbles: true, + cancelable: true, + detail + }); + + element.dispatchEvent(event); + + } else if (element instanceof HTMLCollection || element instanceof NodeList) { + for (let e of element) { + fireCustomEvent(e, type, detail); + } + } else { + throw new TypeError('value is not an instance of HTMLElement or HTMLCollection') + } + +} + +/** + * This function gets the path `Event.composedPath()` from an event and tries to find the next element + * up the tree `element.closest()` with the attribute and value. If no value, or a value that is undefined or null, + * is specified, only the attribute is searched. + * + * ``` + * <script type="module"> + * import {findTargetElementFromEvent} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/events.mjs'; + * findTargetElementFromEvent() + * </script> + * ``` + * + * @since 1.14.0 + * @param {Event} event + * @param {string} attributeName + * @param {string|null|undefined} attributeValue + * @throws {Error} unsupported event + * @memberOf Monster.DOM + * @throws {TypeError} value is not a string + * @throws {TypeError} value is not an instance of HTMLElement + * @summary Help function to find the appropriate control + */ +function findTargetElementFromEvent(event, attributeName, attributeValue) { + validateInstance(event, Event); + + if (typeof event.composedPath !== 'function') { + throw new Error('unsupported event'); + } + + const path = event.composedPath(); + + // closest cannot be used here, because closest is not correct for slotted elements + if (isArray(path)) { + for (let i = 0; i < path.length; i++) { + const o = path[i]; + + if (o instanceof HTMLElement && + o.hasAttribute(attributeName) + && (attributeValue === undefined || o.getAttribute(attributeName) === attributeValue)) { + return o; + } + } + } + + return undefined; + +} +'use strict'; + + +/** + * @author schukai GmbH + */ + + +import {internalSymbol} from "../../constants.mjs"; +import {Base} from "../../types/base.mjs"; +import {getGlobal, getGlobalFunction} from "../../types/global.mjs"; +import {isFunction} from "../../types/is.mjs"; +import {validateInstance, validateString} from "../../types/validate.mjs"; + +export {Factory} + +/** + * A factory for creating worker instances. + * + * ``` + * <script type="module"> + * import {Factory} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/worker/factory.mjs'; + * console.log(new Factory()) + * </script> + * ``` + * + * @since 1.25.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM.Worker + * @summary A small factory to create worker + */ +class Factory extends Base { + + + /** + * + */ + constructor() { + super(); + this[internalSymbol] = { + worker: new WeakMap + } + } + + /** + * Creates a worker from a URL + * + * @param {string|URL} url + * @param {function} messageHandler + * @param {function} errorHandler + * @return {Worker} + */ + createFromURL = function (url, messageHandler, errorHandler) { + + if (url instanceof URL) { + url = url.toString(); + } + + const workerClass = getGlobalFunction('Worker'); + var worker = new workerClass(validateString(url)); + + if (isFunction(messageHandler)) { + worker.onmessage = (event) => { + messageHandler.call(worker, event); + } + } + + if (isFunction(errorHandler)) { + worker.onerror = (event) => { + errorHandler.call(worker, event); + } + } + + return worker; + }; + + /** + * Creates a worker from a script + * + * @param {string} content + * @param {function} messageHandler + * @param {function} errorHandler + * @return {Worker} + * @see https://developer.mozilla.org/de/docs/Web/API/URL/createObjectURL + */ + createFromScript = function (content, messageHandler, errorHandler) { + const blobFunction = new getGlobalFunction('Blob') + const blob = new blobFunction([validateString(content)], {type: 'script/javascript'}); + + const url = getGlobalFunction('URL').createObjectURL(blob); + const worker = this.createFromURL(url, messageHandler, errorHandler); + + this[internalSymbol]['worker'].set(worker, url); + + return worker; + + }; + + /** + * Terminate the worker and call revokeObjectURL if necessary. + * + * @param worker + * @return {Monster.DOM.Worker.Factory} + */ + terminate(worker) { + + const workerClass = getGlobalFunction('Worker'); + validateInstance(worker, workerClass); + + worker.terminate(); + + if (this[internalSymbol]['worker'].has(worker)) { + const url = this[internalSymbol]['worker'].get(worker); + URL.revokeObjectURL(url); + } + + return this; + } + + +} +'use strict'; + +/** + * In this namespace you will find classes and methods for handling the DOM. + * + * @namespace Monster.DOM.Worker + * @memberOf Monster + * @author schukai GmbH + */ +const ns = {};'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from '../types/base.mjs'; +import {getGlobalFunction, getGlobalObject} from '../types/global.mjs'; +import {validateInstance, validateString} from "../types/validate.mjs"; +import {ATTRIBUTE_TEMPLATE_PREFIX} from "./constants.mjs"; +import {getDocumentTheme} from "./theme.mjs"; + +export {Template} + +/** + * The template class provides methods for creating templates. + * + * ``` + * <script type="module"> + * import {Template} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/template.mjs'; + * new Template() + * </script> + * ``` + * + * @since 1.6.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @summary A template class + */ +class Template extends Base { + /** + * + * @param {HTMLTemplateElement} template + * @throws {TypeError} value is not an instance of + * @throws {TypeError} value is not a function + * @throws {Error} the function is not defined + */ + constructor(template) { + super(); + const HTMLTemplateElement = getGlobalFunction('HTMLTemplateElement'); + validateInstance(template, HTMLTemplateElement); + this.template = template; + } + + /** + * + * @returns {HTMLTemplateElement} + */ + getTemplateElement() { + return this.template; + } + + /** + * + * @return {DocumentFragment} + * @throws {TypeError} value is not an instance of + */ + createDocumentFragment() { + return this.template.content.cloneNode(true); + } + +} + +/** + * This method loads a template with the given ID and returns it. + * + * To do this, it first reads the theme of the document and looks for the `data-monster-theme-name` attribute in the HTML tag. + * + * ``` + * <html data-monster-theme-name="my-theme"> + * ``` + * + * If no theme was specified, the default theme is `monster`. + * + * Now it is looked if there is a template with the given ID and theme `id-theme` and if yes it is returned. + * If there is no template a search for a template with the given ID `id` is done. If this is also not found, an error is thrown. + * + * You can call the method via the monster namespace `Monster.DOM.findDocumentTemplate()`. + * + * ``` + * <script type="module"> + * import {findTemplate} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/template.mjs'; + * findDocumentTemplate() + * </script> + * ``` + * + * @example + * + * import { findDocumentTemplate } from "https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/template.mjs"; + * + * const template = document.createElement("template"); + * template.id = "myTemplate"; + * template.innerHTML = "<p>my default template</p>"; + * document.body.appendChild(template); + * + * const themedTemplate = document.createElement("template"); + * themedTemplate.id = "myTemplate-myTheme"; + * themedTemplate.innerHTML = "<p>my themed template</p>"; + * document.body.appendChild(themedTemplate); + * + * // loads the temple and since no theme is set the default template + * const template1 = findDocumentTemplate("myTemplate"); + * console.log(template1.createDocumentFragment()); + * // ↦ '<p>my default template</p>' + * + * // now we set our own theme + * document + * .querySelector("html") + * .setAttribute("data-monster-theme-name", "myTheme"); + * + * // now we don't get the default template, + * // but the template with the theme in the id + * const template2 = findDocumentTemplate("myTemplate"); + * console.log(template2.createDocumentFragment()); + * // ↦ '<p>my themed template</p>' + * + * @param {string} id + * @param {Node} currentNode + * @return {Monster.DOM.Template} + * @since 1.7.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @throws {Error} template id not found. + * @throws {TypeError} value is not a string + */ +export function findDocumentTemplate(id, currentNode) { + validateString(id); + + const document = getGlobalObject('document'); + const HTMLTemplateElement = getGlobalFunction('HTMLTemplateElement'); + const DocumentFragment = getGlobalFunction('DocumentFragment'); + const Document = getGlobalFunction('Document'); + + + let prefixID; + + if (!(currentNode instanceof Document || currentNode instanceof DocumentFragment)) { + + if (currentNode instanceof Node) { + + if (currentNode.hasAttribute(ATTRIBUTE_TEMPLATE_PREFIX)) { + prefixID = currentNode.getAttribute(ATTRIBUTE_TEMPLATE_PREFIX) + } + + currentNode = currentNode.getRootNode(); + + if (!(currentNode instanceof Document || currentNode instanceof DocumentFragment)) { + currentNode = currentNode.ownerDocument; + } + + } + + if (!(currentNode instanceof Document || currentNode instanceof DocumentFragment)) { + currentNode = document; + } + } + + let template; + let theme = getDocumentTheme() + + if (prefixID) { + let themedPrefixID = prefixID + '-' + id + '-' + theme.getName(); + + // current + themedPrefixID + template = currentNode.getElementById(themedPrefixID); + if (template instanceof HTMLTemplateElement) { + return new Template(template); + } + + // document + themedPrefixID + template = document.getElementById(themedPrefixID); + if (template instanceof HTMLTemplateElement) { + return new Template(template); + } + } + + let themedID = id + '-' + theme.getName(); + + // current + themedID + template = currentNode.getElementById(themedID); + if (template instanceof HTMLTemplateElement) { + return new Template(template); + } + + // document + themedID + template = document.getElementById(themedID); + if (template instanceof HTMLTemplateElement) { + return new Template(template); + } + + // current + ID + template = currentNode.getElementById(id); + if (template instanceof HTMLTemplateElement) { + return new Template(template); + } + + // document + ID + template = document.getElementById(id); + if (template instanceof HTMLTemplateElement) { + return new Template(template); + } + + throw new Error("template " + id + " not found.") +} + +'use strict'; + + +/** + * @author schukai GmbH + */ + +import {getDocument, getWindow} from "./util.mjs"; + +export {domReady, windowReady} + +/** + * This variable is a promise that is fulfilled as soon as the dom is available. + * + * The DOMContentLoaded event is fired when the original HTML document is fully loaded and parsed + * without waiting for stylesheets, images, and subframes to finish loading. + * + * ``` + * <script type="module"> + * import {domReady} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/ready.mjs'; + * domReady().then(()=>{ + * // ... + * }) + * </script> + * ``` + * + * @since 1.31.0 + * @memberOf Monster.DOM + * @summary variable to check if dom is ready + * @type {Promise} + * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/DOMContentLoaded_event + * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/readyState + */ +const domReady = new Promise(resolve => { + + const document = getDocument(); + + if (document.readyState === "loading") { + document.addEventListener('DOMContentLoaded', resolve); + } else { + resolve(); + } +}); + + +/** + * This variable is a promise that is fulfilled as soon as the windows is available. + * + * The load event fires when the entire page is loaded, including all dependent resources such as stylesheets, + * assets, and images. Unlike DOMContentLoaded, which fires as soon as the DOM of the page is loaded, + * without waiting for the resources to finish loading. + * + * ``` + * <script type="module"> + * import {windowReady} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/ready.mjs'; + * windowReady().then(()=>{ + * // ... + * }) + * </script> + * ``` + * + * @since 1.31.0 + * @memberOf Monster.DOM + * @summary variable to check if window is ready + * @type {Promise} + * @see https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event + * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/readyState + */ +const windowReady = new Promise(resolve => { + + const document = getDocument(); + const window = getWindow(); + + if (document.readyState === 'complete') { + resolve(); + } else { + window.addEventListener('load', resolve); + } +}); +'use strict'; + + +/** + * @author schukai GmbH + */ + +import {extend} from "../data/extend.mjs"; +import {BaseWithOptions} from "../types/basewithoptions.mjs"; +import {getGlobalObject} from "../types/global.mjs"; +import {isArray} from "../types/is.mjs"; +import {Stack} from "../types/stack.mjs"; +import {validateInstance, validateString} from "../types/validate.mjs"; + +export {FocusManager} + +/** + * @private + * @type {string} + */ +const KEY_DOCUMENT = 'document'; + +/** + * @private + * @type {string} + */ +const KEY_CONTEXT = 'context'; + + +/** + * @private + * @type {Symbol} + */ +const stackSymbol = Symbol('stack'); + + +/** + * With the focusmanager the focus can be stored in a document, recalled and moved. + * + * ``` + * <script type="module"> + * import {FocusManager} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/focusmanager.mjs'; + * new FocusManager() + * </script> + * ``` + * + * @since 1.25.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @throws {Error} unsupported locale + * @summary Handle the focus + */ + class FocusManager extends BaseWithOptions { + + /** + * + * @param {Object|undefined} options + */ + constructor(options) { + super(options); + validateInstance(this.getOption(KEY_DOCUMENT), HTMLDocument); + + this[stackSymbol] = new Stack(); + } + + /** + * @property {HTMLDocument} document the document object into which the node is to be appended + */ + get defaults() { + return extend({}, super.defaults, { + [KEY_DOCUMENT]: getGlobalObject('document'), + [KEY_CONTEXT]: undefined, + }) + } + + /** + * Remembers the current focus on a stack. + * Several focus can be stored. + * + * @return {Monster.DOM.FocusManager} + */ + storeFocus() { + + const active = this.getActive(); + if (active instanceof Node) { + this[stackSymbol].push(active) + } + return this; + } + + /** + * The last focus on the stack is set again + * + * @return {Monster.DOM.FocusManager} + */ + restoreFocus() { + + const last = this[stackSymbol].pop(); + if (last instanceof Node) { + this.focus(last); + } + + return this; + } + + /** + * + * @param {Node} element + * @param {boolean} preventScroll + * @throws {TypeError} value is not an instance of + * @return {Monster.DOM.FocusManager} + */ + focus(element, preventScroll) { + + validateInstance(element, Node) + + element.focus({ + preventScroll: preventScroll ?? false + }) + + return this; + } + + /** + * + * @return {Element} + */ + getActive() { + return this.getOption(KEY_DOCUMENT).activeElement; + } + + /** + * Select all elements that can be focused + * + * @param {string|undefined} query + * @return {array} + * @throws {TypeError} value is not an instance of + */ + getFocusable(query) { + + let contextElement = this.getOption(KEY_CONTEXT); + if (contextElement === undefined) { + contextElement = this.getOption(KEY_DOCUMENT); + } + + validateInstance(contextElement, Node) + + if (query !== undefined) { + validateString(query); + } + + return [...contextElement.querySelectorAll( + 'details, button, input, [tabindex]:not([tabindex="-1"]), select, textarea, a[href], body' + )].filter((element) => { + + if (query !== undefined && !element.matches(query)) { + return false; + } + + if (element.hasAttribute('disabled')) return false; + if (element.getAttribute("aria-hidden") === 'true') return false; + + const rect = element.getBoundingClientRect(); + if(rect.width===0) return false; + if(rect.height===0) return false; + + return true; + }); + } + + /** + * @param {string} query + * @return {Monster.DOM.FocusManager} + */ + focusNext(query) { + + const current = this.getActive(); + const focusable = this.getFocusable(query); + + if (!isArray(focusable) || focusable.length === 0) { + return this; + } + + if (current instanceof Node) { + let index = focusable.indexOf(current); + + if (index > -1) { + this.focus(focusable[index + 1] || focusable[0]); + } else { + this.focus(focusable[0]); + } + } else { + this.focus(focusable[0]) + } + + return this; + } + + /** + * @param {string} query + * @return {Monster.DOM.FocusManager} + */ + focusPrev(query) { + + const current = this.getActive(); + const focusable = this.getFocusable(query); + + if (!isArray(focusable) || focusable.length === 0) { + return this; + } + + if (current instanceof Node) { + let index = focusable.indexOf(current); + + if (index > -1) { + this.focus(focusable[index - 1] || focusable[focusable.length - 1]); + } else { + this.focus(focusable[focusable.length - 1]); + } + } else { + this.focus(focusable[focusable.length - 1]) + } + + return this; + } + + +} + + + + + + + +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from '../types/base.mjs'; +import {getGlobalFunction} from "../types/global.mjs"; +import {ProxyObserver} from "../types/proxyobserver.mjs"; +import {validateInstance, validateString} from "../types/validate.mjs"; + +export {ATTRIBUTEPREFIX,Assembler} + +/** + * attribute prefix + * + * @type {string} + * @memberOf Monster.DOM + */ +const ATTRIBUTEPREFIX = "data-monster-"; + +/** + * Assembler class + * + * ``` + * <script type="module"> + * import {Assembler} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/dom/assembler.mjs'; + * console.log(new Assembler()) + * </script> + * ``` + * + * @since 1.6.0 + * @copyright schukai GmbH + * @memberOf Monster.DOM + * @summary Allows you to build an html fragment + */ +class Assembler extends Base { + + /** + * @param {DocumentFragment} fragment + * @throws {TypeError} value is not an instance of + * @throws {TypeError} value is not a function + * @throws {Error} the function is not defined + */ + constructor(fragment) { + super(); + this.attributePrefix = ATTRIBUTEPREFIX; + validateInstance(fragment, getGlobalFunction('DocumentFragment')); + this.fragment = fragment; + } + + /** + * + * @param {string} prefix + * @returns {Assembler} + * @throws {TypeError} value is not a string + */ + setAttributePrefix(prefix) { + validateString(prefix); + this.attributePrefix = prefix; + return this; + } + + /** + * + * @returns {string} + */ + getAttributePrefix() { + return this.attributePrefix; + } + + /** + * + * @param {ProxyObserver|undefined} data + * @return {DocumentFragment} + * @throws {TypeError} value is not an instance of + */ + createDocumentFragment(data) { + + if (data === undefined) { + data = new ProxyObserver({}); + } + + validateInstance(data, ProxyObserver); + let fragment = this.fragment.cloneNode(true); + return fragment; + } + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from "../types/base.mjs"; +import {isObject, isString} from "../types/is.mjs"; +import {validateInstance, validateInteger, validateObject, validateString} from "../types/validate.mjs"; +import {Locale, parseLocale} from "./locale.mjs"; + +export {Translations} + +/** + * With this class you can manage translations and access the keys. + * + * ``` + * <script type="module"> + * import {Translations} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/i18n/translations.mjs'; + * new Translations() + * </script> + * ``` + * + * @example + * + * import {Translations} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/i18n/translations.mjs'; + * import {parseLocale} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/i18n/locale.mjs'; + * + * const translation = new Translations(parseLocale('en-GB')); + * + * translation.assignTranslations({ + * text1: "click", + * text2: { + * 'one': 'click once', + * 'other': 'click n times' + * } + * }); + * + * console.log(translation.getText('text1')); + * // ↦ click + * + * console.log(translation.getPluralRuleText('text2',1)); + * // -> click once + * console.log(translation.getPluralRuleText('text2',2)); + * // -> click n times + * + * @since 1.13.0 + * @copyright schukai GmbH + * @memberOf Monster.I18n + * @see https://datatracker.ietf.org/doc/html/rfc3066 + */ +class Translations extends Base { + + /** + * + * @param {Locale} locale + */ + constructor(locale) { + super(); + + if (isString(locale)) { + locale = parseLocale(locale); + } + + this.locale = validateInstance(locale, Locale); + this.storage = new Map(); + + } + + + /** + * Fetches a text using the specified key. + * If no suitable key is found, `defaultText` is taken. + * + * @param {string} key + * @param {string|undefined} defaultText + * @return {string} + * @throws {Error} key not found + */ + getText(key, defaultText) { + if (!this.storage.has(key)) { + if (defaultText === undefined) { + throw new Error('key ' + key + ' not found'); + } + + return validateString(defaultText); + } + + let r = this.storage.get(key); + if (isObject(r)) { + return this.getPluralRuleText(key, 'other', defaultText); + } + + return this.storage.get(key); + } + + /** + * A number `count` can be passed to this method. In addition to a number, one of the keywords can also be passed directly. + * "zero", "one", "two", "few", "many" and "other". Remember: not every language has all rules. + * + * The appropriate text for this number is then selected. If no suitable key is found, `defaultText` is taken. + * + * @param {string} key + * @param {integer|count} count + * @param {string|undefined} defaultText + * @return {string} + */ + getPluralRuleText(key, count, defaultText) { + if (!this.storage.has(key)) { + return validateString(defaultText); + } + + let r = validateObject(this.storage.get(key)); + + let keyword; + if (isString(count)) { + keyword = count.toLocaleString(); + } else { + count = validateInteger(count); + if (count === 0) { + // special handlig for zero count + if (r.hasOwnProperty('zero')) { + return validateString(r['zero']); + } + } + + keyword = new Intl.PluralRules(this.locale.toString()).select(validateInteger(count)); + } + + if (r.hasOwnProperty(keyword)) { + return validateString(r[keyword]); + } + + if (r.hasOwnProperty(DEFAULT_KEY)) { + return validateString(r[DEFAULT_KEY]); + } + + return validateString(defaultText); + } + + /** + * Set a text for a key + * + * ``` + * translations.setText("text1": "Make my day!"); + * // plural rules + * translations.setText("text6": { + * "zero": "There are no files on Disk.", + * "one": "There is one file on Disk.", + * "other": "There are files on Disk." + * "default": "There are files on Disk." + * }); + * ``` + * + * @param {string} key + * @param {string|object} text + * @return {Translations} + * @throws {TypeError} value is not a string or object + */ + setText(key, text) { + + if (isString(text) || isObject(text)) { + this.storage.set(validateString(key), text); + return this; + } + + throw new TypeError('value is not a string or object'); + + } + + /** + * This method can be used to transfer overlays from an object. The keys are transferred and the values are entered as text. + * + * The values can either be character strings or, in the case of texts with plural forms, objects. The plural forms must be stored as text via a standard key "zero", "one", "two", "few", "many" and "other". + * + * Additionally, the key default can be specified, which will be used if no other key fits. + * + * In some languages, like for example in german, there is no own more number at the value 0. In these languages the function applies additionally zero. + * + * ``` + * translations.assignTranslations({ + * "text1": "Make my day!", + * "text2": "I'll be back!", + * "text6": { + * "zero": "There are no files on Disk.", + * "one": "There is one file on Disk.", + * "other": "There are files on Disk." + * "default": "There are files on Disk." + * }); + * ``` + * + * @param {object} translations + * @return {Translations} + */ + assignTranslations(translations) { + validateObject(translations); + + for (const [k, v] of Object.entries(translations)) { + this.setText(k, v); + } + + return this; + + } + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from "../types/base.mjs"; +import {validateString} from "../types/validate.mjs"; +import {clone} from "../util/clone.mjs"; + +export {Locale, parseLocale} + +/** + * @memberOf Monster.I18n + * @type {symbol} + */ +const propertiesSymbol = Symbol('properties'); + +/** + * @type {symbol} + * @memberOf Monster.I18n + */ +const localeStringSymbol = Symbol('localeString'); + +/** + * The Locale class is a base class for the language classes. + * + * ``` + * <script type="module"> + * import {Locale} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/i18n/locale.mjs'; + * new Locale() + * </script> + * ``` + * + * RFC + * + * ``` + * A Language-Tag consists of: + * langtag ; generated tag + * -or- private-use ; a private use tag + * + * langtag = (language + * ["-" script] + * ["-" region] + * *("-" variant) + * *("-" extension) + * ["-" privateuse]) + * + * language = "en", "ale", or a registered value + * + * script = "Latn", "Cyrl", "Hant" ISO 15924 codes + * + * region = "US", "CS", "FR" ISO 3166 codes + * "419", "019", or UN M.49 codes + * + * variant = "rozaj", "nedis", "1996", multiple subtags can be used in a tag + * + * extension = single letter followed by additional subtags; more than one extension + * may be used in a language tag + * + * private-use = "x-" followed by additional subtags, as many as are required + * Note that these can start a tag or appear at the end (but not + * in the middle) + * ``` + * + * @since 1.13.0 + * @copyright schukai GmbH + * @memberOf Monster.I18n + * @see https://datatracker.ietf.org/doc/html/rfc3066 + */ +class Locale extends Base { + + /** + * @param {string} language + * @param {string} [region] + * @param {string} [script] + * @param {string} [variants] + * @param {string} [extlang] + * @param {string} [privateUse] + * @throws {Error} unsupported locale + */ + constructor(language, region, script, variants, extlang, privateUse) { + super(); + + this[propertiesSymbol] = { + language: (language === undefined) ? undefined : validateString(language), + script: (script === undefined) ? undefined : validateString(script), + region: (region === undefined) ? undefined : validateString(region), + variants: (variants === undefined) ? undefined : validateString(variants), + extlang: (extlang === undefined) ? undefined : validateString(extlang), + privateUse: (privateUse === undefined) ? undefined : validateString(privateUse), + }; + + let s = []; + if (language !== undefined) s.push(language); + if (script !== undefined) s.push(script); + if (region !== undefined) s.push(region); + if (variants !== undefined) s.push(variants); + if (extlang !== undefined) s.push(extlang); + if (privateUse !== undefined) s.push(privateUse); + + if (s.length === 0) { + throw new Error('unsupported locale'); + } + + this[localeStringSymbol] = s.join('-'); + + } + + /** + * @return {string} + */ + get localeString() { + return this[localeStringSymbol]; + } + + /** + * @return {string|undefined} + */ + get language() { + return this[propertiesSymbol].language; + } + + /** + * @return {string|undefined} + */ + get region() { + return this[propertiesSymbol].region; + } + + /** + * @return {string|undefined} + */ + get script() { + return this[propertiesSymbol].script; + } + + /** + * @return {string|undefined} + */ + get variants() { + return this[propertiesSymbol].variants; + } + + /** + * @return {string|undefined} + */ + get extlang() { + return this[propertiesSymbol].extlang; + } + + /** + * @return {string|undefined} + */ + get privateUse() { + return this[propertiesSymbol].privateValue; + } + + + /** + * @return {string} + */ + toString() { + return "" + this.localeString; + } + + /** + * The structure has the following: language, script, region, variants, extlang, privateUse + * + * @return {Monster.I18n.LocaleMap} + */ + getMap() { + return clone(this[propertiesSymbol]) + } + + +} + +/** + * @typedef {Object} LocaleMap + * @property {string} language + * @property {string} script + * @property {string} region + * @property {string} variants + * @property {string} extlang + * @property {string} privateUse + * @memberOf Monster.I18n + */ + +/** + * Parse local according to rfc4646 standard + * + * Limitations: The regex cannot handle multiple variants or private. + * + * You can call the method via the monster namespace `Monster.I18n.createLocale()`. + * + * ``` + * <script type="module"> + * import {Monster} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source//monster.mjs'; + * new Monster.I18n.createLocale() + * </script> + * ``` + * + * Alternatively, you can also integrate this function individually. + * + * ``` + * <script type="module"> + * import {createLocale} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/i18n/locale.mjs'; + * createLocale() + * </script> + * ``` + * + * RFC + * + * ``` + * The syntax of the language tag in ABNF [RFC4234] is: + * + * Language-Tag = langtag + * / privateuse ; private use tag + * / grandfathered ; grandfathered registrations + * + * langtag = (language + * ["-" script] + * ["-" region] + * *("-" variant) + * *("-" extension) + * ["-" privateuse]) + * + * language = (2*3ALPHA [ extlang ]) ; shortest ISO 639 code + * / 4ALPHA ; reserved for future use + * / 5*8ALPHA ; registered language subtag + * + * extlang = *3("-" 3ALPHA) ; reserved for future use + * + * script = 4ALPHA ; ISO 15924 code + * + * region = 2ALPHA ; ISO 3166 code + * / 3DIGIT ; UN M.49 code + * + * variant = 5*8alphanum ; registered variants + * / (DIGIT 3alphanum) + * + * extension = singleton 1*("-" (2*8alphanum)) + * + * singleton = %x41-57 / %x59-5A / %x61-77 / %x79-7A / DIGIT + * ; "a"-"w" / "y"-"z" / "A"-"W" / "Y"-"Z" / "0"-"9" + * ; Single letters: x/X is reserved for private use + * + * privateuse = ("x"/"X") 1*("-" (1*8alphanum)) + * + * grandfathered = 1*3ALPHA 1*2("-" (2*8alphanum)) + * ; grandfathered registration + * ; Note: i is the only singleton + * ; that starts a grandfathered tag + * + * alphanum = (ALPHA / DIGIT) ; letters and numbers + * + * Figure 1: Language Tag ABNF + * ``` + * + * @param {string} locale + * @returns {Locale} + * @since 1.14.0 + * @copyright schukai GmbH + * @memberOf Monster.I18n + * @throws {TypeError} value is not a string + * @throws {Error} unsupported locale + */ +function parseLocale(locale) { + + locale = validateString(locale).replace(/_/g, "-"); + + let language, region, variants, parts, script, extlang, + regexRegular = "(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang)", + regexIrregular = "(en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)", + regexGrandfathered = "(" + regexIrregular + "|" + regexRegular + ")", + regexPrivateUse = "(x(-[A-Za-z0-9]{1,8})+)", + regexSingleton = "[0-9A-WY-Za-wy-z]", + regexExtension = "(" + regexSingleton + "(-[A-Za-z0-9]{2,8})+)", + regexVariant = "([A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3})", + regexRegion = "([A-Za-z]{2}|[0-9]{3})", + regexScript = "([A-Za-z]{4})", + regexExtlang = "([A-Za-z]{3}(-[A-Za-z]{3}){0,2})", + regexLanguage = "(([A-Za-z]{2,3}(-" + regexExtlang + ")?)|[A-Za-z]{4}|[A-Za-z]{5,8})", + regexLangtag = "(" + regexLanguage + "(-" + regexScript + ")?" + "(-" + regexRegion + ")?" + "(-" + regexVariant + ")*" + "(-" + regexExtension + ")*" + "(-" + regexPrivateUse + ")?" + ")", + regexLanguageTag = "^(" + regexGrandfathered + "|" + regexLangtag + "|" + regexPrivateUse + ")$", + regex = new RegExp(regexLanguageTag), match; + + + if ((match = regex.exec(locale)) !== null) { + if (match.index === regex.lastIndex) { + regex.lastIndex++; + } + } + + if (match === undefined || match === null) { + throw new Error('unsupported locale'); + } + + if (match[6] !== undefined) { + language = match[6]; + + parts = language.split('-'); + if (parts.length > 1) { + language = parts[0]; + extlang = parts[1]; + } + + } + + if (match[14] !== undefined) { + region = match[14]; + } + + if (match[12] !== undefined) { + script = match[12]; + } + + if (match[16] !== undefined) { + variants = match[16]; + } + + return new Locale(language, region, script, variants, extlang); + +} +'use strict'; + +/** + * @author schukai GmbH + */ + + +import {internalSymbol} from "../constants.mjs"; +import {extend} from "../data/extend.mjs"; + +import {Formatter as TextFormatter} from "../text/formatter.mjs"; +import {validateInstance, validateString} from "../types/validate.mjs"; +import {Translations} from "./translations.mjs"; + +export {Formatter} + +/** + * @private + * @type {symbol} + */ +const internalTranslationSymbol = Symbol('internalTranslation') + +/** + * The Formatter extends the Text.Formatter with the possibility to replace the key by a translation. + * + * ``` + * <script type="module"> + * import {Formatter} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/i18n/formatter.mjs'; + * new Formatter() + * </script> + * ``` + * + * @example + * + * import {Formatter} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/i18n/formatter.mjs'; + * import {Translations} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/i18n/translations.mjs'; + * + * const translations = new Translations('en') + * .assignTranslations({ + * thekey: "${animal} has eaten the ${food}!" + * }); + * + * new Formatter({}, translations).format("thekey:animal=dog::food=cake") + * // ↦ dog has eaten the cake! + * + * @since 1.26.0 + * @copyright schukai GmbH + * @memberOf Monster.I18n + */ +class Formatter extends TextFormatter { + + /** + * Default values for the markers are `${` and `}` + * + * @param {object} object + * @throws {TypeError} value is not a object + */ + constructor(object, translation, options) { + super(object, options); + this[internalTranslationSymbol] = validateInstance(translation, Translations); + } + + /** + * @property {object} marker + * @property {array} marker.open=["i18n{","${"] + * @property {array} marker.close=["${"] + * @property {object} parameter + * @property {string} parameter.delimiter="::" + * @property {string} parameter.assignment="=" + * @property {object} callbacks + * @property {function} callbacks.i18n=()=>{} + */ + get defaults() { + const self = this; + return extend({}, super.defaults, { + callbacks: { + i18n: (value) => { + return self[internalTranslationSymbol].getText(validateString(value)); + } + }, + marker: { + open: ['i18n{', '${'], + close: ['}'], + }, + }) + } + + /** + * + * @param {string} text + * @return {string} + * @throws {TypeError} value is not a string + * @throws {Error} too deep nesting + * @throws {Error} key not found + * @throws {Error} the closing marker is missing + */ + format(text) { + validateString(text) + + const openMarker = this[internalSymbol]['marker']['open']?.[0]; + const closeMarker = this[internalSymbol]['marker']['close']?.[0]; + + if (text.indexOf(openMarker) === 0) { + text = text.substring(openMarker.length); + + if (text.indexOf(closeMarker) === text.length - closeMarker.length) { + text = text.substring(0, text.length - closeMarker.length); + } else { + throw new Error("the closing marker is missing") + } + } + + + const parts = validateString(text).split('::') + const translationKey = parts.shift().trim(); // key value delimiter + const parameter = parts.join('::').trim(); + + + let assembledText = openMarker + 'static:' + translationKey + ' | call:i18n'; + if (parameter.length > 0) { + assembledText += '::' + parameter; + } + assembledText += closeMarker; + return super.format(assembledText); + } + + +} +'use strict'; + +/** + * In this namespace you will find classes and methods for handling locale and localized texts. + * + * @namespace Monster.I18n.Providers + * @memberOf Monster + * @author schukai GmbH + */ +const ns = {}; +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../../constants.mjs"; +import {extend} from "../../data/extend.mjs"; +import {Formatter} from "../../text/formatter.mjs"; +import {getGlobalFunction} from "../../types/global.mjs"; +import {isInstance, isString} from "../../types/is.mjs"; +import {validateObject, validateString} from "../../types/validate.mjs"; +import {parseLocale} from "../locale.mjs"; +import {Provider} from "../provider.mjs"; +import {Translations} from "../translations.mjs"; + +export {Fetch} + +/** + * The fetch provider retrieves a JSON file from the given URL and returns a translation object. + * + * ``` + * <script type="module"> + * import {Fetch} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/i18n/providers/fetch.mjs'; + * new Fetch() + * </script> + * ``` + * + * @example <caption>das ist ein test</caption> + * + * import {Fetch} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/i18n/providers/fetch.mjs'; + * + * // fetch from API + * const translation = new Fetch('https://example.com/${language}.json').getTranslation('en-GB'); + * // ↦ https://example.com/en.json + * + * @since 1.13.0 + * @copyright schukai GmbH + * @memberOf Monster.I18n.Providers + * @see {@link https://datatracker.ietf.org/doc/html/rfc3066} + * @tutorial i18n-locale-and-formatter + */ + class Fetch extends Provider { + + /** + * As options the key `fetch` can be passed. This config object is passed to the fetch method as init. + * + * The url may contain placeholders (language, script, region, variants, extlang, privateUse), so you can specify one url for all translations. + * + * ``` + * new Fetch('https://www.example.com/assets/${language}.json') + * ``` + * + * @param {string|URL} url + * @param {Object} options see {@link Monster.I18n.Providers.Fetch#defaults} + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/fetch} + */ + constructor(url, options) { + super(options); + + if (isInstance(url, URL)) { + url = url.toString(); + } + + if (options === undefined) { + options = {}; + } + + validateString(url); + + /** + * @property {string} + */ + this.url = url; + + /** + * @private + * @property {Object} options + */ + this[internalSymbol] = extend({}, super.defaults, this.defaults, validateObject(options)); + + } + + /** + * Defaults + * + * @property {Object} fetch + * @property {String} fetch.method=GET + * @property {String} fetch.mode=cors + * @property {String} fetch.cache=no-cache + * @property {String} fetch.credentials=omit + * @property {String} fetch.redirect=follow + * @property {String} fetch.referrerPolicy=no-referrer + * + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API} + */ + get defaults() { + + return { + fetch: { + method: 'GET', // *GET, POST, PUT, DELETE, etc. + mode: 'cors', // no-cors, *cors, same-origin + cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached + credentials: 'omit', // include, *same-origin, omit + redirect: 'follow', // manual, *follow, error + referrerPolicy: 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url + } + } + + } + + /** + * + * @param {Locale|string} locale + * @return {Promise} + */ + getTranslations(locale) { + + if (isString(locale)) { + locale = parseLocale(locale); + } + + let formatter = new Formatter(locale.getMap()) + + return getGlobalFunction('fetch')(formatter.format(this.url), this.getOption('fetch', {})) + .then((response) => response.json()).then(data => { + return new Translations(locale).assignTranslations(data); + }); + + } + + +} + +'use strict'; + +/** + * In this namespace you will find classes and methods for handling locale and localized texts. + * + * @namespace Monster.I18n + * @memberOf Monster + * @author schukai GmbH + */ +const ns = {};'use strict'; + +/** + * @author schukai GmbH + */ + +import {BaseWithOptions} from "../types/basewithoptions.mjs"; +import {Locale} from "./locale.mjs" +import {Translations} from "./translations.mjs" + +export {Provider} + +/** + * A provider makes a translation object available. + * + * ``` + * <script type="module"> + * import {Provider} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/i18n/provider.mjs'; + * new Provider() + * </script> + * ``` + * + * @since 1.13.0 + * @copyright schukai GmbH + * @memberOf Monster.I18n + * @see {@link https://datatracker.ietf.org/doc/html/rfc3066} + */ +class Provider extends BaseWithOptions { + + /** + * @param {Locale|string} locale + * @return {Promise} + */ + getTranslations(locale) { + return new Promise((resolve, reject) => { + try { + resolve(new Translations(locale)); + } catch (e) { + reject(e); + } + + }); + } + +} +'use strict'; + +/** + * Property-Keys + * @author schukai GmbH + */ + +export { + internalSymbol, + internalStateSymbol +} + + +/** + * @private + * @type {symbol} + * @memberOf Monster + * @since 1.24.0 + */ +const internalSymbol = Symbol('internalData'); + +/** + * @private + * @type {symbol} + * @memberOf Monster + * @since 1.25.0 + */ +const internalStateSymbol = Symbol('state'); + +'use strict'; + + +/** + * @author schukai GmbH + */ + +import {Base} from "./base.mjs"; +import {isString} from "./is.mjs"; +import {validateArray, validateString} from "./validate.mjs"; + +export {MediaType, parseMediaType} + +/** + * @private + * @type {symbol} + */ +const internal = Symbol('internal'); + +/** + * @typedef {Object} Parameter + * @property {string} key + * @property {string} value + * @memberOf Monster.Types + */ + + +/** + * You can create an object via the monster namespace `new Monster.Types.MediaType()`. + * + * ``` + * <script type="module"> + * import {MediaType} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/mediatype.mjs'; + * console.log(new MediaType()) + * </script> + * ``` + * + * @since 1.8.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +class MediaType extends Base { + + /** + * + * @param {String} type + * @param {String} subtype + * @param {Monster.Types.Parameter[]} parameter + */ + constructor(type, subtype, parameter) { + super(); + + this[internal] = { + type: validateString(type).toLowerCase(), + subtype: validateString(subtype).toLowerCase(), + parameter: [] + } + + if (parameter !== undefined) { + this[internal]['parameter'] = validateArray(parameter); + } + + + } + + /** + * @return {String} + */ + get type() { + return this[internal].type; + } + + /** + * @return {String} + */ + get subtype() { + return this[internal].subtype; + } + + /** + * @return {Monster.Types.Parameter[]} + */ + get parameter() { + return this[internal].parameter; + } + + /** + * + * + * @return {Map} + */ + get parameter() { + + const result = new Map + + this[internal]['parameter'].forEach(p => { + + let value = p.value; + + // internally special values are partly stored with quotes, this function removes them. + if (value.startsWith('"') && value.endsWith('"')) { + value = value.substring(1, value.length - 1); + } + + result.set(p.key, value); + }) + + + return result; + } + + /** + * + * @return {string} + */ + toString() { + + let parameter = []; + for (let a of this[internal].parameter) { + parameter.push(a.key + '=' + a.value); + } + + return this[internal].type + '/' + this[internal].subtype + (parameter.length > 0 ? ';' + parameter.join(';') : ''); + } + +} + +/** + * You can call the function via the monster namespace `Monster.Types.parseMediaType()`. + * + * ``` + * <script type="module"> + * import {Monster} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source//monster.mjs'; + * console.log(Monster.Types.parseMediaType()) + * </script> + * ``` + * + * Alternatively, you can also integrate this function individually. + * + * ``` + * <script type="module"> + * import {parseMediaType} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/dataurl.mjs'; + * console.log(parseMediaType()) + * </script> + * ``` + * + * Specification: + * + * ``` + * dataurl := "data:" [ mediatype ] [ ";base64" ] "," data + * mediatype := [ type "/" subtype ] *( ";" parameter ) + * data := *urlchar + * parameter := attribute "=" value + * ``` + * + * @param {String} mediatype + * @return {Monster.Types.MediaType} + * @see https://datatracker.ietf.org/doc/html/rfc2045#section-5.1 + * @throws {TypeError} the mimetype can not be parsed + * @throws {TypeError} blank value is not allowed + * @throws {TypeError} malformed data url + * @memberOf Monster.Types + */ +function parseMediaType(mediatype) { + + const regex = /(?<type>[A-Za-z]+|\*)\/(?<subtype>([a-zA-Z0-9.\+_\-]+)|\*|)(?<parameter>\s*;\s*([a-zA-Z0-9]+)\s*(=\s*("?[A-Za-z0-9_\-]+"?))?)*/g; + const result = regex.exec(validateString(mediatype)); + + const groups = result?.['groups']; + if (groups === undefined) { + throw new TypeError('the mimetype can not be parsed') + } + + const type = groups?.['type']; + const subtype = groups?.['subtype']; + const parameter = groups?.['parameter']; + + if (subtype === "" || type === "") { + throw new TypeError('blank value is not allowed'); + } + + return new MediaType(type, subtype, parseParameter(parameter)); + + +} + +/** + * @private + * @since 1.18.0 + * @param {String} parameter + * @return {Monster.Types.Parameter[]|undefined} + * @memberOf Monster.Types + */ +function parseParameter(parameter) { + + if (!isString(parameter)) { + return undefined; + } + + let result = []; + + parameter.split(';').forEach((entry) => { + + entry = entry.trim(); + if (entry === "") { + return; + } + + const kv = entry.split('=') + + let key = validateString(kv?.[0]).trim(); + let value = validateString(kv?.[1]).trim(); + + // if values are quoted, they remain so internally + result.push({ + key: key, + value: value + }) + + + }) + + return result; + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +export {typeOf} + +/** + * The built-in typeof method is known to have some historical weaknesses. This function tries to provide a better and more accurate result. + * + * ``` + * <script type="module"> + * import {typeOf} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/typeof.mjs'; + * console.log(typeOf()) + * </script> + * ``` + * + * @example + * + * import {typeOf} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/typeof.mjs'; + * + * console.log(typeOf(undefined)); // ↦ undefined + * console.log(typeOf("")); // ↦ string + * console.log(typeOf(5)); // ↦ number + * console.log(typeOf({})); // ↦ object + * console.log(typeOf([])); // ↦ array + * console.log(typeOf(new Map)); // ↦ map + * console.log(typeOf(true)); // ↦ boolean + * + * @param {*} value + * @return {string} + * @since 1.7.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @throws {TypeError} value is not a primitive + */ +function typeOf(value) { + let type = ({}).toString.call(value).match(/\s([a-zA-Z]+)/)[1]; + if ('Object' === type) { + + const name=value.constructor.name; + if (name) { + return name.toLowerCase(); + } + + const results = (/^(class|function)\s+(\w+)/).exec(value.constructor.toString()); + type = (results && results.length > 2) ? results[2] : ''; + } + + return type.toLowerCase(); +} + +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from './base.mjs'; +import {Observer} from "./observer.mjs"; +import {validateInstance} from "./validate.mjs"; + +export {ObserverList} + +/** + * With the help of the ObserverList class, observer can be managed. + * + * ``` + * <script type="module"> + * import {ObserverList} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/observerlist.mjs'; + * console.log(ObserverList()) + * console.log(ObserverList()) + * </script> + * ``` + * + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +class ObserverList extends Base { + + /** + * + */ + constructor() { + super(); + this.observers = []; + } + + /** + * + * @param {Observer} observer + * @return {ObserverList} + * @throws {TypeError} value is not an instance of Observer + */ + attach(observer) { + validateInstance(observer, Observer) + + this.observers.push(observer); + return this; + }; + + /** + * + * @param {Observer} observer + * @return {ObserverList} + * @throws {TypeError} value is not an instance of Observer + */ + detach(observer) { + validateInstance(observer, Observer) + + var i = 0, l = this.observers.length; + for (; i < l; i++) { + if (this.observers[i] === observer) { + this.observers.splice(i, 1); + } + } + + return this; + }; + + /** + * + * @param {Observer} observer + * @return {boolean} + * @throws {TypeError} value is not an instance of Observer + */ + contains(observer) { + validateInstance(observer, Observer) + var i = 0, l = this.observers.length; + for (; i < l; i++) { + if (this.observers[i] === observer) { + return true; + } + } + return false; + }; + + /** + * + * @param subject + * @return {Promise} + */ + notify(subject) { + + let pomises = [] + + let i = 0, l = this.observers.length; + for (; i < l; i++) { + pomises.push(this.observers[i].update(subject)); + } + + return Promise.all(pomises); + }; + +} +'use strict'; + + +/** + * @author schukai GmbH + */ + +import {random} from "../math/random.mjs"; +import {getGlobal} from "./global.mjs"; +import {ID} from "./id.mjs"; + +export {RandomID} + +/** + * @private + * @type {number} + */ +let internalCounter = 0; + +/** + * The `RandomID` class provides a unique ID for an item. + * + * ``` + * <script type="module"> + * import {RandomID} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/randomid.mjs'; + * console.log(new RandomID()) + * </script> + * ``` + * + * @since 1.6.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @summary class to generate random numbers + */ +class RandomID extends ID { + + /** + * create new object + */ + constructor() { + super(); + + internalCounter += 1; + + this.id = getGlobal().btoa(random(1, 10000)) + .replace(/=/g, '') + /** No numbers at the beginning of the ID, because of possible problems with DOM */ + .replace(/^[0-9]+/, 'X') + internalCounter; + } + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../constants.mjs"; +import {random} from "../math/random.mjs"; +import {isObject} from '../types/is.mjs'; +import {Base} from "./base.mjs"; +import {getGlobalObject} from "./global.mjs"; + +export {UUID} + +/** + * The UUID class makes it possible to get a unique UUID for an object. + * + * ``` + * <script type="module"> + * import {Base} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/uuid.mjs'; + * new UUID() + * </script> + * ``` + * + * @since 1.25.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @throws {Error} unsupported + */ + class UUID extends Base { + + /** + * + */ + constructor() { + super(); + + let uuid = createWithCrypto(); + + if (uuid === undefined) { + uuid = createWithRandom(); + } + + + if (uuid === undefined) { + throw new Error('unsupported') + } + + this[internalSymbol] = { + value: uuid + } + + } + + /** + * + * @return {string} + */ + toString() { + return this[internalSymbol]['value']; + } + + +} + +/** + * @private + * @return {string|undefined} + */ +function createWithRandom() { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { + var r = random(0, 65000) * 16 | 0, + v = ((c === 'x') ? r : (r & 0x3 | 0x8)); + return v.toString(16)[0]; + }) +} + + +/** + * @private + * @return {string|undefined} + */ +function createWithCrypto() { + const crypt = getGlobalObject('crypto'); + if (!isObject(crypt)) return; + if (typeof crypt?.['randomUUID']) return; + return crypt.randomUUID(); +} + +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from './base.mjs'; +import {isObject} from './is.mjs'; +import {TokenList} from './tokenlist.mjs'; +import {UniqueQueue} from './uniquequeue.mjs'; + +export {Observer} + +/** + * An observer manages a callback function + * + * ``` + * import {Observer} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/observer.mjs'; + * new Observer() + * ``` + * + * The update method is called with the subject object as this pointer. For this reason the callback should not + * be an arrow function, because it gets the this pointer of its own context. + * + * ``` + * new Observer(()=>{ + * // this is not subject + * }) + * + * new Observer(function() { + * // this is subject + * }) + * ``` + * + * Additional arguments can be passed to the callback. To do this, simply specify them. + * + * ``` + * Observer(function(a, b, c) { + * console.log(a, b, c); // ↦ "a", 2, true + * }, "a", 2, true) + * ``` + * + * The callback function must have as many parameters as arguments are given. + * + * @example + * + * import {Observer} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/observer.mjs'; + * + * const observer = new Observer(function(a, b, c) { + * console.log(this, a, b, c); // ↦ "a", 2, true + * }, "a", 2, true); + * + * observer.update({value:true}).then(()=>{}); + * // ↦ {value: true} "a" 2 true + * + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +class Observer extends Base { + + /** + * + * @param {function} callback + * @param {*} args + */ + constructor(callback, ...args) { + super(); + + if (typeof callback !== 'function') { + throw new Error("observer callback must be a function") + } + + this.callback = callback; + this.arguments = args; + this.tags = new TokenList; + this.queue = new UniqueQueue(); + } + + /** + * + * @param {string} tag + * @returns {Observer} + */ + addTag(tag) { + this.tags.add(tag); + return this; + } + + /** + * + * @param {string} tag + * @returns {Observer} + */ + removeTag(tag) { + this.tags.remove(tag); + return this; + } + + /** + * + * @returns {Array} + */ + getTags() { + return this.tags.entries() + } + + /** + * + * @param {string} tag + * @returns {boolean} + */ + hasTag(tag) { + return this.tags.contains(tag) + } + + /** + * + * @param {object} subject + * @returns {Promise} + */ + update(subject) { + let self = this; + + return new Promise(function (resolve, reject) { + if (!isObject(subject)) { + reject("subject must be an object"); + return; + } + + self.queue.add(subject); + + setTimeout(() => { + + try { + // the queue and the settimeout ensure that an object is not + // informed of the same change more than once. + if (self.queue.isEmpty()) { + resolve(); + return; + } + + let s = self.queue.poll(); + let result = self.callback.apply(s, self.arguments); + + if (isObject(result) && result instanceof Promise) { + result.then(resolve).catch(reject); + return; + } + + resolve(result); + + } catch (e) { + reject(e); + } + }, 0) + + }); + + }; + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {isIterable, isString} from '../types/is.mjs'; +import {validateFunction, validateString} from '../types/validate.mjs'; +import {Base} from './base.mjs'; + +export {TokenList} + +/** + * A tokenlist allows you to manage tokens (individual character strings such as css classes in an attribute string). + * + * The tokenlist offers various functions to manipulate values. For example, you can add, remove or replace a class in a CSS list. + * + * ``` + * <script type="module"> + * import {TokenList} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/tokenlist.mjs'; + * new TokenList("myclass row") + * </script> + * ``` + * + * This class implements the [iteration protocol](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols). + * + * ``` + * typeof new TokenList("myclass row")[Symbol.iterator]; + * // ↦ "function" + * ``` + * + * @since 1.2.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +class TokenList extends Base { + + /** + * + * @param {array|string|iteratable} init + */ + constructor(init) { + super(); + this.tokens = new Set(); + + if (typeof init !== "undefined") { + this.add(init); + } + + } + + /** + * Iterator protocol + * + * @returns {Symbol.iterator} + */ + getIterator() { + return this[Symbol.iterator](); + } + + /** + * Iterator + * + * @returns {{next: ((function(): ({value: *, done: boolean}))|*)}} + */ + [Symbol.iterator]() { + // Use a new index for each iterator. This makes multiple + // iterations over the iterable safe for non-trivial cases, + // such as use of break or nested looping over the same iterable. + let index = 0; + let entries = this.entries() + + return { + next: () => { + if (index < entries.length) { + return {value: entries?.[index++], done: false} + } else { + return {done: true} + } + } + } + } + + /** + * Returns true if it contains token, otherwise false + * + * ``` + * new TokenList("start middle end").contains('start')); // ↦ true + * new TokenList("start middle end").contains('end')); // ↦ true + * new TokenList("start middle end").contains('xyz')); // ↦ false + * new TokenList("start middle end").contains(['end','start','middle'])); // ↦ true + * new TokenList("start middle end").contains(['end','start','xyz'])); // ↦ false + * ``` + * + * @param {array|string|iteratable} value + * @returns {boolean} + */ + contains(value) { + if (isString(value)) { + value = value.trim() + let counter = 0; + value.split(" ").forEach(token => { + if (this.tokens.has(token.trim()) === false) return false; + counter++ + }) + return counter > 0 ? true : false; + } + + if (isIterable(value)) { + let counter = 0; + for (let token of value) { + validateString(token); + if (this.tokens.has(token.trim()) === false) return false; + counter++ + } + return counter > 0 ? true : false; + } + + return false; + } + + /** + * add tokens + * + * ``` + * new TokenList().add("abc xyz").toString(); // ↦ "abc xyz" + * new TokenList().add(["abc","xyz"]).toString(); // ↦ "abc xyz" + * new TokenList().add(undefined); // ↦ add nothing + * ``` + * + * @param {array|string|iteratable} value + * @returns {TokenList} + * @throws {TypeError} unsupported value + */ + add(value) { + if (isString(value)) { + value.split(" ").forEach(token => { + this.tokens.add(token.trim()); + }) + } else if (isIterable(value)) { + for (let token of value) { + validateString(token); + this.tokens.add(token.trim()); + } + } else if (typeof value !== "undefined") { + throw new TypeError("unsupported value"); + } + + return this; + } + + /** + * remove all tokens + * + * @returns {TokenList} + */ + clear() { + this.tokens.clear(); + return this; + } + + /** + * Removes token + * + * ``` + * new TokenList("abc xyz").remove("xyz").toString(); // ↦ "abc" + * new TokenList("abc xyz").remove(["xyz"]).toString(); // ↦ "abc" + * new TokenList("abc xyz").remove(undefined); // ↦ remove nothing + * ``` + * + * @param {array|string|iteratable} value + * @returns {TokenList} + * @throws {TypeError} unsupported value + */ + remove(value) { + if (isString(value)) { + value.split(" ").forEach(token => { + this.tokens.delete(token.trim()); + }) + } else if (isIterable(value)) { + for (let token of value) { + validateString(token); + this.tokens.delete(token.trim()); + } + } else if (typeof value !== "undefined") { + throw new TypeError("unsupported value", "types/tokenlist.mjs"); + } + + return this; + } + + /** + * this method replaces a token with a new token. + * + * if the passed token exists, it is replaced with newToken and TokenList is returned. + * if the token does not exist, newToken is not set and TokenList is returned. + * + * @param {string} token + * @param {string} newToken + * @returns {TokenList} + */ + replace(token, newToken) { + validateString(token); + validateString(newToken); + if (!this.contains(token)) { + return this; + } + + let a = Array.from(this.tokens) + let i = a.indexOf(token); + if (i === -1) return this; + + a.splice(i, 1, newToken); + this.tokens = new Set(); + this.add(a); + + return this; + + + } + + /** + * Removes token from string. If token doesn't exist it's added. + * + * ``` + * new TokenList("abc def ghi").toggle("def xyz").toString(); // ↦ "abc ghi xyz" + * new TokenList("abc def ghi").toggle(["abc","xyz"]).toString(); // ↦ "def ghi xyz" + * new TokenList().toggle(undefined); // ↦ nothing + * ``` + * + * @param {array|string|iteratable} value + * @returns {boolean} + * @throws {TypeError} unsupported value + */ + toggle(value) { + + if (isString(value)) { + value.split(" ").forEach(token => { + toggleValue.call(this, token); + }) + } else if (isIterable(value)) { + for (let token of value) { + toggleValue.call(this, token); + } + } else if (typeof value !== "undefined") { + throw new TypeError("unsupported value", "types/tokenlist.mjs"); + } + + return this; + + } + + /** + * returns an array with all tokens + * + * @returns {array} + */ + entries() { + return Array.from(this.tokens) + } + + /** + * executes the provided function with each value of the set + * + * @param {function} callback + * @returns {TokenList} + */ + forEach(callback) { + validateFunction(callback); + this.tokens.forEach(callback); + return this; + } + + /** + * returns the individual tokens separated by a blank character + * + * @returns {string} + */ + toString() { + return this.entries().join(' '); + } + +} + +/** + * @private + * @param token + * @returns {toggleValue} + * @throws {Error} must be called with TokenList.call + */ +function toggleValue(token) { + if (!(this instanceof TokenList)) throw Error("must be called with TokenList.call") + validateString(token); + token = token.trim(); + if (this.contains(token)) { + this.remove(token); + return this; + } + this.add(token); + return this; +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from './base.mjs'; + +export {Queue} + +/** + * You can create the instance via the monster namespace `new Monster.Types.Queue()`. + * + * ``` + * <script type="module"> + * import {Queue} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/queue.mjs'; + * new Queue() + * </script> + * ``` + * + * @example + * + * import {Queue} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/queue.mjs'; + * + * const queue = new Queue; + * + * queue.add(2); + * queue.add(true); + * queue.add("Hello"); + * queue.add(4.5); + * + * console.log(queue.poll()); + * // ↦ 2 + * console.log(queue.poll()); + * // ↦ true + * console.log(queue.poll()); + * // ↦ "Hello" + * console.log(queue.poll()); + * // ↦ 4.5 + * console.log(queue.poll()); + * // ↦ undefined + * + * + * @since 1.4.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @summary A Queue (Fifo) + */ +class Queue extends Base { + + /** + * + */ + constructor() { + super(); + this.data = []; + } + + + /** + * @return {boolean} + */ + isEmpty() { + return this.data.length === 0; + } + + /** + * Read the element at the front of the queue without removing it. + * + * @return {*} + */ + peek() { + if (this.isEmpty()) { + return undefined; + } + + return this.data[0]; + } + + /** + * Add a new element to the end of the queue. + * + * @param {*} value + * @returns {Queue} + */ + add(value) { + this.data.push(value) + return this; + } + + /** + * remove all entries + * + * @returns {Queue} + */ + clear() { + this.data = []; + return this; + } + + /** + * Remove the element at the front of the queue + * If the queue is empty, return undefined. + * + * @return {*} + */ + poll() { + if (this.isEmpty()) { + return undefined; + } + return this.data.shift(); + } + + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from './base.mjs'; + +export {Stack} + +/** + * You can call the method via the monster namespace `new Monster.Types.Queue()`. + * + * ``` + * <script type="module"> + * import {ID} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/stack.mjs'; + * console.log(new Stack()) + * </script> + * ``` + * + * @since 1.4.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +class Stack extends Base { + + /** + * + */ + constructor() { + super(); + this.data = []; + } + + + /** + * @return {boolean} + */ + isEmpty() { + return this.data.length === 0; + } + + /** + * looks at the object at the top of this stack without removing it from the stack. + * + * @return {*} + */ + peek() { + if (this.isEmpty()) { + return undefined; + } + + return this.data?.[this.data.length - 1]; + } + + /** + * pushes an item onto the top of this stack. + * + * @param {*} value + * @returns {Queue} + */ + push(value) { + this.data.push(value) + return this; + } + + /** + * remove all entries + * + * @returns {Queue} + */ + clear() { + this.data = []; + return this; + } + + /** + * removes the object at the top of this stack and returns + * that object as the value of this function. is the stack empty + * the return value is undefined. + * + * @return {*} + */ + pop() { + if (this.isEmpty()) { + return undefined; + } + return this.data.pop(); + } + + +} +'use strict'; + +/** + * @author schukai GmbH + */ +import {validateString} from "./validate.mjs"; + +export {toBinary, fromBinary} + +/** + * You can call the function via the monster namespace `Monster.Types.toBinary()`. + * + * ``` + * <script type="module"> + * import {toBinary} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/binary.mjs'; + * toBinary() + * </script> + * ``` + * + * @param {String} binary + * @return {String} + * @throws {TypeError} value is not a string + * @memberOf Monster.Types + * @since 1.18.0 + */ +function toBinary(string) { + const codeUnits = new Uint16Array(validateString(string).length); + for (let i = 0; i < codeUnits.length; i++) { + codeUnits[i] = string.charCodeAt(i); + } + + const charCodes = new Uint8Array(codeUnits.buffer); + let result = ''; + + for (let i = 0; i < charCodes.byteLength; i++) { + result += String.fromCharCode(charCodes[i]); + } + + return result; +} + +/** + * You can call the function via the monster namespace `Monster.Types.fromBinary()`. + * + * ``` + * <script type="module"> + * import {fromBinary} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/binary.mjs'; + * fromBinary() + * </script> + * ``` + * + * @param {String} binary + * @return {String} + * @throws {TypeError} value is not a string + * @memberOf Monster.Types + * @since 1.18.0 + */ +function fromBinary(binary) { + const bytes = new Uint8Array(validateString(binary).length); + for (let i = 0; i < bytes.length; i++) { + bytes[i] = binary.charCodeAt(i); + } + const charCodes = new Uint16Array(bytes.buffer); + let result = ''; + for (let i = 0; i < charCodes.length; i++) { + result += String.fromCharCode(charCodes[i]); + } + return result; +} + +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../constants.mjs"; +import {extend} from "../data/extend.mjs"; +import {Pathfinder} from "../data/pathfinder.mjs"; + +import {Base} from "./base.mjs"; +import {validateObject} from "./validate.mjs"; + +export {BaseWithOptions} + +/** + * This is the base class with options from which some monster classes are derived. + * + * This class is actually only used as a base class. + * + * ```html + * <script type="module"> + * import {BaseWithOptions} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/basewithoptions.mjs'; + * new BaseWithOptions() + * </script> + * ``` + * + * Classes that require the possibility of options can be derived directly from this class. + * Derived classes almost always override the `defaul` getter with their own values. + * + * ```javascript + * class My extends BaseWithOptions { + * get defaults() { + * return Object.assign({}, super.defaults, { + * mykey: true + * }); + * } + * } + * ``` + * + * The class was formerly called Object. + * + * @since 1.13.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +class BaseWithOptions extends Base { + + /** + * + * @param {object} options + */ + constructor(options) { + super(); + + if (options === undefined) { + options = {}; + } + + this[internalSymbol] = extend({}, this.defaults, validateObject(options)); + + } + + /** + * This getter provides the options. Derived classes overwrite + * this getter with their own values. It is good karma to always include + * the values from the parent class. + * + * ```javascript + * get defaults() { + * return Object.assign({}, super.defaults, { + * mykey: true + * }); + * } + * + * ``` + * + * @return {object} + */ + get defaults() { + return {} + } + + /** + * nested options can be specified by path `a.b.c` + * + * @param {string} path + * @param {*} defaultValue + * @return {*} + * @since 1.10.0 + */ + getOption(path, defaultValue) { + let value; + + try { + value = new Pathfinder(this[internalSymbol]).getVia(path); + } catch (e) { + + } + + if (value === undefined) return defaultValue; + return value; + } + + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {validateString} from "./validate.mjs"; + +export {escapeString} + +/** + * This function prefixes all special characters that may appear in a regex with a slash. + * + * ``` + * <script type="module"> + * import {escapeString} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/validate.mjs'; + * escapeString() + * </script> + * ``` + * + * @param {string} value + * @return {string} + * @since 1.26.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @throws {TypeError} value is not a string + */ +function escapeString(value) { + return validateString(value) + .replace(/[|\\{}()[\]^$+*?.]/g, '\\$&') + .replace(/-/g, '\\x2d'); +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from './base.mjs'; +import {isArray, isObject, isPrimitive} from "./is.mjs"; +import {Observer} from "./observer.mjs"; +import {ObserverList} from "./observerlist.mjs"; +import {validateObject} from "./validate.mjs"; +import {extend} from "../data/extend.mjs"; + +export {ProxyObserver} + +/** + * An observer manages a callback function + * + * ``` + * <script type="module"> + * import {ProxyObserver} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/proxyobserver.mjs'; + * new ProxyObserver() + * </script> + * ``` + * + * With the ProxyObserver you can attach observer for observation. with each change at the object to be observed an update takes place. + * + * this also applies to nested objects. + * + * @example + * + * import {ProxyObserver} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/proxyobserver.mjs'; + * import {Observer} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/observer.mjs'; + * import {isObject} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/is.mjs'; + * + * const o = new Observer(function () { + * if (isObject(this) && this instanceof ProxyObserver) { + * // do something (this ist ProxyObserver) + * const subject = this.getSubject(); + * console.log(subject); + * } + * }); + * + * let realSubject = { + * a: { + * b: { + * c: true + * }, + * d: 9 + * } + * } + * + * const p = new ProxyObserver(realSubject); + * p.attachObserver(o); + * const s = p.getSubject(); + * s.a.b.c = false; + * + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ + class ProxyObserver extends Base { + + /** + * + * @param {object} object + * @throws {TypeError} value is not a object + */ + constructor(object) { + super(); + + this.realSubject = validateObject(object); + this.subject = new Proxy(object, getHandler.call(this)); + + this.objectMap = new WeakMap(); + this.objectMap.set(this.realSubject, this.subject); + + this.proxyMap = new WeakMap(); + this.proxyMap.set(this.subject, this.realSubject); + + this.observers = new ObserverList; + } + + /** + * get the real object + * + * changes to this object are not noticed by the observers, so you can make a large number of changes and inform the observers later. + * + * @returns {object} + */ + getSubject() { + return this.subject + } + + /** + * @since 1.24.0 + * @param {Object} obj + * @return {Monster.Types.ProxyObserver} + */ + setSubject(obj) { + + let i, k = Object.keys(this.subject); + for (i = 0; i < k.length; i++) { + delete this.subject[k[i]]; + } + + this.subject = extend(this.subject, obj); + return this; + } + + /** + * get the proxied object + * + * @returns {object} + */ + getRealSubject() { + return this.realSubject + } + + /** + * attach a new observer + * + * @param {Observer} observer + * @returns {ProxyObserver} + */ + attachObserver(observer) { + this.observers.attach(observer) + return this; + } + + /** + * detach a observer + * + * @param {Observer} observer + * @returns {ProxyObserver} + */ + detachObserver(observer) { + this.observers.detach(observer) + return this; + } + + /** + * notify all observer + * + * @returns {Promise} + */ + notifyObservers() { + return this.observers.notify(this); + } + + /** + * @param {Observer} observer + * @returns {boolean} + */ + containsObserver(observer) { + return this.observers.contains(observer) + } + +} + +/** + * + * @returns {{defineProperty: (function(*=, *=, *=): *), setPrototypeOf: (function(*, *=): boolean), set: (function(*, *, *, *): boolean), get: ((function(*=, *=, *=): (undefined))|*), deleteProperty: ((function(*, *): (boolean))|*)}} + * @private + * @see {@link https://gitlab.schukai.com/-/snippets/49} + */ +function getHandler() { + + const proxy = this; + + // https://262.ecma-international.org/9.0/#sec-proxy-object-internal-methods-and-internal-slots + const handler = { + + // https://262.ecma-international.org/9.0/#sec-proxy-object-internal-methods-and-internal-slots-get-p-receiver + get: function (target, key, receiver) { + + const value = Reflect.get(target, key, receiver); + + if (typeof key === "symbol") { + return value; + } + + if (isPrimitive(value)) { + return value; + } + + // set value as proxy if object or array + if ((isArray(value) || isObject(value))) { + if (proxy.objectMap.has(value)) { + return proxy.objectMap.get(value); + } else if (proxy.proxyMap.has(value)) { + return value; + } else { + let p = new Proxy(value, handler); + proxy.objectMap.set(value, p); + proxy.proxyMap.set(p, value); + return p; + } + + } + + return value; + + }, + + // https://262.ecma-international.org/9.0/#sec-proxy-object-internal-methods-and-internal-slots-set-p-v-receiver + set: function (target, key, value, receiver) { + + if (proxy.proxyMap.has(value)) { + value = proxy.proxyMap.get(value); + } + + if (proxy.proxyMap.has(target)) { + target = proxy.proxyMap.get(target); + } + + let current = Reflect.get(target, key, receiver); + if (proxy.proxyMap.has(current)) { + current = proxy.proxyMap.get(current); + } + + if (current === value) { + return true; + } + + let result; + let descriptor = Reflect.getOwnPropertyDescriptor(target, key); + + if (descriptor === undefined) { + descriptor = { + writable: true, + enumerable: true, + configurable: true + } + } + + descriptor['value'] = value; + result = Reflect.defineProperty(target, key, descriptor); + + if (typeof key !== "symbol") { + proxy.observers.notify(proxy); + } + + return result; + }, + + + // https://262.ecma-international.org/9.0/#sec-proxy-object-internal-methods-and-internal-slots-delete-p + deleteProperty: function (target, key) { + if (key in target) { + delete target[key]; + + if (typeof key !== "symbol") { + proxy.observers.notify(proxy); + } + + return true; + } + return false; + }, + + // https://262.ecma-international.org/9.0/#sec-proxy-object-internal-methods-and-internal-slots-defineownproperty-p-desc + defineProperty: function (target, key, descriptor) { + + let result = Reflect.defineProperty(target, key, descriptor); + if (typeof key !== "symbol") { + proxy.observers.notify(proxy); + } + return result; + }, + + // https://262.ecma-international.org/9.0/#sec-proxy-object-internal-methods-and-internal-slots-setprototypeof-v + setPrototypeOf: function (target, key) { + let result = Reflect.setPrototypeOf(object1, key); + + if (typeof key !== "symbol") { + proxy.observers.notify(proxy); + } + + return result; + } + + }; + + + return handler; +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {isArray, isInstance} from "./is.mjs"; +import {Node} from "./node.mjs"; +import {validateInstance} from "./validate.mjs"; + +export {NodeList} + +/** + * You can create the instance via the monster namespace `new Monster.Types.NodeList()`. + * + * ``` + * <script type="module"> + * import {NodeList} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/nodelist.mjs'; + * new NodeList() + * </script> + * ``` + * + * @since 1.26.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @summary A NodeList class + */ +class NodeList extends Set { + + /** + * @throws {Error} invalid value type + * @param {NodeList|Node|Array<Node>}values + */ + constructor(values) { + super(); + + const self = this + + if (values === undefined) return; + + if (isArray(values)) { + values.forEach(value => self.add(value)); + } else if (isInstance(values, NodeList)) { + values.forEach(value => self.add(value)); + } else if (isInstance(values, Node)) { + self.add(values); + } else { + throw new Error('invalid value type'); + } + } + + /** + * + * @param {Node} node + * @return {Monster.Types.NodeList} + */ + add(node) { + super.add(validateInstance(node, Node)); + return this; + } + + /** + * @param {Node} node + * @returns {NodeList} + */ + remove(node) { + super.delete(validateInstance(node, Node)); + return this; + } + + /** + * @param {Node} node + * @returns {boolean} + */ + has(node) { + return super.has(validateInstance(node, Node)); + return false; + } + + /** + * @returns {NodeList} + */ + clear() { + super.clear(); + return this; + } + + /** + * @returns {NodeList} + */ + toArray() { + return Array.from(this); + } + + /** + * @returns {NodeList} + */ + toJSON() { + return this.toArray(); + } + + /** + * @returns {NodeList} + */ + toString() { + let parts = []; + + for (const node of this.toArray()) { + parts.push(node.toString()) + } + + return parts.join("\n"); + } + + get length() { + return super.size; + } +} + +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from './base.mjs'; + +export {Version, getMonsterVersion} + +/** + * The version object contains a sematic version number + * + * ``` + * <script type="module"> + * import {Version} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/version.mjs'; + * console.log(new Version('1.2.3')) // ↦ 1.2.3 + * console.log(new Version('1')) // ↦ 1.0.0 + * </script> + * ``` + * + * @example + * + * import {Version} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/version.mjs'; + * + * new Version('1.0.0') // ↦ 1.0.0 + * new Version(1) // ↦ 1.0.0 + * new Version(1, 0, 0) // ↦ 1.0.0 + * new Version('1.2.3', 4, 5) // ↦ 1.4.5 + * + * @since 1.0.0 + * @author schukai GmbH + * @copyright schukai GmbH + * @memberOf Monster.Types + * @summary The version object contains a sematic version number + */ +class Version extends Base { + + /** + * + * @param major + * @param minor + * @param patch + * @throws {Error} major is not a number + * @throws {Error} minor is not a number + * @throws {Error} patch is not a number + */ + constructor(major, minor, patch) { + super(); + + if (typeof major === 'string' && minor === undefined && patch === undefined) { + + let parts = major.toString().split('.'); + major = parseInt(parts[0] || 0); + minor = parseInt(parts[1] || 0); + patch = parseInt(parts[2] || 0); + } + + if (major === undefined) { + throw new Error("major version is undefined"); + } + + if (minor === undefined) { + minor = 0; + } + + if (patch === undefined) { + patch = 0; + } + + this.major = parseInt(major); + this.minor = parseInt(minor); + this.patch = parseInt(patch); + + if (isNaN(this.major)) { + throw new Error("major is not a number"); + } + + if (isNaN(this.minor)) { + throw new Error("minor is not a number"); + } + + if (isNaN(this.patch)) { + throw new Error("patch is not a number"); + } + + } + + /** + * + * @returns {string} + */ + toString() { + return this.major + '.' + this.minor + '.' + this.patch; + } + + /** + * returns 0 if equal, -1 if the object version is less and 1 if greater + * then the compared version + * + * @param {string|Version} version Version to compare + * @returns {number} + */ + compareTo(version) { + + if (version instanceof Version) { + version = version.toString(); + } + + if (typeof version !== 'string') { + throw new Error("type exception"); + } + + if (version === this.toString()) { + return 0; + } + + let a = [this.major, this.minor, this.patch]; + let b = version.split('.'); + let len = Math.max(a.length, b.length); + + for (let i = 0; i < len; i += 1) { + if ((a[i] && !b[i] && parseInt(a[i]) > 0) || (parseInt(a[i]) > parseInt(b[i]))) { + return 1; + } else if ((b[i] && !a[i] && parseInt(b[i]) > 0) || (parseInt(a[i]) < parseInt(b[i]))) { + return -1; + } + } + + return 0; + }; + +} + +let monsterVersion; + +/** + * Version of monster + * + * You can call the method via the monster namespace `Monster.getVersion()`. + * + * ``` + * <script type="module"> + * import {Monster} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source//monster.mjs'; + * console.log(Monster.getVersion()) + * </script> + * ``` + * + * Alternatively, you can also integrate this function individually. + * + * ``` + * <script type="module"> + * import {getMonsterVersion} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/version.mjs'; + * console.log(getMonsterVersion()) + * </script> + * ``` + * + * @returns {Monster.Types.Version} + * @since 1.0.0 + * @copyright schukai GmbH + * @author schukai GmbH + * @memberOf Monster + */ +function getMonsterVersion() { + if (monsterVersion instanceof Version) { + return monsterVersion; + } + /**#@+ dont touch, replaced by make with package.json version */ + monsterVersion = new Version('1.31.0') + /**#@-*/ + + return monsterVersion; + +} +'use strict'; + +/** + * Namespace for types. + * + * @namespace Monster.Types + * @memberOf Monster + * @author schukai GmbH + */ +const ns = {}; + +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from './base.mjs'; +import {isPrimitive} from "./is.mjs"; +import {NodeList} from './nodelist.mjs'; +import {validateInstance} from './validate.mjs'; + +export {Node} + +/** + * @private + * @type {symbol} + */ +const internalValueSymbol = Symbol('internalData'); + +/** + * @private + * @type {symbol} + */ +const treeStructureSymbol = Symbol('treeStructure'); + + +/** + * You can create the instance via the monster namespace `new Monster.Types.Node()`. + * + * ``` + * <script type="module"> + * import {Node} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/node.mjs'; + * new Node() + * </script> + * ``` + * + * @since 1.26.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @summary A Node Class + * @see https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Iteration_protocols + */ +class Node extends Base { + + /** + * @param {*} [value] + */ + constructor(value) { + super(); + this[internalValueSymbol] = value; + + this[treeStructureSymbol] = { + parent: null, + childNodes: new NodeList, + level: 0 + } + + } + + /** + * @property {*} + */ + get value() { + return this[internalValueSymbol]; + } + + /** + * @property {*} + */ + set value(value) { + this[internalValueSymbol] = value; + } + + /** + * @property {Monster.Types.Node|null} + */ + get parent() { + return this[treeStructureSymbol].parent; + } + + /** + * @property {integer} + */ + get level() { + return this[treeStructureSymbol].level; + } + + /** + * + * @property {NodeList} + */ + get childNodes() { + return this[treeStructureSymbol].childNodes; + } + + /** + * + * @property {NodeList} + */ + set childNodes(childNodes) { + this[treeStructureSymbol].childNodes = validateInstance(childNodes, NodeList); + setChildLevelAndParent.call(this, this, 1); + } + + /** + * @return {Monster.Types.Node} + * @param {Node} node + */ + appendChild(node) { + this[treeStructureSymbol].childNodes.add(validateInstance(node, Node)); + node[treeStructureSymbol].parent = this; + + node[treeStructureSymbol].level = this.level + 1; + setChildLevelAndParent.call(this, node, 1); + return this; + } + + /** + * @return {Monster.Types.Node} + * @param {Node} node + */ + removeChild(node) { + this[treeStructureSymbol].childNodes.remove(validateInstance(node, Node)); + node[treeStructureSymbol].parent = null; + + node[treeStructureSymbol].level = 0; + setChildLevelAndParent.call(this, node, -1); + return this; + } + + /** + * + * @return {boolean} + */ + hasChildNodes() { + return this[treeStructureSymbol].childNodes.length > 0; + } + + /** + * @return {Monster.Types.Node} + * @param {Node} node + */ + hasChild(node) { + return this[treeStructureSymbol].childNodes.has(validateInstance(node, Node)); + } + + /** + * @since 1.28.0 + * @return {string} + */ + toString() { + + let parts = []; + if (this[internalValueSymbol]) { + let label = this[internalValueSymbol]; + if (!isPrimitive(label)) label = JSON.stringify(this[internalValueSymbol]) + + parts.push(label); + } + + if (!this.hasChildNodes()) { + return parts.join("\n"); + } + + let count = this.childNodes.length, + counter = 0; + + for (const node of this.childNodes) { + counter++; + const prefix = (count === counter ? '└' : '├').padStart(2 * node.level, ' |'); + parts.push(prefix + node.toString()); + } + + return parts.join("\n"); + } + +} + +/** + * @private + * @param {Node} node + * @param {int} operand + * @return {setChildLevelAndParent} + */ +function setChildLevelAndParent(node, operand) { + const self = this; + + if (node !== this) { + node[treeStructureSymbol].parent = this + } + + node[treeStructureSymbol].childNodes.forEach(function (child) { + child[treeStructureSymbol].parent = node; + child[treeStructureSymbol].level = node[treeStructureSymbol].level + operand; + setChildLevelAndParent.call(self, child, operand); + }); + return this; +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../constants.mjs"; + +import {Base} from './base.mjs'; +import {isInstance} from "./is.mjs"; +import {Node} from "./node.mjs"; +import {NodeList} from "./nodelist.mjs"; +import {validateInstance} from "./validate.mjs"; + +export {NodeRecursiveIterator} + +/** + * @private + * @type {symbol} + */ +const isNodeListSymbol = Symbol('isNodeList'); + +/** + * You can create the instance via the monster namespace `new Monster.Types.NodeRecursiveIterator()`. + * + * ``` + * <script type="module"> + * import {NodeRecursiveIterator} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/noderecursiveiterator.mjs'; + * new NodeRecursiveIterator() + * </script> + * ``` + * + * @example + * import {Node} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/node.mjs'; + * import {NodeRecursiveIterator} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/noderecursiveiterator.mjs'; + * + * const node =new Node('1') + * + * // 1 + * // 2 + * // ├ 2.1 + * // ├ 2.2 + * // └ 2.3 + * // 3 + * // 4 + * // ├ 4.1 + * // └ 4.2 + * node.appendChild( + * (new Node('2')) + * .appendChild(new Node('2.1')) + * .appendChild(new Node('2.2')) + * .appendChild(new Node('2.3'))) + * .appendChild(new Node('3')) + * .appendChild(new Node('4') + * .appendChild(new Node('4.1')) + * .appendChild(new Node('4.2'))); + * + * const iterator = new NodeRecursiveIterator(node); + * + * const result = []; + * for (const n of iterator) { + * result.push(n.value); + * } + * + * // ↦ ['1', '2', '2.1', '2.2', '2.3', '3', '4', '4.1', '4.2'] + * + * @since 1.26.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @summary An iterator to run recursively through a tree of nodes + */ + class NodeRecursiveIterator extends Base { + + /** + * @param {Node} [data] + */ + constructor(node) { + super(); + + this[isNodeListSymbol] = false; + + // iterator is a nodelist + if (isInstance(node, NodeList)) { + let children = node; + node = new Node(); + node.childNodes = children; + this[isNodeListSymbol] = true; + } + + this[internalSymbol] = validateInstance(node, Node); + } + + /** + * @private + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield + */ + [Symbol.iterator] = function* () { + + /** + * The end of the generator function is reached. In this case, execution of the generator + * ends and an IteratorResult is returned to the caller in which the value is undefined and done is true. + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield + */ + if (this[internalSymbol] === undefined) { + return; + } + + // iterator is a nodelist and the main node is only a placeholder + if (this[isNodeListSymbol] !== true) { + yield this[internalSymbol]; + } + + if (this[internalSymbol].hasChildNodes()) { + let childNodes = this[internalSymbol].childNodes; + + for (let node of childNodes) { + yield* new NodeRecursiveIterator(node); + } + } + + return; + } + + /** + * @param {function} callback + * @return {Monster.Types.NodeRecursiveIterator} + */ + forEach(callback) { + for (const node of this) { + callback(node); + } + return this; + } + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {validateFunction, validateObject, validateString} from "./validate.mjs"; + +export {getGlobal, getGlobalObject, getGlobalFunction} + +/** + * @type {objec} + * @private + */ +let globalReference; + +/** + * @private + * @throws {Error} unsupported environment. + */ +(function () { + + if (typeof globalThis === 'object') { + globalReference = globalThis; + return; + } + + if (typeof self !== 'undefined') { + globalReference = self; + return; + } else if (typeof window !== 'undefined') { + globalReference = window; + return; + } + + Object.defineProperty(Object.prototype, '__monster__', { + get: function () { + return this; + }, + configurable: true + }); + + if (typeof __monster__ === 'object') { + __monster__.globalThis = __monster__; + delete Object.prototype.__monster__; + + globalReference = globalThis; + return; + } + + try { + globalReference = Function('return this')(); + } catch (e) { + + } + + throw new Error("unsupported environment.") + + +}()); + +/** + * Return globalThis + * + * If globalThis is not available, it will be polyfilled + * + * @since 1.6.0 + * @memberOf Monster.Types + * @returns {objec} globalThis + */ +function getGlobal() { + return globalReference; +} + +/** + * Return global object or throw Error + * + * You can call the method via the monster namespace `Monster.Types.getGlobalObject()`. + * + * ``` + * <script type="module"> + * import {Monster} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@1.30.0/dist/monster.mjs'; + * Monster.Types.getGlobalObject('document') + * // ↦ { } + * </script> + * ``` + * + * Alternatively, you can also integrate this function individually. + * + * ``` + * <script type="module"> + * import {getGlobalObject} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@1.30.0/dist/modules/types/global.mjs'; + * getGlobalObject('document') + * // ↦ { } + * </script> + * ``` + * + * @since 1.6.0 + * @memberOf Monster.Types + * @param {string} name + * @returns {objec} + * @throws {Error} the object is not defined + * @throws {TypeError} value is not a object + * @throws {TypeError} value is not a string + */ +function getGlobalObject(name) { + validateString(name); + let o = globalReference?.[name]; + if (typeof o === 'undefined') throw new Error('the object ' + name + ' is not defined'); + validateObject(o); + return o; +} + +/** + * Return global function or throw Error + * + * You can call the method via the monster namespace `Monster.Types.getGlobalFunction()`. + * + * ``` + * <script type="module"> + * import {Monster} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@1.30.0/dist/monster.mjs'; + * console.log(Monster.Types.getGlobalFunction('parseInt')) // ↦ f parseInt() { } + * </script> + * ``` + * + * Alternatively, you can also integrate this function individually. + * + * ``` + * <script type="module"> + * import {getGlobalFunction} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@1.30.0/dist/modules/types/global.mjs'; + * console.log(getGlobalFunction('parseInt')) // ↦ f parseInt() { } + * </script> + * ``` + * + * @since 1.6.0 + * @memberOf Monster.Types + * @param {string} name + * @return {objec} + * @throws {TypeError} value is not a function + * @throws {Error} the function is not defined + * @throws {TypeError} value is not a string + */ +function getGlobalFunction(name) { + validateString(name); + let f = globalReference?.[name]; + if (typeof f === 'undefined') throw new Error('the function ' + name + ' is not defined'); + validateFunction(f); + return f; +} + + + + +'use strict'; + + +/** + * @author schukai GmbH + */ + +import {Base} from "./base.mjs"; +import {isString} from "./is.mjs"; +import {MediaType, parseMediaType} from "./mediatype.mjs"; +import {validateBoolean, validateInstance, validateString} from "./validate.mjs"; + +export {DataUrl, parseDataURL} + +/** + * @private + * @type {symbol} + */ +const internal = Symbol('internal'); + + +/** + * You can create an object via the monster namespace `new Monster.Types.DataUrl()`. + * + * ``` + * <script type="module"> + * import {DataUrl} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/dataurl.mjs'; + * new DataUrl() + * </script> + * ``` + * + * @since 1.8.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs + * @see https://datatracker.ietf.org/doc/html/rfc2397 + */ +class DataUrl extends Base { + + /** + * + * @param {String} content + * @param {String|Monster.Types.MediaType} mediatype + * @param {boolean} base64=true + */ + constructor(content, mediatype, base64) { + super(); + + if (isString(mediatype)) { + mediatype = parseMediaType(mediatype); + } + + this[internal] = { + content: validateString(content), + mediatype: validateInstance(mediatype, MediaType), + base64: validateBoolean(base64 === undefined ? true : base64) + } + + + } + + get content() { + return this[internal].base64 ? atob(this[internal].content) : this[internal].content; + } + + get mediatype() { + return this[internal].mediatype; + } + + + /** + * + * @return {string} + * @see https://datatracker.ietf.org/doc/html/rfc2397 + */ + toString() { + + let content = this[internal].content; + + if (this[internal].base64 === true) { + content = ';base64,' + content; + } else { + content = ',' + encodeURIComponent(content); + } + + return 'data:' + this[internal].mediatype.toString() + content; + } + +} + +/** + * You can call the function via the monster namespace `Monster.Types.parseDataURL()`. + * + * ``` + * <script type="module"> + * import {parseDataURL} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/dataurl.mjs'; + * parseDataURL(...) + * </script> + * ``` + * + * Specification: + * + * ``` + * dataurl := "data:" [ mediatype ] [ ";base64" ] "," data + * mediatype := [ type "/" subtype ] *( ";" parameter ) + * data := *urlchar + * parameter := attribute "=" value + * ``` + * + * @param {String} dataurl + * @return {Monster.Types.DataUrl} + * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs + * @see https://datatracker.ietf.org/doc/html/rfc2397 + * @throws {TypeError} incorrect or missing data protocol + * @throws {TypeError} malformed data url + * @memberOf Monster.Types + */ +function parseDataURL(dataurl) { + + validateString(dataurl); + + dataurl = dataurl.trim(); + + if (dataurl.substring(0, 5) !== 'data:') { + throw new TypeError('incorrect or missing data protocol') + } + + dataurl = dataurl.substring(5); + + let p = dataurl.indexOf(','); + if (p === -1) { + throw new TypeError('malformed data url') + } + + let content = dataurl.substring(p + 1); + let mediatypeAndBase64 = dataurl.substring(0, p).trim(); + let mediatype = 'text/plain;charset=US-ASCII'; + let base64Flag = false; + + if (mediatypeAndBase64 !== "") { + mediatype = mediatypeAndBase64; + if (mediatypeAndBase64.endsWith('base64')) { + let i = mediatypeAndBase64.lastIndexOf(';'); + mediatype = mediatypeAndBase64.substring(0, i); + base64Flag = true; + } else { + content = decodeURIComponent(content); + } + + mediatype = parseMediaType(mediatype); + } else { + content = decodeURIComponent(content); + } + + return new DataUrl(content, mediatype, base64Flag); + + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import { + isArray, + isBoolean, + isFunction, + isInstance, + isInteger, + isIterable, + isObject, + isPrimitive, + isString, + isSymbol +} from './is.mjs'; + +export { + validateIterable, + validatePrimitive, + validateBoolean, + validateString, + validateObject, + validateInstance, + validateArray, + validateSymbol, + validateFunction, + validateInteger +} + +/** + * This method checks if the type matches the primitive type. this function is identical to isPrimitive() except that a TypeError is thrown. + * + * ``` + * <script type="module"> + * import {validateIterable} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/validate.mjs'; + * console.log(validateIterable('2')) // ↦ TypeError + * console.log(validateIterable([])) // ↦ value + * </script> + * ``` + * + * @param {*} value + * @return {*} + * @since 1.2.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @throws {TypeError} value is not a primitive + * @see {@link isPrimitive} + * @see {@link Monster.Types.isPrimitive} + * @see {@link Monster.Types#isPrimitive} + */ +function validateIterable(value) { + if (!isIterable(value)) { + throw new TypeError('value is not iterable') + } + return value +} + +/** + * This method checks if the type matches the primitive type. this function is identical to isPrimitive() except that a TypeError is thrown. + * + * ``` + * <script type="module"> + * import {validatePrimitive} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/validate.mjs'; + * console.log(validatePrimitive('2')) // ↦ value + * console.log(validatePrimitive([])) // ↦ TypeError + * </script> + * ``` + * + * @param {*} value + * @return {*} + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @throws {TypeError} value is not a primitive + * @see {@link isPrimitive} + * @see {@link Monster.Types.isPrimitive} + * @see {@link Monster.Types#isPrimitive} + */ +function validatePrimitive(value) { + if (!isPrimitive(value)) { + throw new TypeError('value is not a primitive') + } + return value +} + +/** + * This method checks if the type matches the boolean type. this function is identical to isBoolean() except that a TypeError is thrown. + * + * ``` + * <script type="module"> + * import {validateBoolean} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/validate.mjs'; + * console.log(validateBoolean(false)) // ↦ value + * console.log(validateBoolean('2')) // ↦ TypeError + * console.log(validateBoolean([])) // ↦ TypeError + * </script> + * ``` + * + * @param {*} value + * @return {*} + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + + * @throws {TypeError} value is not primitive + */ +function validateBoolean(value) { + if (!isBoolean(value)) { + throw new TypeError('value is not a boolean') + } + return value +} + +/** + * This method checks if the type matches the string type. this function is identical to isString() except that a TypeError is thrown. + * + * ``` + * <script type="module"> + * import {validateString} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/validate.mjs'; + * console.log(validateString('2')) // ↦ value + * console.log(validateString([])) // ↦ TypeError + * </script> + * ``` + * + * @param {*} value + * @return {*} + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @throws {TypeError} value is not a string + */ +function validateString(value) { + if (!isString(value)) { + throw new TypeError('value is not a string') + } + return value +} + + +/** + * This method checks if the type matches the object type. this function is identical to isObject() except that a TypeError is thrown. + * + * ``` + * <script type="module"> + * import {validateObject} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/validate.mjs'; + * console.log(validateObject({})) // ↦ value + * console.log(validateObject('2')) // ↦ TypeError + * console.log(validateObject([])) // ↦ TypeError + * </script> + * ``` + * + * @param {*} value + * @return {*} + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @throws {TypeError} value is not a object + */ +function validateObject(value) { + if (!isObject(value)) { + throw new TypeError('value is not a object') + } + return value +} + +/** + * This method checks if the type matches the object instance. + * + * ``` + * <script type="module"> + * import {validateInstance} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/validate.mjs'; + * console.log(validateInstance({}, Object)) // ↦ value + * console.log(validateInstance('2', Object)) // ↦ TypeError + * console.log(validateInstance([], Object)) // ↦ TypeError + * </script> + * ``` + * + * @param {*} value + * @return {*} + * @since 1.5.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @throws {TypeError} value is not an instance of + */ +function validateInstance(value, instance) { + if (!isInstance(value, instance)) { + let n = ""; + if (isObject(instance) || isFunction(instance)) { + n = instance?.['name'] + } + + if (n) { + n = " " + n; + } + + throw new TypeError('value is not an instance of' + n) + } + return value +} + +/** + * This method checks if the type matches the array type. this function is identical to isArray() except that a TypeError is thrown. + * + * ``` + * <script type="module"> + * import {validateArray} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/validate.mjs'; + * console.log(validateArray('2')) // ↦ TypeError + * console.log(validateArray([])) // ↦ value + * </script> + * ``` + * + * @param {*} value + * @return {*} + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @throws {TypeError} value is not an array + */ +function validateArray(value) { + if (!isArray(value)) { + throw new TypeError('value is not an array') + } + return value +} + +/** + * This method checks if the type matches the symbol type. this function is identical to isSymbol() except that a TypeError is thrown. + * + * ``` + * <script type="module"> + * import {validateSymbol} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/validate.mjs'; + * console.log(validateSymbol('2')) // ↦ TypeError + * console.log(validateSymbol()) // ↦ value + * </script> + * ``` + * + * @param {*} value + * @return {*} + * @since 1.9.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @throws {TypeError} value is not an symbol + */ +function validateSymbol(value) { + if (!isSymbol(value)) { + throw new TypeError('value is not an symbol') + } + return value +} + +/** + * This method checks if the type matches the function type. this function is identical to isFunction() except that a TypeError is thrown. + * + * ``` + * <script type="module"> + * import {validateFunction} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/validate.mjs'; + * console.log(validateFunction(()=>{})) // ↦ value + * console.log(validateFunction('2')) // ↦ TypeError + * console.log(validateFunction([])) // ↦ TypeError + * </script> + * ``` + * + * @param {*} value + * @return {*} + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @throws {TypeError} value is not a function + */ +function validateFunction(value) { + if (!isFunction(value)) { + throw new TypeError('value is not a function') + } + return value +} + +/** + * This method checks if the type is an integer. this function is identical to isInteger() except that a TypeError is thrown. + * + * ``` + * <script type="module"> + * import {validateFunction} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/validate.mjs'; + * console.log(validateInteger(true)) // ↦ TypeError + * console.log(validateInteger('2')) // ↦ TypeError + * console.log(validateInteger(2)) // ↦ value + * </script> + * ``` + * + * @param {*} value + * @return {*} + * @since 1.4.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @throws {TypeError} value is not an integer + */ +function validateInteger(value) { + if (!isInteger(value)) { + throw new TypeError('value is not an integer') + } + return value +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Queue} from "./queue.mjs"; +import {validateObject} from "./validate.mjs"; + +export {UniqueQueue} + +/** + * A UniqueQueue is a queue that contains items only once. + * + * ``` + * <script type="module"> + * import {UniqueQueue} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/uniquequeue.mjs'; + * new UniqueQueue() + * </script> + * ``` + * + * @since 1.4.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @summary A queue for unique values + */ + class UniqueQueue extends Queue { + + /** + * + */ + constructor() { + super(); + this.unique = new WeakSet(); + } + + /** + * Add a new element to the end of the queue. + * + * @param {object} value + * @returns {Queue} + * @throws {TypeError} value is not a object + */ + add(value) { + + validateObject(value); + + if (!this.unique.has(value)) { + this.unique.add(value); + super.add(value); + } + + return this; + } + + /** + * remove all entries + * + * @returns {Queue} + */ + clear() { + super.clear(); + this.unique = new WeakSet; + return this; + } + + /** + * Remove the element at the front of the queue + * If the queue is empty, return undefined. + * + * @return {object} + */ + poll() { + + if (this.isEmpty()) { + return undefined; + } + let value = this.data.shift(); + this.unique.delete(value); + return value; + } + + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +export {Base} + +/** + * This is the base class from which all monster classes are derived. + * + * ``` + * <script type="module"> + * import {Base} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/base.mjs'; + * new Base() + * </script> + * ``` + * + * The class was formerly called Object. + * + * @since 1.5.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ + class Base extends Object { + + /** + * + * @returns {string} + */ + toString() { + return JSON.stringify(this); + }; + + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +export {isIterable, isPrimitive, isSymbol, isBoolean, isString, isObject, isInstance, isArray, isFunction, isInteger} + + +/** + * With this function you can check if a value is iterable. + * + * This method is used in the library to have consistent names. + * + * You can call the method via the monster namespace `Monster.Types.isPrimitive()`. + * + * ``` + * <script type="module"> + * import {isIterable} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/is.mjs'; + * isIterable(null) // ↦ false + * isIterable('hello') // ↦ true + * isIterable([]) // ↦ true + * </script> + * ``` + * + * @param {*} value + * @returns {boolean} + * @since 1.2.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +function isIterable(value) { + if (value === undefined) return false; + if (value === null) return false; + return typeof value?.[Symbol.iterator] === 'function'; +} + + +/** + * Checks whether the value passed is a primitive (string, number, boolean, NaN, undefined, null or symbol) + * + * This method is used in the library to have consistent names. + * + * ``` + * <script type="module"> + * import {isPrimitive} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/is.mjs'; + * isPrimitive('2')) // ↦ true + * isPrimitive([])) // ↦ false + * </script> + * ``` + * + * @param {*} value + * @returns {boolean} + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +function isPrimitive(value) { + var type; + + if (value === undefined || value === null) { + return true; + } + + type = typeof value; + + if (type === 'string' || type === 'number' || type === 'boolean' || type === 'symbol') { + return true; + } + + return false; +} + +/** + * Checks whether the value passed is a symbol + * + * This method is used in the library to have consistent names. + * + * ``` + * <script type="module"> + * import {isSymbol} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/is.mjs'; + * isSymbol(Symbol('a'))) // ↦ true + * isSymbol([]) // ↦ false + * </script> + * ``` + * + * @param {*} value + * @returns {boolean} + * @since 1.9.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +function isSymbol(value) { + return ('symbol' === typeof value) ? true : false; +} + +/** + * Checks whether the value passed is a boolean. + * + * This method is used in the library to have consistent names. + * + * ``` + * <script type="module"> + * import {isBoolean} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/is.mjs'; + * isBoolean('2')) // ↦ false + * isBoolean([])) // ↦ false + * isBoolean(2>4)) // ↦ true + * </script> + * ``` + * + * @param {*} value + * @returns {boolean} + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +function isBoolean(value) { + + if (value === true || value === false) { + return true; + } + + return false; +} + +/** + * Checks whether the value passed is a string + * + * This method is used in the library to have consistent names. + * + * ``` + * <script type="module"> + * import {isString} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/is.mjs'; + * isString('2')) // ↦ true + * isString([])) // ↦ false + * </script> + * ``` + * + * @param {*} value + * @returns {boolean} + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +function isString(value) { + if (value === undefined || typeof value !== 'string') { + return false; + } + return true; +} + +/** + * Checks whether the value passed is a object + * + * This method is used in the library to have consistent names. + * + * ``` + * <script type="module"> + * import {isObject} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/is.mjs'; + * isObject('2')) // ↦ false + * isObject([])) // ↦ false + * </script> + * ``` + * + * @param {*} value + * @returns {boolean} + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +function isObject(value) { + + if (isArray(value)) return false; + if (isPrimitive(value)) return false; + + if (typeof value === 'object') { + return true; + } + + return false; +} + +/** + * Checks whether the value passed is a object and instance of instance. + * + * This method is used in the library to have consistent names. + * + * ``` + * <script type="module"> + * import {isInstance} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/is.mjs'; + * isInstance('2')) // ↦ false + * isInstance([])) // ↦ false + * </script> + * ``` + * + * @param {*} value + * @param {*} instance + * @returns {boolean} + * @since 1.5.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +function isInstance(value, instance) { + if (!isObject(value)) return false; + if (!isFunction(instance)) return false; + if (!instance.hasOwnProperty('prototype')) return false; + return (value instanceof instance) ? true : false; +} + +/** + * Checks whether the value passed is a array + * + * This method is used in the library to have consistent names. + * + * ``` + * <script type="module"> + * import {isArray} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/is.mjs'; + * isArray('2')) // ↦ false + * isArray([])) // ↦ true + * </script> + * ``` + * + * @param {*} value + * @returns {boolean} + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @see https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray + */ +function isArray(value) { + return Array.isArray(value); +} + +/** + * Checks whether the value passed is a function + * + * This method is used in the library to have consistent names. + * + * ``` + * <script type="module"> + * import {isFunction} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/is.mjs'; + * isFunction(()=>{}) // ↦ true + * isFunction('2')) // ↦ false + * isFunction([])) // ↦ false + * </script> + * ``` + * + * @param {*} value + * @returns {boolean} + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +function isFunction(value) { + if (isArray(value)) return false; + if (isPrimitive(value)) return false; + + if (typeof value === 'function') { + return true; + } + + return false; + +} + +/** + * Checks whether the value passed is an integer. + * + * This method is used in the library to have consistent names. + * + * ``` + * <script type="module"> + * import {isInteger} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/is.mjs'; + * isInteger(()=>{}) // ↦ true + * isInteger('2')) // ↦ false + * isInteger(2)) // ↦ true + * </script> + * ``` + * + * @param {*} value + * @returns {boolean} + * @since 1.4.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + */ +function isInteger(value) { + return Number.isInteger(value); +} + +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from './base.mjs'; +import {validateString} from "./validate.mjs"; + +export {ID} + +/** + * @private + * @type {Map<string, integer>} + */ +let internalCounter = new Map; + +/** + * With the id class, sequences of ids can be created. for this purpose, an internal counter is incremented for each prefix. + * thus, the first id with the prefix `myid` will be `myid1` and the second id `myid2`. + * The ids are the same for every call, for example on a web page. + * + * So the ids can also be used for navigation. you just have to take care that the order stays the same. + * + * ``` + * <script type="module"> + * import {ID} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/types/id.mjs'; + * console.log(new ID()) + * </script> + * ``` + * + * As of version 1.6.0 there is the new RandomID. this ID class is continuous from now on. + * + * @since 1.0.0 + * @copyright schukai GmbH + * @memberOf Monster.Types + * @summary Automatic generation of ids + */ + class ID extends Base { + + /** + * create new id with prefix + * + * @param {string} prefix + */ + constructor(prefix) { + super(); + + if (prefix === undefined) { + prefix = "id"; + } + + validateString(prefix); + + if (!internalCounter.has(prefix)) { + internalCounter.set(prefix, 1); + } + + let count = internalCounter.get(prefix); + this.id = prefix + count; + + internalCounter.set(prefix, ++count); + } + + /** + * @return {string} + */ + toString() { + return this.id; + } + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {ID} from "../types/id.mjs"; +import {isObject} from "../types/is.mjs"; +import {validateString} from "../types/validate.mjs"; + +export {trimSpaces} + +/** + * This special trim function allows to trim spaces that have been protected by a special escape character. + * + * ``` + * <script type="module"> + * import {trimSpaces} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/util/trimspaces.mjs'; + * trimSpaces(' hello \\ ') + * </script> + * ``` + * + * Hint: One stroke is escaped by the javascript interpreter, the second stroke escapes the stroke. + * + * ```text + * a\ b ↦ a b + * a\\ b ↦ a\ b + * ``` + * + * @since 1.24.0 + * @memberOf Monster.Util + * @copyright schukai GmbH + * @param {string} value + * @return {string} + * @throws {TypeError} value is not a string + */ + function trimSpaces(value) { + + validateString(value); + + let placeholder = new Map; + const regex = /((?<pattern>\\(?<char>.)){1})/mig; + + // The separator for args must be escaped + // undefined string which should not occur normally and is also not a regex + let result = value.matchAll(regex) + + for (let m of result) { + let g = m?.['groups']; + if (!isObject(g)) { + continue; + } + + let p = g?.['pattern']; + let c = g?.['char']; + + if (p && c) { + let r = '__' + new ID().toString() + '__'; + placeholder.set(r, c); + value = value.replace(p, r); + } + + } + + value = value.trim(); + placeholder.forEach((v, k) => { + value = value.replace(k, '\\' + v) + }) + + return value; + +} + +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../constants.mjs"; + +import {Base} from "../types/base.mjs"; +import {isInteger} from "../types/is.mjs"; +import {validateFunction, validateInteger} from "../types/validate.mjs"; + +export {DeadMansSwitch} + +/** + * The dead man's switch allows to set a timer which can be reset again and again within a defined period of time. + * + * ``` + * <script type="module"> + * import {DeadMansSwitch} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/util/deadmansswitch.mjs'; + * new DeadMansSwitch(); + * </script> + * ``` + * + * @example + * import {DeadMansSwitch} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/util/deadmansswitch.mjs'; + * + * const deadmansswitch = new DeadMansSwitch(100, ()=>{ + * console.log('yeah!') + * // ↦ "yeah!" + * }) + * + * deadmansswitch.touch(); // from here wait again 100 ms + * deadmansswitch.touch(200); // from here wait 200 ms + * + * @copyright schukai GmbH + * @since 1.29.0 + * @memberOf Monster.Util + * @summary Class to be able to execute function chains + */ + class DeadMansSwitch extends Base { + + /** + * Create new dead man's switch + * + * @param {Integer} delay + * @param {function} callback + * @throw {TypeError} the arguments must be either integer or functions + * @throws {TypeError} value is not an integer + */ + constructor(delay, callback) { + super(); + + init.call(this, validateInteger(delay), validateFunction(callback)); + } + + /** + * + * @param {Integer|undefined} [delay] + */ + touch(delay) { + + if (this[internalSymbol]['isAlreadyRun'] === true) { + throw new Error('has already run') + } + + if (isInteger(delay)) { + this[internalSymbol]['delay'] = delay + } else if (delay !== undefined) { + throw new Error('unsupported argument') + } + + clearTimeout(this[internalSymbol]['timer']); + + initCallback.call(this); + + return this; + } +} + +/** + * @private + */ +function initCallback() { + + const self = this; + + self[internalSymbol]['timer'] = setTimeout(() => { + self[internalSymbol]['isAlreadyRun'] = true; + self[internalSymbol]['callback'](); + }, self[internalSymbol]['delay']) +} + +/** + * @private + * @param {integer} delay + * @param {function} callback + */ +function init(delay, callback) { + const self = this; + + self[internalSymbol] = { + callback, + delay, + isAlreadyRun: false, + timer: undefined + }; + + initCallback.call(self); + +} + + + +'use strict'; + +/** + * @author schukai GmbH + */ + +import {validateObject} from '../types/validate.mjs'; + +export {deepFreeze} + +/** + * Deep freeze a object + * + * ``` + * <script type="module"> + * import {deepFreeze} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/util/freeze.mjs'; + * deepFreeze({}) + * </script> + * ``` + * + * @param {object} object object to be freeze + * @since 1.0.0 + * @returns {object} + * @memberOf Monster.Util + * @copyright schukai GmbH + * @throws {TypeError} value is not a object + */ + function deepFreeze(object) { + + validateObject(object) + + // Retrieve the defined property names of the object + var propNames = Object.getOwnPropertyNames(object); + + // Freeze properties before freezing yourself + for (let name of propNames) { + let value = object[name]; + + object[name] = (value && typeof value === "object") ? + deepFreeze(value) : value; + } + + return Object.freeze(object); +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from '../types/base.mjs'; +import {isFunction} from '../types/is.mjs'; + +export {Comparator} + +/** + * The comparator allows a comparison function to be abstracted. + * + * ``` + * <script type="module"> + * import {Comparator} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/util/comparator.mjs'; + * console.log(new Comparator()) + * </script> + * ``` + * + * The following are some examples of the application of the class. + * + * ``` + * new Comparator().lessThanOrEqual(2, 5) // ↦ true + * new Comparator().greaterThan(4, 2) // ↦ true + * new Comparator().equal(4, 4) // ↦ true + * new Comparator().equal(4, 5) // ↦ false + * ``` + * + * You can also pass your own comparison function, and thus define the comparison function. + * + * ``` + * new Comparator(function (a, b) { + * if (a.v === b.v) return 0; + * return a.v < b.v ? -1 : 1; + * }).equal({v: 2}, {v: 2}); // ↦ true + * ``` + * + * @example + * + * import {Comparator} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/util/comparator.mjs'; + * + * console.log(new Comparator().lessThanOrEqual(2, 5)) + * // ↦ true + * console.log(new Comparator().greaterThan(4, 2)) + * // ↦ true + * console.log(new Comparator().equal(4, 4)) + * // ↦ true + * console.log(new Comparator().equal(4, 5)) + * // ↦ false + * + * @since 1.3.0 + * @memberOf Monster.Util + */ +class Comparator extends Base { + + /** + * create new comparator + * + * @param {Monster.Util~exampleCallback} [callback] Comparator callback + * @throw {TypeError} unsupported type + * @throw {TypeError} impractical comparison + */ + constructor(callback) { + super(); + + if (isFunction(callback)) { + this.compare = callback + } else if (callback !== undefined) { + throw new TypeError("unsupported type") + } else { + // default compare function + + /** + * + * @param {*} a + * @param {*} b + * @return {integer} -1, 0 or 1 + */ + this.compare = function (a, b) { + + if (typeof a !== typeof b) { + throw new TypeError("impractical comparison", "types/comparator.mjs") + } + + if (a === b) { + return 0; + } + return a < b ? -1 : 1; + }; + } + + } + + /** + * changes the order of the operators + * + * @return {Comparator} + */ + reverse() { + const original = this.compare; + this.compare = (a, b) => original(b, a); + return this; + } + + /** + * Checks if two variables are equal. + * + * @param {*} a + * @param {*} b + * + * @return {boolean} + */ + equal(a, b) { + return this.compare(a, b) === 0; + } + + + /** + * Checks if variable `a` is greater than `b` + * + * @param {*} a + * @param {*} b + * + * @return {boolean} + */ + greaterThan(a, b) { + return this.compare(a, b) > 0; + } + + /** + * Checks if variable `a` is greater than or equal to `b` + * + * @param {*} a + * @param {*} b + * + * @return {boolean} + */ + greaterThanOrEqual(a, b) { + return this.greaterThan(a, b) || this.equal(a, b); + } + + /** + * Checks if variable `a` is less than or equal to `b` + * + * @param {*} a + * @param {*} b + * + * @return {boolean} + */ + lessThanOrEqual(a, b) { + return this.lessThan(a, b) || this.equal(a, b); + } + + /** + * Checks if variable a is less than b + * + * @param {*} a + * @param {*} b + * + * @return {boolean} + */ + lessThan(a, b) { + return this.compare(a, b) < 0; + } + + +} + + +/** + * This is the description for the callback function used by the operator + * + * ``` + * new Comparator(function (a, b) { + * if (a.v === b.v) return 0; + * return a.v < b.v ? -1 : 1; + * }).equal({v: 2}, {v: 2}); // ↦ true + * ``` + * + * @callback Monster.Util~exampleCallback + * @param {*} a + * @param {*} b + * @return {integer} -1, 0 or 1 + * @memberOf Monster.Util + * @see Monster.Util.Comparator + */ + +'use strict'; + +/** + * Namespace for utilities. + * + * @namespace Monster.Util + * @memberOf Monster + * @author schukai GmbH + */ +const ns = {};'use strict'; + +/** + * @author schukai GmbH + */ + + +import {getGlobal} from '../types/global.mjs'; +import {isArray, isFunction, isObject, isPrimitive} from '../types/is.mjs'; +import {typeOf} from "../types/typeof.mjs"; +import {validateObject} from "../types/validate.mjs"; + +export {clone} + +/** + * With this function, objects can be cloned. + * The entire object tree is run through. + * + * Proxy, Element, HTMLDocument and DocumentFragment instances are not cloned. + * Global objects such as windows are also not cloned, + * + * If an object has a method `getClone()`, this method is used to create the clone. + * + * ``` + * <script type="module"> + * import {clone} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/util/clone.mjs'; + * clone({}) + * </script> + * ``` + * + * @param {*} obj object to be cloned + * @returns {*} + * @since 1.0.0 + * @memberOf Monster.Util + * @copyright schukai GmbH + * @throws {Error} unable to clone obj! its type isn't supported. + */ +function clone(obj) { + + // typeof null results in 'object'. https://2ality.com/2013/10/typeof-null.html + if (null === obj) { + return obj; + } + + // Handle the two simple types, null and undefined + if (isPrimitive(obj)) { + return obj; + } + + // Handle the two simple types, null and undefined + if (isFunction(obj)) { + return obj; + } + + // Handle Array + if (isArray(obj)) { + let copy = []; + for (var i = 0, len = obj.length; i < len; i++) { + copy[i] = clone(obj[i]); + } + + return copy; + } + + if (isObject(obj)) { + + + // Handle Date + if (obj instanceof Date) { + let copy = new Date(); + copy.setTime(obj.getTime()); + return copy; + } + + /** Do not clone DOM nodes */ + if (typeof Element !== 'undefined' && obj instanceof Element) return obj; + if (typeof HTMLDocument !== 'undefined' && obj instanceof HTMLDocument) return obj; + if (typeof DocumentFragment !== 'undefined' && obj instanceof DocumentFragment) return obj; + + /** Do not clone global objects */ + if (obj === getGlobal()) return obj; + if (typeof globalContext !== 'undefined' && obj === globalContext) return obj; + if (typeof window !== 'undefined' && obj === window) return obj; + if (typeof document !== 'undefined' && obj === document) return obj; + if (typeof navigator !== 'undefined' && obj === navigator) return obj; + if (typeof JSON !== 'undefined' && obj === JSON) return obj; + + // Handle Proxy-Object + try { + // try/catch because possible: TypeError: Function has non-object prototype 'undefined' in instanceof check + if (obj instanceof Proxy) { + return obj; + } + } catch (e) { + } + + return cloneObject(obj) + + } + + throw new Error("unable to clone obj! its type isn't supported."); +} + +/** + * + * @param {object} obj + * @returns {object} + * @private + */ +function cloneObject(obj) { + + validateObject(obj); + + const constructor = obj?.['constructor']; + + /** Object has clone method */ + if(typeOf(constructor)==='function') { + const prototype = constructor?.prototype; + if(typeof prototype==='object') { + if(prototype.hasOwnProperty('getClone')&& typeOf(obj.getClone) === 'function') { + return obj.getClone(); + } + } + } + + let copy = {}; + if (typeof obj.constructor === 'function' && + typeof obj.constructor.call === 'function') { + copy = new obj.constructor(); + } + + for (let key in obj) { + + if (!obj.hasOwnProperty(key)) { + continue; + } + + if (isPrimitive(obj[key])) { + copy[key] = obj[key]; + continue; + } + + copy[key] = clone(obj[key]); + } + + return copy; +} + +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../constants.mjs"; +import {Base} from "../types/base.mjs"; +import {getGlobalFunction} from "../types/global.mjs"; +import {isFunction, isInteger} from "../types/is.mjs"; +import {Queue} from "../types/queue.mjs"; +import {validateFunction, validateInteger} from "../types/validate.mjs"; + +export {Processing} + +/** + * @private + */ +class Callback { + + /** + * + * @param {function} callback + * @param {int|undefined} time + * @throws {TypeError} value is not a function + * @throws {TypeError} value is not an integer + * @private + */ + constructor(callback, time) { + this[internalSymbol] = { + callback: validateFunction(callback), + time: validateInteger(time ?? 0) + }; + } + + /** + * @private + * @param {*} data + * @return {Promise} + */ + run(data) { + const self = this; + return new Promise((resolve, reject) => { + + getGlobalFunction('setTimeout')(() => { + try { + resolve(self[internalSymbol].callback(data)); + } catch (e) { + reject(e); + } + + }, + self[internalSymbol].time); + + + }) + + } +} + +/** + * This class allows to execute several functions in order. + * + * Functions and timeouts can be passed. If a timeout is passed, it applies to all further functions. + * In the example + * + * `timeout1, function1, function2, function3, timeout2, function4` + * + * the timeout1 is valid for the functions 1, 2 and 3 and the timeout2 for the function4. + * + * So the execution time is timeout1+timeout1+timeout1+timeout2 + * + * The result of `run()` is a promise. + * + * ``` + * <script type="module"> + * import {Processing} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/util/processing.mjs'; + * new Processing(); + * </script> + * ``` + * + * @example + * import {Processing} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/util/processing.mjs'; + * + * let startTime = +new Date(); + * + * new Processing((url)=>{ + * return fetch(url) + * },(response)=>{ + * // do something with the response + * console.log(response.status, +new Date()-startTime) + * },200,()=>{ + * // this function is called 200 seconds after fetch is received. + * console.log('finished', +new Date()-startTime) + * return 'done' + * }).run('https://monsterjs.org/assets/world.json').then(r=>{ + * console.log(r) + * // ↦ "done" + * }) + * + * @copyright schukai GmbH + * @since 1.21.0 + * @memberOf Monster.Util + * @summary Class to be able to execute function chains + */ +class Processing extends Base { + + /** + * Create new Processing + * + * Functions and timeouts can be passed. If a timeout is passed, it applies to all further functions. + * In the example + * + * `timeout1, function1, function2, function3, timeout2, function4` + * + * the timeout1 is valid for the functions 1, 2 and 3 and the timeout2 for the function4. + * + * So the execution time is timeout1+timeout1+timeout1+timeout2 + * + * @param {int} timeout Timeout + * @param {function} callback Callback + * @throw {TypeError} the arguments must be either integer or functions + */ + constructor() { + super(); + + this[internalSymbol] = { + queue: new Queue + }; + + let time = 0 + + for (const [, arg] of Object.entries(arguments)) { + if (isInteger(arg) && arg >= 0) { + time = arg; + } else if (isFunction(arg)) { + this[internalSymbol].queue.add(new Callback(arg, time)) + } else { + throw new TypeError('the arguments must be either integer or functions') + } + } + + + } + + /** + * Adds a function with the desired timeout + * If no timeout is specified, the timeout of the previous function is used. + * + * @param {function} callback + * @param {int|undefined} time + * @throws {TypeError} value is not a function + * @throws {TypeError} value is not an integer + */ + add(callback, time) { + this[internalSymbol].queue.add(new Callback(callback, time)) + return this; + } + + + /** + * Executes the defined functions in order. + * + * @param {*} data + * @return {Promise} + */ + run(data) { + const self = this; + if (this[internalSymbol].queue.isEmpty()) { + return Promise.resolve(data); + } + + return this[internalSymbol].queue.poll().run(data).then((result) => { + return self.run(result); + }); + + } + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {isArray, isObject} from "../types/is.mjs"; +import {typeOf} from "../types/typeof.mjs"; + +export {extend} + +/** + * Extend copies all enumerable own properties from one or + * more source objects to a target object. It returns the modified target object. + * + * ``` + * <script type="module"> + * import {extend} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/extend.mjs'; + * extend(a, b) + * </script> + * ``` + * + * @param {object} target + * @param {object} + * @return {object} + * @since 1.10.0 + * @copyright schukai GmbH + * @memberOf Monster.Data + * @throws {Error} unsupported argument + * @throws {Error} type mismatch + */ +function extend() { + let o, i; + + for (i = 0; i < arguments.length; i++) { + let a = arguments[i]; + + if (!(isObject(a) || isArray(a))) { + throw new Error('unsupported argument ' + JSON.stringify(a)); + } + + if (o === undefined) { + o = a; + continue; + } + + for (let k in a) { + + let v = a?.[k]; + + if (v === o?.[k]) { + continue; + } + + if ((isObject(v)&&typeOf(v)==='object') || isArray(v)) { + + if (o[k] === undefined) { + if (isArray(v)) { + o[k] = []; + } else { + o[k] = {}; + } + } else { + if (typeOf(o[k]) !== typeOf(v)) { + throw new Error("type mismatch: " + JSON.stringify(o[k]) + "(" + typeOf(o[k]) + ") != " + JSON.stringify(v) + "(" + typeOf(v) + ")"); + } + } + + o[k] = extend(o[k], v); + + } else { + o[k] = v; + } + + } + } + + return o; +} +'use strict'; + +/** + * @author schukai GmbH + */ + + +import {Base} from '../types/base.mjs'; +import {validateString} from '../types/validate.mjs'; +import {Transformer} from './transformer.mjs'; + +export {Pipe} + +const DELIMITER = '|'; + +/** + * The pipe class makes it possible to combine several processing steps. + * + * ``` + * <script type="module"> + * import {Pipe} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/pipe.mjs'; + * new Pipe() + * </script> + * ``` + * + * A pipe consists of commands whose input and output are connected with the pipe symbol `|`. + * + * With the Pipe, processing steps can be combined. Here, the value of an object is accessed via the pathfinder (path command). + * the word is then converted to uppercase letters and a prefix Hello is added. the two backslash safe the space char. + * + * @example + * import {Pipe} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/pipe.mjs'; + * + * let obj = { + * a: { + * b: { + * c: { + * d: "world" + * } + * } + * } + * } + * + * console.log(new Pipe('path:a.b.c.d | toupper | prefix:Hello\\ ').run(obj)); + * // ↦ Hello WORLD + * + * @since 1.5.0 + * @copyright schukai GmbH + * @memberOf Monster.Data + */ +class Pipe extends Base { + + /** + * + * @param {string} pipe a pipe consists of commands whose input and output are connected with the pipe symbol `|`. + * @throws {TypeError} + */ + constructor(pipe) { + super(); + validateString(pipe); + + this.pipe = pipe.split(DELIMITER).map((v) => { + return new Transformer(v); + }); + + + } + + /** + * + * @param {string} name + * @param {function} callback + * @param {object} context + * @returns {Transformer} + * @throws {TypeError} value is not a string + * @throws {TypeError} value is not a function + */ + setCallback(name, callback, context) { + + for (const [, t] of Object.entries(this.pipe)) { + t.setCallback(name, callback, context); + } + + return this; + } + + /** + * run a pipe + * + * @param {*} value + * @returns {*} + */ + run(value) { + return this.pipe.reduce((accumulator, transformer, currentIndex, array) => { + return transformer.run(accumulator); + }, value); + } +} +'use strict'; + +/** + * @author schukai GmbH + */ + + +import {isFunction, isObject, isString} from "../types/is.mjs"; +import {validateString} from "../types/validate.mjs"; +import {clone} from "../util/clone.mjs"; +import {DELIMITER, Pathfinder, WILDCARD} from "./pathfinder.mjs"; + +export {buildMap, PARENT, assembleParts} + +/** + * @type {string} + * @memberOf Monster.Data + */ +const PARENT = '^'; + + +/** + * With the help of the function `buildMap()`, maps can be easily created from data objects. + * + * Either a simple definition `a.b.c` or a template `${a.b.c}` can be specified as the path. + * Key and value can be either a definition or a template. The key does not have to be defined. + * + * ``` + * <script type="module"> + * import {buildMap} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/buildmap.mjs'; + * console.log(buildMap()) + * </script> + * ``` + * + * The templates determine the appearance of the keys and the value of the map. Either a single value `id` can be taken or a composite key `${id} ${name}` can be used. + * + * If you want to access values of the parent data set, you have to use the `^` character `${id} ${^.name}`. + * + * @example + * + * import {buildMap} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/buildmap.mjs'; + * // a typical data structure as reported by an api + * + * let map; + * let obj = { + * "data": [ + * { + * "id": 10, + * "name": "Cassandra", + * "address": { + * "street": "493-4105 Vulputate Street", + * "city": "Saumur", + * "zip": "52628" + * } + * }, + * { + * "id": 20, + * "name": "Holly", + * "address": { + * "street": "1762 Eget Rd.", + * "city": "Schwalbach", + * "zip": "952340" + * } + * }, + * { + * "id": 30, + * "name": "Guy", + * "address": { + * "street": "957-388 Sollicitudin Avenue", + * "city": "Panchià", + * "zip": "420729" + * } + * } + * ] + * }; + * + * // The function is passed this data structure and with the help of the selector `'data.*'` the data to be considered are selected. + * // The key is given by a simple definition `'id'` and the value is given by a template `'${name} (${address.zip} ${address.city})'`. + * map = buildMap(obj, 'data.*', '${name} (${address.zip} ${address.city})', 'id'); + * console.log(map); + * + * // ↦ Map(3) { + * // '10' => 'Cassandra (52628 Saumur)', + * // '20' => 'Holly (952340 Schwalbach)', + * // '30' => 'Guy (420729 Panchià)' + * // } + * + * // If no key is specified, the key from the selection, here the array index, is taken. + * map = buildMap(obj, 'data.*', '${name} (${address.zip} ${address.city})'); + * console.log(map); + * + * // ↦ Map(3) { + * // '0' => 'Cassandra (52628 Saumur)', + * // '1' => 'Holly (952340 Schwalbach)', + * // '2' => 'Guy (420729 Panchià)' + * // } + * + * // a filter (function(value, key) {}) can be specified to accept only defined entries. + * map = buildMap(obj, 'data.*', '${name} (${address.zip} ${address.city})', 'id', function (value, key) { + * return (value['id'] >= 20) ? true : false + * }); + * console.log(map); + * + * // ↦ Map(2) { + * // 20 => 'Holly (952340 Schwalbach)', + * // 30 => 'Guy (420729 Panchià)' + * // } + * + * @param {*} subject + * @param {string|Monster.Data~exampleSelectorCallback} selector + * @param {string} [valueTemplate] + * @param {string} [keyTemplate] + * @param {Monster.Data~exampleFilterCallback} [filter] + * @return {*} + * @memberOf Monster.Data + * @throws {TypeError} value is neither a string nor a function + * @throws {TypeError} the selector callback must return a map + */ +function buildMap(subject, selector, valueTemplate, keyTemplate, filter) { + return assembleParts(subject, selector, filter, function (v, k, m) { + k = build(v, keyTemplate, k); + v = build(v, valueTemplate); + this.set(k, v); + }); + +} + + +/** + * @private + * @param {*} subject + * @param {string|Monster.Data~exampleSelectorCallback} selector + * @param {Monster.Data~exampleFilterCallback} [filter] + * @param {function} callback + * @return {Map} + * @throws {TypeError} selector is neither a string nor a function + */ +function assembleParts(subject, selector, filter, callback) { + + const result = new Map(); + + let map; + if (isFunction(selector)) { + map = selector(subject) + if (!(map instanceof Map)) { + throw new TypeError('the selector callback must return a map'); + } + } else if (isString(selector)) { + map = new Map; + buildFlatMap.call(map, subject, selector); + } else { + throw new TypeError('selector is neither a string nor a function') + } + + if (!(map instanceof Map)) { + return result; + } + + map.forEach((v, k, m) => { + if (isFunction(filter)) { + if (filter.call(m, v, k) !== true) return; + } + + callback.call(result, v, k, m); + + }); + + return result; +} + +/** + * @private + * @param subject + * @param selector + * @param key + * @param parentMap + * @return {*} + */ +function buildFlatMap(subject, selector, key, parentMap) { + + const result = this; + const currentMap = new Map; + + const resultLength = result.size; + + if (key === undefined) key = []; + + let parts = selector.split(DELIMITER); + let current = "", currentPath = []; + do { + + current = parts.shift(); + currentPath.push(current); + + if (current === WILDCARD) { + + let finder = new Pathfinder(subject); + let map; + + try { + map = finder.getVia(currentPath.join(DELIMITER)); + } catch (e) { + let a = e; + map = new Map(); + } + + for (const [k, o] of map) { + + let copyKey = clone(key); + + currentPath.map((a) => { + copyKey.push((a === WILDCARD) ? k : a) + }) + + let kk = copyKey.join(DELIMITER); + let sub = buildFlatMap.call(result, o, parts.join(DELIMITER), copyKey, o); + + if (isObject(sub) && parentMap !== undefined) { + sub[PARENT] = parentMap; + } + + currentMap.set(kk, sub); + } + + } + + + } while (parts.length > 0); + + // no set in child run + if (resultLength === result.size) { + for (const [k, o] of currentMap) { + result.set(k, o); + } + } + + return subject; + +} + + +/** + * With the help of this filter callback, values can be filtered out. Only if the filter function returns true, the value is taken for the map. + * + * @callback Monster.Data~exampleFilterCallback + * @param {*} value Value + * @param {string} key Key + * @memberOf Monster.Data + * @see {@link Monster.Data.buildMap} + */ + +/** + * Alternatively to a string selector a callback can be specified. this must return a map. + * + * @example + * import {buildMap} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/buildmap.mjs'; + * + * let obj = { + * "data": [ + * { + * "id": 10, + * "name": "Cassandra", + * "enrichment": { + * variants: [ + * { + * sku: 1, label: "XXS", price: [ + * {vk: '12.12 €'}, + * {vk: '12.12 €'} + * ] + * }, + * { + * sku: 2, label: "XS", price: [ + * {vk: '22.12 €'}, + * {vk: '22.12 €'} + * ] + * }, + * { + * sku: 3, label: "S", price: [ + * {vk: '32.12 €'}, + * {vk: '32.12 €'} + * ] + * }, + * { + * sku: 4, label: "L", price: [ + * {vk: '42.12 €'}, + * {vk: '42.12 €'} + * ] + * } + * ] + * + * } + * }, + * { + * "id": 20, + * "name": "Yessey!", + * "enrichment": { + * variants: [ + * { + * sku: 1, label: "XXS", price: [ + * {vk: '12.12 €'}, + * {vk: '12.12 €'} + * ] + * }, + * { + * sku: 2, label: "XS", price: [ + * {vk: '22.12 €'}, + * {vk: '22.12 €'} + * ] + * }, + * { + * sku: 3, label: "S", price: [ + * {vk: '32.12 €'}, + * {vk: '32.12 €'} + * ] + * }, + * { + * sku: 4, label: "L", price: [ + * {vk: '42.12 €'}, + * {vk: '42.12 €'} + * ] + * } + * ] + * + * } + * } + * ] + * }; + * + * let callback = function (subject) { + * let m = new Map; + * + * for (const [i, b] of Object.entries(subject.data)) { + * + * let key1 = i; + * + * for (const [j, c] of Object.entries(b.enrichment.variants)) { + * let key2 = j; + * + * for (const [k, d] of Object.entries(c.price)) { + * + * let key3 = k; + * + * d.name = b.name; + * d.label = c.label; + * d.id = [key1, key2, key3].join('.'); + * + * m.set(d.id, d); + * } + * + * } + * } + * return m; + * } + * + * let map = buildMap(obj, callback, '${name} ${vk}', '${id}') + * + * // ↦ Map(3) { + * // "0.0.0":"Cassandra 12.12 €", + * // "0.0.1":"Cassandra 12.12 €", + * // "0.1.0":"Cassandra 22.12 €", + * // "0.1.1":"Cassandra 22.12 €", + * // "0.2.0":"Cassandra 32.12 €", + * // "0.2.1":"Cassandra 32.12 €", + * // "0.3.0":"Cassandra 42.12 €", + * // "0.3.1":"Cassandra 42.12 €", + * // "1.0.0":"Yessey! 12.12 €", + * // "1.0.1":"Yessey! 12.12 €", + * // "1.1.0":"Yessey! 22.12 €", + * // "1.1.1":"Yessey! 22.12 €", + * // "1.2.0":"Yessey! 32.12 €", + * // "1.2.1":"Yessey! 32.12 €", + * // "1.3.0":"Yessey! 42.12 €", + * // "1.3.1":"Yessey! 42.12 €" + * // } + * + * @callback Monster.Data~exampleSelectorCallback + * @param {*} subject subject + * @return Map + * @since 1.17.0 + * @memberOf Monster.Data + * @see {@link Monster.Data.buildMap} + */ + +/** + * @private + * @param {*} subject + * @param {string|undefined} definition + * @param {*} defaultValue + * @return {*} + */ +function build(subject, definition, defaultValue) { + if (definition === undefined) return defaultValue ? defaultValue : subject; + validateString(definition); + + const regexp = /(?<placeholder>\${(?<path>[a-z\^A-Z.\-_0-9]*)})/gm + const array = [...definition.matchAll(regexp)]; + + let finder = new Pathfinder(subject); + + if (array.length === 0) { + return finder.getVia(definition); + } + + array.forEach((a) => { + let groups = a?.['groups']; + let placeholder = groups?.['placeholder'] + if (placeholder === undefined) return; + + let path = groups?.['path'] + + let v = finder.getVia(path); + if (v === undefined) v = defaultValue; + + definition = definition.replaceAll(placeholder, v); + + + }) + + return definition; + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../constants.mjs"; + +import {Base} from "../types/base.mjs"; +import {parseDataURL} from "../types/dataurl.mjs"; +import {isString} from "../types/is.mjs"; +import {ProxyObserver} from "../types/proxyobserver.mjs"; +import {validateObject} from "../types/validate.mjs"; +import {extend} from "./extend.mjs"; +import {Pathfinder} from "./pathfinder.mjs"; + +export {Datasource} + +/** + * @private + * @type {symbol} + * @memberOf Monster.Data + * @since 1.24.0 + */ +const internalDataSymbol = Symbol('internalData'); + +/** + * The datasource class is the basis for dealing with different data sources. + * It provides a unified interface for accessing data + * + * ``` + * <script type="module"> + * import {Datasource} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/datasource.mjs'; + * new Datasource() + * </script> + * ``` + * + * @example + * + * import {Datasource} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/datasource.mjs' + * + * class MyDatasource extends Datasource { + * + * } + * + * const ds = new MyDatasource(); + * + * @since 1.22.0 + * @copyright schukai GmbH + * @memberOf Monster.Data + * @summary The datasource class encapsulates the access to data objects. + */ +class Datasource extends Base { + + /** + * + */ + constructor() { + super(); + this[internalSymbol] = new ProxyObserver({ + 'options': extend({}, this.defaults) + }); + + this[internalDataSymbol] = new ProxyObserver({ + + }); + + + } + + /** + * attach a new observer + * + * @param {Observer} observer + * @returns {Datasource} + */ + attachObserver(observer) { + this[internalDataSymbol].attachObserver(observer) + return this; + } + + /** + * detach a observer + * + * @param {Observer} observer + * @returns {Datasource} + */ + detachObserver(observer) { + this[internalDataSymbol].detachObserver(observer) + return this; + } + + /** + * @param {Observer} observer + * @returns {boolean} + */ + containsObserver(observer) { + return this[internalDataSymbol].containsObserver(observer); + } + + /** + * Derived classes can override and extend this method as follows. + * + * ``` + * get defaults() { + * return Object.assign({}, super.defaults, { + * myValue:true + * }); + * } + * ``` + */ + get defaults() { + return {}; + } + + /** + * Set option + * + * @param {string} path + * @param {*} value + * @return {Datasource} + */ + setOption(path, value) { + new Pathfinder(this[internalSymbol].getSubject()['options']).setVia(path, value); + return this; + } + + /** + * @param {string|object} options + * @return {Datasource} + * @throws {Error} the options does not contain a valid json definition + */ + setOptions(options) { + + if (isString(options)) { + options = parseOptionsJSON(options) + } + + const self = this; + extend(self[internalSymbol].getSubject()['options'], self.defaults, options); + + return self; + } + + /** + * nested options can be specified by path `a.b.c` + * + * @param {string} path + * @param {*} defaultValue + * @return {*} + */ + getOption(path, defaultValue) { + let value; + + try { + value = new Pathfinder(this[internalSymbol].getRealSubject()['options']).getVia(path); + } catch (e) { + + } + + if (value === undefined) return defaultValue; + return value; + } + + /** + * @throws {Error} this method must be implemented by derived classes. + * @return {Promise} + */ + read() { + throw new Error("this method must be implemented by derived classes") + } + + /** + * @throws {Error} this method must be implemented by derived classes. + * @return {Promise} + */ + write() { + throw new Error("this method must be implemented by derived classes") + } + + + /** + * Returns real object + * + * @return {Object|Array} + */ + get() { + const self = this; + return self[internalDataSymbol].getRealSubject(); + } + + /** + * @param {Object|Array} data + * @return {Datasource} + */ + set(data) { + const self = this; + self[internalDataSymbol].setSubject(data); + return self; + } + +} + +/** + * @private + * @param data + * @return {Object} + * @throws {Error} the options does not contain a valid json definition + */ +function parseOptionsJSON(data) { + if (isString(data)) { + + // the configuration can be specified as a data url. + try { + let dataUrl = parseDataURL(data); + data = dataUrl.content; + } catch (e) { + + } + + + try { + let obj = JSON.parse(data); + validateObject(obj); + return obj; + } catch (e) { + throw new Error('the options does not contain a valid json definition (actual: ' + data + ').'); + } + } + + return {}; +} +'use strict'; + +/** + * In this namespace you will find classes and methods for handling data. + * + * @namespace Monster.Data + * @memberOf Monster + * @author schukai GmbH + */ +const ns = {};'use strict'; + +/** + * @author schukai GmbH + */ + + +import {isArray, isObject} from "../types/is.mjs"; +import {Node} from "../types/node.mjs"; +import {NodeList} from "../types/nodelist.mjs"; +import {assembleParts} from "./buildmap.mjs"; +import {extend} from "./extend.mjs"; + +export {buildTree} + +/** + * @private + * @type {symbol} + */ +const parentSymbol = Symbol('parent'); + +/** + * @private + * @type {symbol} + */ +const rootSymbol = Symbol('root'); + +/** + * @typedef {Object} buildTreeOptions + * @property {array} options.rootReferences=[null, undefined] defines the values for elements without parents + * @property {Monster.Data~exampleFilterCallback} options.filter filtering of the values + * @memberOf Monster.Data + */ + +/** + * With the help of the function `buildTree()`, nodes can be easily created from data objects. + * + * ``` + * <script type="module"> + * import {buildTree} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/buildtree.mjs'; + * buildTree() + * </script> + * ``` + * + * @param {*} subject + * @param {string|Monster.Data~exampleSelectorCallback} selector + * @param {string} idKey + * @param {string} parentIDKey + * @param {buildTreeOptions} [options] + * @return {*} + * @memberOf Monster.Data + * @throws {TypeError} value is neither a string nor a function + * @throws {TypeError} the selector callback must return a map + * @throws {Error} the object has no value for the specified id + * @since 1.26.0 + */ +function buildTree(subject, selector, idKey, parentIDKey, options) { + + const nodes = new Map; + + if (!isObject(options)) { + options = {} + } + + options = extend({}, { + rootReferences: [null, undefined], + filter: undefined + }, options) + + const filter = options?.filter; + let rootReferences = options.rootReferences; + if (!isArray(rootReferences)) { + rootReferences = [rootReferences]; + } + + const childMap = assembleParts(subject, selector, filter, function (o, k, m) { + + const key = o?.[idKey] + let ref = o?.[parentIDKey] + if (rootReferences.indexOf(ref) !== -1) ref = rootSymbol; + + if (key === undefined) { + throw new Error('the object has no value for the specified id') + } + + o[parentSymbol] = ref; + + const node = new Node(o); + this.has(ref) ? this.get(ref).add(node) : this.set(ref, new NodeList().add(node)); + nodes.set(key, node); + + }) + + nodes.forEach(node => { + + let id = node?.['value']?.[idKey]; + + if (childMap.has(id)) { + node.childNodes = childMap.get(id); + childMap.delete(id) + } + }) + + const list = new NodeList; + + childMap.forEach((s) => { + if (s instanceof Set) { + s.forEach((n) => { + list.add(n); + }) + } + }) + + return list; +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from '../types/base.mjs'; +import {getGlobal, getGlobalObject} from "../types/global.mjs"; +import {ID} from '../types/id.mjs'; +import {isArray, isObject, isString} from '../types/is.mjs'; +import { + validateFunction, + validateInteger, + validateObject, + validatePrimitive, + validateString +} from '../types/validate.mjs'; +import {clone} from "../util/clone.mjs"; +import {Pathfinder} from "./pathfinder.mjs"; + +export {Transformer} + +/** + * The transformer class is a swiss army knife for manipulating values. especially in combination with the pipe, processing chains can be built up. + * + * ``` + * <script type="module"> + * import {Transformer} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/transformer.mjs'; + * new Transformer() + * </script> + * ``` + * + * A simple example is the conversion of all characters to lowercase. for this purpose the command `tolower` must be used. + * + * ``` + * let t = new Transformer('tolower').run('ABC'); // ↦ abc + * ``` + * + * **all commands** + * + * in the following table all commands, parameters and existing aliases are described. + * + * | command | parameter | alias | description | + * |:-------------|:---------------------------|:------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| + * | to-base64 | | base64, btob | Converts the value to base64 | + * | from-base64 | | atob | Converts the value from base64 | + * | call | function:param1:param2:... | | Calling a callback function. The function can be defined in three places: either globally, in the context `addCallback` or in the passed object | + * | default | value:type | ?? | If the value is undefined the first argument is returned, otherwise the value. The third optional parameter specifies the desired type. If no type is specified, string is used. Valid types are bool, string, int, float, undefined and object. An object default value must be specified as a base64 encoded json string. (since 1.12.0) | + * | debug | | | the passed value is output (console) and returned | + * | empty | | | Return empty String "" | + * | first-key | default | | Can be applied to objects and returns the value of the first key. All keys of the object are fetched and sorted. (since 1.23.0) | + * | fromjson | | | Type conversion from a JSON string (since 1.12.0) | + * | if | statement1:statement2 | ? | Is the ternary operator, the first parameter is the valid statement, the second is the false part. To use the current value in the queue, you can set the value keyword. On the other hand, if you want to have the static string "value", you have to put one backslash \\ in front of it and write value. the follow values are true: 'on', true, 'true'. If you want to have a space, you also have to write \\ in front of the space. | + * | index | key:default | property, key | Fetches a value from an object, an array, a map or a set | + * | last-key | default | | Can be applied to objects and returns the value of the last key. All keys of the object are fetched and sorted. (since 1.23.0) | + * | length | | count | Length of the string or entries of an array or object | + * | nop | | | Do nothing | + * | nth-key | index:default | | Can be applied to objects and returns the value of the nth key. All keys of the object are fetched and sorted. (since 1.23.0) | + * | nth-last-key | index:default | | Can be applied to objects and returns the value of the nth key from behind. All keys of the object are fetched and sorted. (since 1.23.0) | + * | path | path | | The access to an object is done via a Pathfinder object | + * | path-exists | path | | Check if the specified path is available in the value (since 1.24.0) | + * | plaintext | | plain | All HTML tags are removed (*) | + * | prefix | text | | Adds a prefix | + * | rawurlencode | | | URL coding | + * | static | | none | The Arguments value is used and passed to the value. Special characters \ <space> and : can be quotet by a preceding \. | + * | substring | start:length | | Returns a substring | + * | suffix | text | | Adds a suffix | + * | tointeger | | | Type conversion to an integer value | + * | tojson | | | Type conversion to a JSON string (since 1.8.0) | + * | tolower | | strtolower, tolowercase | The input value is converted to lowercase letters | + * | tostring | | | Type conversion to a string. | + * | toupper | | strtoupper, touppercase | The input value is converted to uppercase letters | + * | trim | | | Remove spaces at the beginning and end | + * | ucfirst | | | First character large | + * | ucwords | | | Any word beginning large | + * | undefined | | | Return undefined | + * | uniqid | | | Creates a string with a unique value (**) + * + * (*) for this functionality the extension [jsdom](https://www.npmjs.com/package/jsdom) must be loaded in the nodejs context. + * + * ``` + * // polyfill + * if (typeof window !== "object") { + * const {window} = new JSDOM('', { + * url: 'http://example.com/', + * pretendToBeVisual: true + * }); + * + * [ + * 'self', + * 'document', + * 'Node', + * 'Element', + * 'HTMLElement', + * 'DocumentFragment', + * 'DOMParser', + * 'XMLSerializer', + * 'NodeFilter', + * 'InputEvent', + * 'CustomEvent' + * ].forEach(key => (global[key] = window[key])); + * } + * ``` + * + * (**) for this command the crypt library is necessary in the nodejs context. + * + * ``` + * import * as Crypto from "@peculiar/webcrypto"; + * global['crypto'] = new Crypto.Crypto(); + * ``` + * + * @example + * + * import {Transformer} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/transformer.mjs'; + * + * const transformer = new Transformer("tolower") + * + * console.log(transformer.run("HELLO")) + * // ↦ hello + * + * console.log(transformer.run("WORLD")) + * // ↦ world + * + * @since 1.5.0 + * @copyright schukai GmbH + * @memberOf Monster.Data + */ +class Transformer extends Base { + /** + * + * @param {string} definition + */ + constructor(definition) { + super(); + this.args = disassemble(definition); + this.command = this.args.shift() + this.callbacks = new Map(); + + } + + /** + * + * @param {string} name + * @param {function} callback + * @param {object} context + * @returns {Transformer} + * @throws {TypeError} value is not a string + * @throws {TypeError} value is not a function + */ + setCallback(name, callback, context) { + validateString(name) + validateFunction(callback) + + if (context !== undefined) { + validateObject(context); + } + + this.callbacks.set(name, { + callback: callback, + context: context, + }); + + return this; + } + + /** + * + * @param {*} value + * @returns {*} + * @throws {Error} unknown command + * @throws {TypeError} unsupported type + * @throws {Error} type not supported + */ + run(value) { + return transform.apply(this, [value]) + } +} + +/** + * + * @param {string} command + * @returns {array} + * @private + */ +function disassemble(command) { + + validateString(command); + + let placeholder = new Map; + const regex = /((?<pattern>\\(?<char>.)){1})/mig; + + // The separator for args must be escaped + // undefined string which should not occur normally and is also not a regex + let result = command.matchAll(regex) + + for (let m of result) { + let g = m?.['groups']; + if (!isObject(g)) { + continue; + } + + let p = g?.['pattern']; + let c = g?.['char']; + + if (p && c) { + let r = '__' + new ID().toString() + '__'; + placeholder.set(r, c); + command = command.replace(p, r); + } + + } + let parts = command.split(':'); + + parts = parts.map(function (value) { + let v = value.trim(); + for (let k of placeholder) { + v = v.replace(k[0], k[1]); + } + return v; + + + }); + + return parts +} + +/** + * tries to make a string out of value and if this succeeds to return it back + * + * @param {*} value + * @returns {string} + * @private + */ +function convertToString(value) { + + if (isObject(value) && value.hasOwnProperty('toString')) { + value = value.toString(); + } + + validateString(value) + return value; +} + +/** + * + * @param {*} value + * @returns {*} + * @private + * @throws {Error} unknown command + * @throws {TypeError} unsupported type + * @throws {Error} type not supported + * @throws {Error} missing key parameter + */ +function transform(value) { + + const console = getGlobalObject('console'); + + let args = clone(this.args); + let key, defaultValue; + + switch (this.command) { + + case 'static': + return this.args.join(':'); + + case 'tolower': + case 'strtolower': + case 'tolowercase': + validateString(value) + return value.toLowerCase(); + + case 'toupper': + case 'strtoupper': + case 'touppercase': + validateString(value) + return value.toUpperCase(); + + case 'tostring': + return "" + value; + + case 'tointeger': + let n = parseInt(value); + validateInteger(n); + return n + + case 'tojson': + return JSON.stringify(value); + + case 'fromjson': + return JSON.parse(value); + + case 'trim': + validateString(value) + return value.trim(); + + case 'rawurlencode': + validateString(value) + return encodeURIComponent(value) + .replace(/!/g, '%21') + .replace(/'/g, '%27') + .replace(/\(/g, '%28') + .replace(/\)/g, '%29') + .replace(/\*/g, '%2A'); + + + case 'call': + + /** + * callback-definition + * function callback(value, ...args) { + * return value; + * } + */ + + let callback; + let callbackName = args.shift(); + let context = getGlobal(); + + if (isObject(value) && value.hasOwnProperty(callbackName)) { + callback = value[callbackName]; + } else if (this.callbacks.has(callbackName)) { + let s = this.callbacks.get(callbackName); + callback = s?.['callback']; + context = s?.['context']; + } else if (typeof window === 'object' && window.hasOwnProperty(callbackName)) { + callback = window[callbackName]; + } + validateFunction(callback); + + args.unshift(value); + return callback.call(context, ...args); + + case 'plain': + case 'plaintext': + validateString(value); + let doc = new DOMParser().parseFromString(value, 'text/html'); + return doc.body.textContent || ""; + + case 'if': + case '?': + + validatePrimitive(value); + + let trueStatement = (args.shift() || undefined); + let falseStatement = (args.shift() || undefined); + + if (trueStatement === 'value') { + trueStatement = value; + } + if (trueStatement === '\\value') { + trueStatement = 'value'; + } + if (falseStatement === 'value') { + falseStatement = value; + } + if (falseStatement === '\\value') { + falseStatement = 'value'; + } + + let condition = ((value !== undefined && value !== '' && value !== 'off' && value !== 'false' && value !== false) || value === 'on' || value === 'true' || value === true); + return condition ? trueStatement : falseStatement; + + + case 'ucfirst': + validateString(value); + + let firstchar = value.charAt(0).toUpperCase(); + return firstchar + value.substr(1); + case 'ucwords': + validateString(value); + + return value.replace(/^([a-z\u00E0-\u00FC])|\s+([a-z\u00E0-\u00FC])/g, function (v) { + return v.toUpperCase(); + }); + + case 'count': + case 'length': + + if ((isString(value) || isObject(value) || isArray(value)) && value.hasOwnProperty('length')) { + return value.length; + } + + throw new TypeError("unsupported type " + typeof value); + + case 'to-base64': + case 'btoa': + case 'base64': + return btoa(convertToString(value)); + + case 'atob': + case 'from-base64': + return atob(convertToString(value)); + + case 'empty': + return ''; + + case 'undefined': + return undefined; + + case 'debug': + + if (isObject(console)) { + console.log(value); + } + + return value; + + case 'prefix': + validateString(value); + let prefix = args?.[0]; + return prefix + value; + + case 'suffix': + validateString(value); + let suffix = args?.[0]; + return value + suffix; + + case 'uniqid': + return (new ID()).toString(); + + case 'first-key': + case 'last-key': + case 'nth-last-key': + case 'nth-key': + + if (!isObject(value)) { + throw new Error("type not supported") + } + + const keys = Object.keys(value).sort() + + if (this.command === 'first-key') { + key = 0; + } else if (this.command === 'last-key') { + key = keys.length - 1; + } else { + + key = validateInteger(parseInt(args.shift())); + + if (this.command === 'nth-last-key') { + key = keys.length - key - 1; + } + } + + defaultValue = (args.shift() || ''); + + let useKey = keys?.[key]; + + if (value?.[useKey]) { + return value?.[useKey]; + } + + return defaultValue; + + + case 'key': + case 'property': + case 'index': + + key = args.shift() || undefined; + + if (key === undefined) { + throw new Error("missing key parameter") + } + + defaultValue = (args.shift() || undefined); + + if (value instanceof Map) { + if (!value.has(key)) { + return defaultValue; + } + return value.get(key); + } + + if (isObject(value) || isArray(value)) { + + if (value?.[key]) { + return value?.[key]; + } + + return defaultValue; + } + + throw new Error("type not supported") + + case 'path-exists': + + key = args.shift(); + if (key === undefined) { + throw new Error("missing key parameter") + } + + return new Pathfinder(value).exists(key); + + case 'path': + + key = args.shift(); + if (key === undefined) { + throw new Error("missing key parameter") + } + + let pf = new Pathfinder(value); + + if (!pf.exists(key)) { + return undefined; + } + + return pf.getVia(key); + + + case 'substring': + + validateString(value); + + let start = parseInt(args[0]) || 0; + let end = (parseInt(args[1]) || 0) + start; + + return value.substring(start, end); + + case 'nop': + return value; + + case '??': + case 'default': + if (value !== undefined && value !== null) { + return value; + } + + defaultValue = args.shift(); + let defaultType = args.shift(); + if (defaultType === undefined) { + defaultType = 'string'; + } + + switch (defaultType) { + case 'int': + case 'integer': + return parseInt(defaultValue); + case 'float': + return parseFloat(defaultValue); + case 'undefined': + return undefined + case 'bool': + case 'boolean': + defaultValue = defaultValue.toLowerCase() + return ((defaultValue !== 'undefined' && defaultValue !== '' && defaultValue !== 'off' && defaultValue !== 'false' && defaultValue !== 'false') || defaultValue === 'on' || defaultValue === 'true' || defaultValue === 'true'); + case 'string': + return "" + defaultValue; + case "object": + return JSON.parse(atob(defaultValue)); + } + + throw new Error("type not supported") + + + default: + throw new Error("unknown command " + this.command) + } + + return value; +} + +'use strict'; + +/** + * @author schukai GmbH + */ + +import {Base} from '../types/base.mjs'; +import {isArray, isInteger, isObject, isPrimitive} from '../types/is.mjs'; +import {Stack} from "../types/stack.mjs"; +import {validateInteger, validateString} from '../types/validate.mjs'; + +export {Pathfinder, DELIMITER, WILDCARD} + +/** + * path separator + * + * @private + * @type {string} + */ +const DELIMITER = '.'; + +/** + * @private + * @type {string} + */ +const WILDCARD = '*'; + +/** + * Pathfinder is a class to find a path to an object. + * + * ``` + * <script type="module"> + * import {Pathfinder} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/pathfinder.mjs'; + * console.log(new Pathfinder()) + * </script> + * ``` + * + * With the help of the pathfinder, values can be read and written from an object construct. + * + * ``` + * new Pathfinder({ + * a: { + * b: { + * f: [ + * { + * g: false, + * } + * ], + * } + * } + * }).getVia("a.b.f.0.g"); // ↦ false + * ``` + * + * if a value is not present or has the wrong type, a corresponding exception is thrown. + * + * ``` + * new Pathfinder({}).getVia("a.b.f.0.g"); // ↦ Error + * ``` + * + * The `Pathfinder.exists()` method can be used to check whether access to the path is possible. + * + * ``` + * new Pathfinder({}).exists("a.b.f.0.g"); // ↦ false + * ``` + * + * pathfinder can also be used to build object structures. to do this, the `Pathfinder.setVia()` method must be used. + * + * ``` + * obj = {}; + * new Pathfinder(obj).setVia('a.b.0.c', true); // ↦ {a:{b:[{c:true}]}} + * ``` + * + * @example + * + * import {Pathfinder} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/pathfinder.mjs'; + * + * let value = new Pathfinder({ + * a: { + * b: { + * f: [ + * { + * g: false, + * } + * ], + * } + * } + * }).getVia("a.b.f.0.g"); + * + * console.log(value); + * // ↦ false + * + * try { + * new Pathfinder({}).getVia("a.b.f.0.g"); + * } catch(e) { + * console.log(e.toString()); + * // ↦ Error: the journey is not at its end (b.f.0.g) + * } + * + * @example + * + * import {Pathfinder} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/pathfinder.mjs'; + * + * let p = new Pathfinder({ + * a: { + * x: [ + * {c: 1}, {c: 2} + * ], + * y: true + * }, + * b: { + * x: [ + * {c: 1, d: false}, {c: 2} + * ], + * y: true + * }, + * }); + * + * let r = p.getVia("*.x.*.c"); + * console.log(r); + * + * @since 1.4.0 + * @copyright schukai GmbH + * @memberOf Monster.Data + */ +class Pathfinder extends Base { + + /** + * @param {array|object|Map|Set} value + * @since 1.4.0 + * @throws {Error} the parameter must not be a simple type + **/ + constructor(object) { + super(); + + if (isPrimitive(object)) { + throw new Error('the parameter must not be a simple type'); + } + + this.object = object; + this.wildCard = WILDCARD; + } + + /** + * set wildcard + * + * @param {string} wildcard + * @return {Pathfinder} + * @since 1.7.0 + */ + setWildCard(wildcard) { + validateString(wildcard); + this.wildCard = wildcard; + return this; + } + + /** + * + * @param {string} path + * @since 1.4.0 + * @returns {*} + * @throws {TypeError} unsupported type + * @throws {Error} the journey is not at its end + * @throws {TypeError} value is not a string + * @throws {TypeError} value is not an integer + * @throws {Error} unsupported action for this data type + */ + getVia(path) { + return getValueViaPath.call(this, this.object, validateString(path)); + } + + /** + * + * @param {string} path + * @param {*} value + * @returns {Pathfinder} + * @since 1.4.0 + * @throws {TypeError} unsupported type + * @throws {TypeError} value is not a string + * @throws {TypeError} value is not an integer + * @throws {Error} unsupported action for this data type + */ + setVia(path, value) { + validateString(path); + setValueViaPath.call(this, this.object, path, value); + return this; + } + + /** + * Delete Via Path + * + * @param {string} path + * @returns {Pathfinder} + * @since 1.6.0 + * @throws {TypeError} unsupported type + * @throws {TypeError} value is not a string + * @throws {TypeError} value is not an integer + * @throws {Error} unsupported action for this data type + */ + deleteVia(path) { + validateString(path); + deleteValueViaPath.call(this, this.object, path); + return this; + } + + /** + * + * @param {string} path + * @return {bool} + * @throws {TypeError} unsupported type + * @throws {TypeError} value is not a string + * @throws {TypeError} value is not an integer + * @since 1.4.0 + */ + exists(path) { + validateString(path); + try { + getValueViaPath.call(this, this.object, path, true); + return true; + } catch (e) { + + } + + return false; + } + +} + + +/** + * + * @param {*} subject + * @param {string} path + * @param {string} check + * @return {Map} + * @throws {TypeError} unsupported type + * @throws {Error} the journey is not at its end + * @throws {Error} unsupported action for this data type + * @private + */ +function iterate(subject, path, check) { + + const result = new Map; + + if (isObject(subject) || isArray(subject)) { + for (const [key, value] of Object.entries(subject)) { + result.set(key, getValueViaPath.call(this, value, path, check)) + } + } else { + let key = path.split(DELIMITER).shift(); + result.set(key, getValueViaPath.call(this, subject, path, check)); + } + + return result; + + +} + +/** + * + * @param {*} subject + * @param [string} path + * @param [boolean} check + * @returns {*} + * @throws {TypeError} unsupported type + * @throws {Error} the journey is not at its end + * @throws {Error} unsupported action for this data type + * @private + */ +function getValueViaPath(subject, path, check) { + + if (path === "") { + return subject; + } + + let parts = path.split(DELIMITER) + let current = parts.shift(); + + if (current === this.wildCard) { + return iterate.call(this, subject, parts.join(DELIMITER), check); + } + + if (isObject(subject) || isArray(subject)) { + + let anchor; + if (subject instanceof Map || subject instanceof WeakMap) { + anchor = subject.get(current); + + } else if (subject instanceof Set || subject instanceof WeakSet) { + current = parseInt(current); + validateInteger(current) + anchor = [...subject]?.[current]; + + } else if (typeof WeakRef === 'function' && subject instanceof WeakRef) { + throw Error('unsupported action for this data type'); + + } else if (isArray(subject)) { + current = parseInt(current); + validateInteger(current) + anchor = subject?.[current]; + } else { + anchor = subject?.[current]; + } + + if (isObject(anchor) || isArray(anchor)) { + return getValueViaPath.call(this, anchor, parts.join(DELIMITER), check) + } + + if (parts.length > 0) { + throw Error("the journey is not at its end (" + parts.join(DELIMITER) + ")"); + } + + + if (check === true) { + const descriptor = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(subject), current); + + if (!subject.hasOwnProperty(current) && descriptor === undefined) { + throw Error('unknown value'); + } + + } + + return anchor; + + } + + throw TypeError("unsupported type " + typeof subject) + +} + +/** + * + * @param object + * @param path + * @param value + * @returns {void} + * @throws {TypeError} unsupported type + * @throws {TypeError} unsupported type + * @throws {Error} the journey is not at its end + * @throws {Error} unsupported action for this data type + * @private + */ +function setValueViaPath(object, path, value) { + + validateString(path); + + let parts = path.split(DELIMITER) + let last = parts.pop(); + let subpath = parts.join(DELIMITER); + + let stack = new Stack() + let current = subpath; + while (true) { + + try { + getValueViaPath.call(this, object, current, true) + break; + } catch (e) { + + } + + stack.push(current); + parts.pop(); + current = parts.join(DELIMITER); + + if (current === "") break; + } + + while (!stack.isEmpty()) { + current = stack.pop(); + let obj = {}; + + if (!stack.isEmpty()) { + let n = stack.peek().split(DELIMITER).pop(); + if (isInteger(parseInt(n))) { + obj = []; + } + + } + + setValueViaPath.call(this, object, current, obj); + } + + let anchor = getValueViaPath.call(this, object, subpath); + + if (!isObject(object) && !isArray(object)) { + throw TypeError("unsupported type: " + typeof object); + } + + if (anchor instanceof Map || anchor instanceof WeakMap) { + anchor.set(last, value); + } else if (anchor instanceof Set || anchor instanceof WeakSet) { + anchor.append(value) + + } else if (typeof WeakRef === 'function' && anchor instanceof WeakRef) { + throw Error('unsupported action for this data type'); + + } else if (isArray(anchor)) { + last = parseInt(last); + validateInteger(last) + assignProperty(anchor, last, value); + } else { + assignProperty(anchor, last, value); + } + + +} + +/** + * @private + * @param {object} object + * @param {string} key + * @param {*} value + */ +function assignProperty(object, key, value) { + + if (!object.hasOwnProperty(key)) { + object[key] = value; + return; + } + + if (value === undefined) { + delete object[key]; + } + + object[key] = value; + +} + +/** + * + * @param object + * @param path + * @returns {void} + * @throws {TypeError} unsupported type + * @throws {TypeError} unsupported type + * @throws {Error} the journey is not at its end + * @throws {Error} unsupported action for this data type + * @since 1.6.0 + * @private + */ +function deleteValueViaPath(object, path) { + + const parts = path.split(DELIMITER) + let last = parts.pop(); + const subpath = parts.join(DELIMITER); + + const anchor = getValueViaPath.call(this, object, subpath); + + if (anchor instanceof Map) { + anchor.delete(last); + } else if (anchor instanceof Set || anchor instanceof WeakMap || anchor instanceof WeakSet || (typeof WeakRef === 'function' && anchor instanceof WeakRef)) { + throw Error('unsupported action for this data type'); + + } else if (isArray(anchor)) { + last = parseInt(last); + validateInteger(last) + delete anchor[last]; + } else { + delete anchor[last]; + } + + +} +'use strict'; + +/** + * @author schukai GmbH + */ + + +import {isArray, isObject} from "../types/is.mjs"; +import {typeOf} from "../types/typeof.mjs"; + +export {diff} + +/** + * With the diff function you can perform the change of one object to another. The result shows the changes of the second object to the first object. + * + * The operator `add` means that something has been added to the second object. `delete` means that something has been deleted from the second object compared to the first object. + * + * ``` + * <script type="module"> + * import {Diff} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/diff.mjs'; + * Diff(a, b) + * </script> + * ``` + * + * @example + * + * import {Diff} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/diff.mjs'; + * + * // given are two objects x and y. + * + * let x = { + * a: 1, + * b: "Hello!" + * } + * + * let y = { + * a: 2, + * c: true + * } + * + * // These two objects can be compared with each other. + * + * console.log(Diff(x, y)); + * + * // the result is then the following + * + * // + * // [ + * // { + * // operator: 'update', + * // path: [ 'a' ], + * // first: { value: 1, type: 'number' }, + * // second: { value: 2, type: 'number' } + * // }, + * // { + * // operator: 'delete', + * // path: [ 'b' ], + * // first: { value: 'Hello!', type: 'string' } + * // }, + * // { + * // operator: 'add', + * // path: [ 'c' ], + * // second: { value: true, type: 'boolean' } + * // } + * // ] + * + * @param {*} first + * @param {*} second + * @return {array} + * @since 1.6.0 + * @copyright schukai GmbH + * @memberOf Monster.Data + */ +function diff(first, second) { + return doDiff(first, second) +} + +/** + * @private + * @param a + * @param b + * @param type + * @return {Set<string>|Set<number>} + */ +function getKeys(a, b, type) { + if (isArray(type)) { + const keys = a.length > b.length ? new Array(a.length) : new Array(b.length); + keys.fill(0); + return new Set(keys.map((_, i) => i)); + } + + return new Set(Object.keys(a).concat(Object.keys(b))); +} + +/** + * @private + * @param a + * @param b + * @param path + * @param diff + * @return {array} + */ +function doDiff(a, b, path, diff) { + + let typeA = typeOf(a) + let typeB = typeOf(b) + + const currPath = path || []; + const currDiff = diff || []; + + if (typeA === typeB && (typeA === 'object' || typeA ==='array')) { + + getKeys(a, b, typeA).forEach((v) => { + + if (!(Object.prototype.hasOwnProperty.call(a, v))) { + currDiff.push(buildResult(a[v], b[v], 'add', currPath.concat(v))); + } else if (!(Object.prototype.hasOwnProperty.call(b, v))) { + currDiff.push(buildResult(a[v], b[v], 'delete', currPath.concat(v))); + } else { + doDiff(a[v], b[v], currPath.concat(v), currDiff); + } + }); + + } else { + + const o = getOperator(a, b, typeA, typeB); + if (o !== undefined) { + currDiff.push(buildResult(a, b, o, path)); + } + + } + + return currDiff; + +} + +/** + * + * @param {*} a + * @param {*} b + * @param {string} operator + * @param {array} path + * @return {{path: array, operator: string}} + * @private + */ +function buildResult(a, b, operator, path) { + + const result = { + operator, + path, + }; + + if (operator !== 'add') { + result.first = { + value: a, + type: typeof a + }; + + if (isObject(a)) { + const name = Object.getPrototypeOf(a)?.constructor?.name; + if (name !== undefined) { + result.first.instance = name; + } + } + } + + if (operator === 'add' || operator === 'update') { + result.second = { + value: b, + type: typeof b + }; + + if (isObject(b)) { + const name = Object.getPrototypeOf(b)?.constructor?.name; + if (name !== undefined) { + result.second.instance = name; + } + } + + } + + return result; +} + +/** + * @private + * @param {*} a + * @param {*} b + * @return {boolean} + */ +function isNotEqual(a, b) { + + if (typeof a !== typeof b) { + return true; + } + + if (a instanceof Date && b instanceof Date) { + return a.getTime() !== b.getTime(); + } + + return a !== b; +} + +/** + * @private + * @param {*} a + * @param {*} b + * @return {string|undefined} + */ +function getOperator(a, b) { + + /** + * @type {string|undefined} + */ + let operator; + + /** + * @type {string} + */ + let typeA = typeof a; + + /** + * @type {string} + */ + let typeB = typeof b; + + if (typeA === 'undefined' && typeB !== 'undefined') { + operator = 'add'; + } else if (typeA !== 'undefined' && typeB === 'undefined') { + operator = 'delete'; + } else if (isNotEqual(a, b)) { + operator = 'update'; + } + + return operator; + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../../constants.mjs"; +import {isObject} from "../../types/is.mjs"; +import {Datasource} from "../datasource.mjs"; +import {Pathfinder} from "../pathfinder.mjs"; +import {Pipe} from "../pipe.mjs"; +import {WriteError} from "./restapi/writeerror.mjs"; + +export {RestAPI} + +/** + * The RestAPI is a class that enables a REST API server. + * + * ``` + * <script type="module"> + * import {RestAPI} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/datasource/restapi.mjs'; + * new RestAPI() + * </script> + * ``` + * + * @example + * + * import {RestAPI} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/datasource/restapi.mjs'; + * + * const ds = new RestAPI({ + * url: 'https://httpbin.org/get' + * },{ + * url: 'https://httpbin.org/post' + * }); + * + * ds.set({flag:true}) + * ds.write().then(()=>console.log('done')); + * ds.read().then(()=>console.log('done')); + * + * @since 1.22.0 + * @copyright schukai GmbH + * @memberOf Monster.Data.Datasource + * @summary The LocalStorage class encapsulates the access to data objects. + */ +class RestAPI extends Datasource { + + /** + * + * @param {Object} [readDefinition] An options object containing any custom settings that you want to apply to the read request. + * @param {Object} [writeDefinition] An options object containing any custom settings that you want to apply to the write request. + * @throws {TypeError} value is not a string + */ + constructor(readDefinition, writeDefinition) { + super(); + + const options = {} + + if (isObject(readDefinition)) options.read = readDefinition; + if (isObject(writeDefinition)) options.write = writeDefinition; + + this.setOptions(options); + + } + + /** + * @property {string} url=undefined Defines the resource that you wish to fetch. + * @property {Object} write={} Options + * @property {Object} write.init={} An options object containing any custom settings that you want to apply to the request. The parameters are identical to those of the {@link https://developer.mozilla.org/en-US/docs/Web/API/Request/Request|Request constructor} + * @property {string} write.init.method=POST + * @property {string} write.acceptedStatus=[200,201] + * @property {string} write.url URL + * @property {Object} write.mapping the mapping is applied before writing. + * @property {String} write.mapping.transformer Transformer to select the appropriate entries + * @property {Object} write.report + * @property {String} write.report.path Path to validations + * @property {Monster.Data.Datasource~exampleCallback[]} write.mapping.callback with the help of the callback, the structures can be adjusted before writing. + * @property {Object} read.init={} An options object containing any custom settings that you want to apply to the request. The parameters are identical to those of the {@link https://developer.mozilla.org/en-US/docs/Web/API/Request/Request|Request constructor} + * @property {string} read.init.method=GET + * @property {string} read.acceptedStatus=[200] + * @property {string} read.url URL + * @property {Object} read.mapping the mapping is applied after reading. + * @property {String} read.mapping.transformer Transformer to select the appropriate entries + * @property {Monster.Data.Datasource~exampleCallback[]} read.mapping.callback with the help of the callback, the structures can be adjusted after reading. + */ + get defaults() { + return Object.assign({}, super.defaults, { + write: { + init: { + method: 'POST', + }, + acceptedStatus: [200, 201], + url: undefined, + mapping: { + transformer: undefined, + callbacks: [] + }, + report: { + path: undefined + } + }, + read: { + init: { + method: 'GET' + }, + acceptedStatus: [200], + url: undefined, + mapping: { + transformer: undefined, + callbacks: [] + }, + }, + + }); + } + + /** + * @return {Promise} + * @throws {Error} the options does not contain a valid json definition + * @throws {TypeError} value is not a object + * @throws {Error} the data cannot be read + */ + read() { + const self = this; + let response; + + let init = self.getOption('read.init'); + if (!isObject(init)) init = {}; + + return fetch(self.getOption('read.url'), init).then(resp => { + response = resp; + + const acceptedStatus = self.getOption('read.acceptedStatus', [200]); + + if (acceptedStatus.indexOf(resp.status) === -1) { + throw Error('the data cannot be read (response ' + resp.status + ')') + } + + return resp.text() + }).then(body => { + + let obj; + + try { + obj = JSON.parse(body); + + } catch (e) { + + if (body.length > 100) { + body = body.substring(0, 97) + '...'; + } + + throw new Error('the response does not contain a valid json (actual: ' + body + ').'); + } + + let transformation = self.getOption('read.mapping.transformer'); + if (transformation !== undefined) { + const pipe = new Pipe(transformation); + obj = pipe.run(obj); + } + + self.set(obj); + return response; + }) + } + + /** + * @return {Promise} + * @throws {WriteError} the data cannot be written + */ + write() { + const self = this; + + + let init = self.getOption('write.init'); + if (!isObject(init)) init = {}; + if (typeof init['headers'] !== 'object') { + init['headers'] = { + 'Content-Type': 'application/json' + } + } + + let obj = self.get(); + let transformation = self.getOption('write.mapping.transformer'); + if (transformation !== undefined) { + const pipe = new Pipe(transformation); + obj = pipe.run(obj); + } + + let sheathingObject = self.getOption('write.sheathing.object'); + let sheathingPath = self.getOption('write.sheathing.path'); + let reportPath = self.getOption('write.report.path'); + + if (sheathingObject && sheathingPath) { + const sub = obj; + obj = sheathingObject; + (new Pathfinder(obj)).setVia(sheathingPath, sub); + } + + init['body'] = JSON.stringify(obj); + + return fetch(self.getOption('write.url'), init).then(response => { + + const acceptedStatus = self.getOption('write.acceptedStatus', [200, 2001]); + + if (acceptedStatus.indexOf(response.status) === -1) { + + return response.text().then((body) => { + + let obj, validation; + try { + obj = JSON.parse(body); + validation = new Pathfinder(obj).getVia(reportPath) + + } catch (e) { + + if (body.length > 100) { + body = body.substring(0, 97) + '...'; + } + + throw new Error('the response does not contain a valid json (actual: ' + body + ').'); + } + + throw new WriteError('the data cannot be written (response ' + response.status + ')', response, validation) + + }) + + + } + + return response; + }); + } + + + /** + * @return {RestAPI} + */ + getClone() { + const self = this; + return new RestAPI(self[internalSymbol].getRealSubject()['options'].read, self[internalSymbol].getRealSubject()['options'].write); + } + +} + + +/** + * This callback can be passed to a datasource and is used to adapt data structures. + * + * @callback Monster.Data.Datasource~exampleCallback + * @param {*} value Value + * @param {string} key Key + * @memberOf Monster.Data + * @see Monster.Data.Datasource + */ +'use strict'; + +/** + * Namespace for datasources + * + * @namespace Monster.Data.Datasource + * @memberOf Monster.Data + * @author schukai GmbH + */ +const ns = {};'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../../../constants.mjs"; +import {getGlobalObject} from "../../../types/global.mjs"; +import {Datasource} from "../../datasource.mjs"; +import {Storage, storageObjectSymbol} from "../storage.mjs"; + +export {SessionStorage} + +/** + * The SessionStorage class provides a data source that uses the SessionStorage API on the client. + * + * ``` + * <script type="module"> + * import {SessionStorage} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/datasource/storage/sessionstorage.mjs'; + * new SessionStorage() + * </script> + * ``` + * + * @since 1.22.0 + * @copyright schukai GmbH + * @memberOf Monster.Data.Datasource.Storage + * @summary The LocalStorage class encapsulates the access to data objects. + */ +class SessionStorage extends Storage { + + /** + * @throws {Error} this method must be implemented by derived classes. + * @return {external:sessionStorage} + * @private + */ + [storageObjectSymbol]() { + return getGlobalObject('sessionStorage'); + } + + /** + * Create Clone + * + * @return {SessionStorage} + */ + getClone() { + const self = this; + return new SessionStorage(self[internalSymbol].getRealSubject()['options'].key); + } + +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../../../constants.mjs"; +import {getGlobalObject} from "../../../types/global.mjs"; +import {Datasource} from "../../datasource.mjs"; +import {Storage, storageObjectSymbol} from "../storage.mjs"; + +export {LocalStorage} + +/** + * The LocalStorage Datasource provides a data store in the browser localStorage. + * + * ``` + * <script type="module"> + * import {LocalStorage} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/datasource/storage/localstorage.mjs'; + * new LocalStorage() + * </script> + * ``` + * + * @since 1.22.0 + * @copyright schukai GmbH + * @memberOf Monster.Data.Datasource.Storage + * @summary The LocalStorage class encapsulates the access to data objects. + */ +class LocalStorage extends Storage { + + /** + * @throws {Error} this method must be implemented by derived classes. + * @return {external:localStorage} + * @private + */ + [storageObjectSymbol]() { + return getGlobalObject('localStorage'); + } + + /** + * Create clone + * @return {LocalStorage} + */ + getClone() { + const self = this; + return new LocalStorage(self[internalSymbol].getRealSubject()['options'].key); + } + + +} +'use strict'; + +/** + * Namespace for storages + * + * @namespace Monster.Data.Datasource.Storage + * @memberOf Monster.Data.Datasource + * @author schukai GmbH + */ +const ns = {}; +'use strict'; + +/** + * Namespace for storages + * + * @namespace Monster.Data.Datasource.RestAPI + * @memberOf Monster.Data.Datasource + * @author schukai GmbH + */ +const ns = {}; +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../../../constants.mjs"; + +export {WriteError} + +/** + * Error message for API requests with extension of request and validation. + * + * @since 1.24.0 + * @copyright schukai GmbH + * @memberOf Monster.Data.Datasource.RestAPI + * @summary the error is thrown by the rest api in case of error + */ +class WriteError extends Error { + /** + * + * @param {string} message + * @param {Response} response + */ + constructor(message, response, validation) { + super(message); + this[internalSymbol] = { + response: response, + validation: validation + }; + } + + /** + * @return {Response} + */ + getResponse() { + return this[internalSymbol]['response'] + } + + /** + * @return {Object} + */ + getValidation() { + return this[internalSymbol]['validation'] + } +} +'use strict'; + +/** + * @author schukai GmbH + */ + +import {internalSymbol} from "../../constants.mjs"; +import {validateString} from "../../types/validate.mjs"; +import {Datasource} from "../datasource.mjs"; + +export {Storage, storageObjectSymbol} + +/** + * @private + * @type {symbol} + */ +const storageObjectSymbol = Symbol('storageObject'); + +/** + * The class represents a record. + * + * ``` + * <script type="module"> + * import {Storage} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/datasource/storage.mjs'; + * new Storage() + * </script> + * ``` + * + * @example + * + * import {Storage} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/data/datasource/storage.mjs'; + * + * new Datasource(); + * + * @since 1.22.0 + * @copyright schukai GmbH + * @memberOf Monster.Data.Datasource + * @summary The Storage class encapsulates the access to data objects over WebStorageAPI. + */ +class Storage extends Datasource { + + /** + * + * @param {string} key LocalStorage Key + * @throws {TypeError} value is not a string + */ + constructor(key) { + super(); + this.setOption('key', validateString(key)); + } + + /** + * @property {string} key=undefined LocalStorage Key + */ + get defaults() { + return Object.assign({}, super.defaults, { + key: undefined, + }); + } + + /** + * @throws {Error} this method must be implemented by derived classes. + * @return {external:Storage} + * @private + */ + [storageObjectSymbol]() { + throw new Error("this method must be implemented by derived classes") + } + + /** + * @return {Promise} + * @throws {Error} the options does not contain a valid json definition + * @throws {TypeError} value is not a object + * @throws {Error} the data cannot be read + */ + read() { + const self = this; + + const storage = self[storageObjectSymbol](); + + return new Promise(function (resolve) { + const data = JSON.parse(storage.getItem(self.getOption('key'))); + self.set(data??{}); + resolve(); + }) + + } + + /** + * @return {Storage} + * @throws {Error} the data cannot be written + */ + write() { + const self = this; + + const storage = self[storageObjectSymbol](); + + return new Promise(function (resolve) { + + const data = self.get(); + if (data === undefined) { + storage.removeItem(self.getOption('key')); + } else { + storage.setItem(self.getOption('key'), JSON.stringify(data)); + } + + resolve(); + }) + } + + /** + * @return {Storage} + */ + getClone() { + const self=this; + return new Storage(self[internalSymbol].getRealSubject()['options'].key); + } + +} +'use strict'; + +/** + * @author schukai GmbH + */ + + +import {getGlobal} from '../types/global.mjs'; + +export {random} + +/** + * this function uses crypt and returns a random number. + * + * ``` + * <script type="module"> + * import {random} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@latest/source/math/random.mjs'; + * random(1,10) + * // ↦ 5 + * </script> + * ``` + * + * @param {number} min starting value of the definition set (default is 0) + * @param {number} max end value of the definition set (default is 1000000000) + * @returns {number} + * @memberOf Monster.Math + * @throws {Error} missing crypt + * @throws {Error} we cannot generate numbers larger than 53 bits. + * @throws {Error} the distance is too small to create a random number. + + * @since 1.0.0 + * @copyright schukai GmbH + */ + function random(min, max) { + + if (min === undefined) { + min = 0; + } + if (max === undefined) { + max = MAX; + } + + if (max < min) { + throw new Error("max must be greater than min"); + } + + return Math.round(create(min, max)); + +} + +/** + * @private + * @type {number} + */ +var MAX = 1000000000; + + +Math.log2 = Math.log2 || function (n) { + return Math.log(n) / Math.log(2); +}; + +/** + * + * @param {number} min + * @param {number} max + * @returns {number} + * @private + * @throws {Error} missing crypt + * @throws {Error} we cannot generate numbers larger than 53 bits. + * @throws {Error} the distance is too small to create a random number. + */ +function create(min, max) { + let crypt; + let globalReference = getGlobal(); + + crypt = globalReference?.['crypto'] || globalReference?.['msCrypto'] || globalReference?.['crypto'] || undefined; + + if (typeof crypt === "undefined") { + throw new Error("missing crypt") + } + + let rval = 0; + const range = max - min; + if (range < 2) { + throw new Error('the distance is too small to create a random number.') + } + + const bitsNeeded = Math.ceil(Math.log2(range)); + if (bitsNeeded > 53) { + throw new Error("we cannot generate numbers larger than 53 bits."); + } + const bytesNeeded = Math.ceil(bitsNeeded / 8); + const mask = Math.pow(2, bitsNeeded) - 1; + + const byteArray = new Uint8Array(bytesNeeded); + crypt.getRandomValues(byteArray); + + let p = (bytesNeeded - 1) * 8; + for (var i = 0; i < bytesNeeded; i++) { + rval += byteArray[i] * Math.pow(2, p); + p -= 8; + } + + rval = rval & mask; + + if (rval >= range) { + return create(min, max); + } + + if (rval < min) { + rval += min; + } + + return rval; + +} +'use strict'; + +/** + * Namespace for math. + * + * @namespace Monster.Math + * @memberOf Monster + * @author schukai GmbH + */ +const ns = {}; \ No newline at end of file diff --git a/deployment/package.json b/development/package.json similarity index 74% rename from deployment/package.json rename to development/package.json index 22f4e1ba3a2824df28c0d15f5e7f46a1e74b7d71..ba8eb253d9b8a20fa511835d67a11ec54eaadc4d 100644 --- a/deployment/package.json +++ b/development/package.json @@ -6,6 +6,11 @@ "type": "git", "url": "https://gitlab.schukai.com/oss/libraries/javascript/monster.git" }, + "scripts": { + "build": "echo \"Error: 'build' is not a supported command.\" && exit 0", + "test": "npx mocha --recursive test/cases/", + "web-test": "script/web-test.sh" + }, "type": "module", "keywords": [], "author": "schukai GmbH", @@ -14,11 +19,13 @@ "@peculiar/webcrypto": "^1.4.0", "btoa": "^1.2.1", "c8": "^7.12.0", - "esbuild": "^0.14.53", "chai": "^4.3.6", "chai-dom": "^1.11.0", "clean-jsdoc-theme": "^4.1.6", + "create-polyfill-service-url": "^2.2.6", "crypt": "^0.0.2", + "es6-fix": "link:@pixi/jsdoc-template/plugins/es6-fix", + "esbuild": "^0.14.53", "flow-bin": "^0.184.0", "fs": "^0.0.1-security", "jsdoc": "^3.6.11", diff --git a/deployment/pnpm-lock.yaml b/development/pnpm-lock.yaml similarity index 75% rename from deployment/pnpm-lock.yaml rename to development/pnpm-lock.yaml index 6059a509fb0caa69461a11b224ba26982f472978..114b4bec25d42f24930b781da6a37cce6f838c2b 100644 --- a/deployment/pnpm-lock.yaml +++ b/development/pnpm-lock.yaml @@ -7,7 +7,9 @@ specifiers: chai: ^4.3.6 chai-dom: ^1.11.0 clean-jsdoc-theme: ^4.1.6 + create-polyfill-service-url: ^2.2.6 crypt: ^0.0.2 + es6-fix: link:@pixi/jsdoc-template/plugins/es6-fix esbuild: ^0.14.53 flow-bin: ^0.184.0 fs: ^0.0.1-security @@ -29,7 +31,9 @@ devDependencies: chai: 4.3.6 chai-dom: 1.11.0_chai@4.3.6+mocha@10.0.0 clean-jsdoc-theme: 4.1.7_jsdoc@3.6.11 + create-polyfill-service-url: 2.2.6 crypt: 0.0.2 + es6-fix: link:@pixi/jsdoc-template/plugins/es6-fix esbuild: 0.14.53 flow-bin: 0.184.0 fs: 0.0.1-security @@ -46,6 +50,128 @@ devDependencies: packages: + /@ampproject/remapping/2.2.0: + resolution: {integrity: sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/gen-mapping': 0.1.1 + '@jridgewell/trace-mapping': 0.3.14 + dev: true + + /@babel/code-frame/7.18.6: + resolution: {integrity: sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/highlight': 7.18.6 + dev: true + + /@babel/compat-data/7.18.8: + resolution: {integrity: sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/core/7.18.10: + resolution: {integrity: sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==} + engines: {node: '>=6.9.0'} + dependencies: + '@ampproject/remapping': 2.2.0 + '@babel/code-frame': 7.18.6 + '@babel/generator': 7.18.12 + '@babel/helper-compilation-targets': 7.18.9_@babel+core@7.18.10 + '@babel/helper-module-transforms': 7.18.9 + '@babel/helpers': 7.18.9 + '@babel/parser': 7.18.11 + '@babel/template': 7.18.10 + '@babel/traverse': 7.18.11 + '@babel/types': 7.18.10 + convert-source-map: 1.8.0 + debug: 4.3.4 + gensync: 1.0.0-beta.2 + json5: 2.2.1 + semver: 6.3.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/generator/7.18.12: + resolution: {integrity: sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.18.10 + '@jridgewell/gen-mapping': 0.3.2 + jsesc: 2.5.2 + dev: true + + /@babel/helper-compilation-targets/7.18.9_@babel+core@7.18.10: + resolution: {integrity: sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/compat-data': 7.18.8 + '@babel/core': 7.18.10 + '@babel/helper-validator-option': 7.18.6 + browserslist: 4.21.3 + semver: 6.3.0 + dev: true + + /@babel/helper-environment-visitor/7.18.9: + resolution: {integrity: sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-function-name/7.18.9: + resolution: {integrity: sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.18.10 + '@babel/types': 7.18.10 + dev: true + + /@babel/helper-hoist-variables/7.18.6: + resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.18.10 + dev: true + + /@babel/helper-module-imports/7.18.6: + resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.18.10 + dev: true + + /@babel/helper-module-transforms/7.18.9: + resolution: {integrity: sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-module-imports': 7.18.6 + '@babel/helper-simple-access': 7.18.6 + '@babel/helper-split-export-declaration': 7.18.6 + '@babel/helper-validator-identifier': 7.18.6 + '@babel/template': 7.18.10 + '@babel/traverse': 7.18.11 + '@babel/types': 7.18.10 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/helper-simple-access/7.18.6: + resolution: {integrity: sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.18.10 + dev: true + + /@babel/helper-split-export-declaration/7.18.6: + resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.18.10 + dev: true + /@babel/helper-string-parser/7.18.10: resolution: {integrity: sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==} engines: {node: '>=6.9.0'} @@ -56,6 +182,31 @@ packages: engines: {node: '>=6.9.0'} dev: true + /@babel/helper-validator-option/7.18.6: + resolution: {integrity: sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helpers/7.18.9: + resolution: {integrity: sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.18.10 + '@babel/traverse': 7.18.11 + '@babel/types': 7.18.10 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/highlight/7.18.6: + resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.18.6 + chalk: 2.4.2 + js-tokens: 4.0.0 + dev: true + /@babel/parser/7.18.11: resolution: {integrity: sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ==} engines: {node: '>=6.0.0'} @@ -64,6 +215,33 @@ packages: '@babel/types': 7.18.10 dev: true + /@babel/template/7.18.10: + resolution: {integrity: sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.18.6 + '@babel/parser': 7.18.11 + '@babel/types': 7.18.10 + dev: true + + /@babel/traverse/7.18.11: + resolution: {integrity: sha512-TG9PiM2R/cWCAy6BPJKeHzNbu4lPzOSZpeMfeNErskGpTJx6trEvFaVCbDvpcxwy49BKWmEPwiW8mrysNiDvIQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.18.6 + '@babel/generator': 7.18.12 + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-function-name': 7.18.9 + '@babel/helper-hoist-variables': 7.18.6 + '@babel/helper-split-export-declaration': 7.18.6 + '@babel/parser': 7.18.11 + '@babel/types': 7.18.10 + debug: 4.3.4 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/types/7.18.10: resolution: {integrity: sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ==} engines: {node: '>=6.9.0'} @@ -86,16 +264,61 @@ packages: dev: true optional: true + /@financial-times/js-features-analyser/0.0.4: + resolution: {integrity: sha512-AouQ5I0a9TeRdHSN093WX5yz3ZlMbRyQ1xHn7K4OmXcbsRnS96Biy5Q2sTkqLwwTouAk/SNeWIjzrDZFQHp9Mw==} + hasBin: true + dependencies: + '@babel/core': 7.18.10 + globals: 11.12.0 + yargs: 13.3.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@financial-times/polyfill-useragent-normaliser/1.10.2: + resolution: {integrity: sha512-/9xHARfrKdWHt1ZXoT+/GpKx2N7uX88U1m6tF61AYSGaJFYaFlSoL1I4WbQOGH4eTQVb1z0a9LfwXaWblpRTBg==} + engines: {node: '>=8'} + dependencies: + '@financial-times/useragent_parser': 1.6.3 + semver: 7.3.7 + dev: true + + /@financial-times/useragent_parser/1.6.3: + resolution: {integrity: sha512-TlQiXt/vS5ZwY0V3salvlyQzIzMGZEyw9inmJA25A8heL2kBVENbToiEc64R6ETNf5YHa2lwnc2I7iNHP9SqeQ==} + dev: true + /@istanbuljs/schema/0.1.3: resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} engines: {node: '>=8'} dev: true + /@jridgewell/gen-mapping/0.1.1: + resolution: {integrity: sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/set-array': 1.1.2 + '@jridgewell/sourcemap-codec': 1.4.14 + dev: true + + /@jridgewell/gen-mapping/0.3.2: + resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/set-array': 1.1.2 + '@jridgewell/sourcemap-codec': 1.4.14 + '@jridgewell/trace-mapping': 0.3.14 + dev: true + /@jridgewell/resolve-uri/3.1.0: resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} engines: {node: '>=6.0.0'} dev: true + /@jridgewell/set-array/1.1.2: + resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} + engines: {node: '>=6.0.0'} + dev: true + /@jridgewell/sourcemap-codec/1.4.14: resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} dev: true @@ -234,11 +457,23 @@ packages: engines: {node: '>=6'} dev: true + /ansi-regex/4.1.1: + resolution: {integrity: sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==} + engines: {node: '>=6'} + dev: true + /ansi-regex/5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} dev: true + /ansi-styles/3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + dependencies: + color-convert: 1.9.3 + dev: true + /ansi-styles/4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} @@ -326,6 +561,17 @@ packages: resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==} dev: true + /browserslist/4.21.3: + resolution: {integrity: sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + dependencies: + caniuse-lite: 1.0.30001374 + electron-to-chromium: 1.4.211 + node-releases: 2.0.6 + update-browserslist-db: 1.0.5_browserslist@4.21.3 + dev: true + /btoa/1.2.1: resolution: {integrity: sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==} engines: {node: '>= 0.4.0'} @@ -365,11 +611,20 @@ packages: upper-case: 1.1.3 dev: true + /camelcase/5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + dev: true + /camelcase/6.3.0: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} dev: true + /caniuse-lite/1.0.30001374: + resolution: {integrity: sha512-mWvzatRx3w+j5wx/mpFN5v5twlPrabG8NqX2c6e45LCpymdoGqNvRkRutFUqpRTXKFQFNQJasvK0YT7suW6/Hw==} + dev: true + /catharsis/0.9.0: resolution: {integrity: sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==} engines: {node: '>= 10'} @@ -401,6 +656,15 @@ packages: type-detect: 4.0.8 dev: true + /chalk/2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + dev: true + /chalk/4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -446,6 +710,22 @@ packages: nanoid: 3.3.4 dev: true + /cliui/5.0.0: + resolution: {integrity: sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==} + dependencies: + string-width: 3.1.0 + strip-ansi: 5.2.0 + wrap-ansi: 5.1.0 + dev: true + + /cliui/6.0.0: + resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + dev: true + /cliui/7.0.4: resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} dependencies: @@ -454,6 +734,12 @@ packages: wrap-ansi: 7.0.0 dev: true + /color-convert/1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + dependencies: + color-name: 1.1.3 + dev: true + /color-convert/2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -461,6 +747,10 @@ packages: color-name: 1.1.4 dev: true + /color-name/1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + dev: true + /color-name/1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} dev: true @@ -486,6 +776,26 @@ packages: safe-buffer: 5.1.2 dev: true + /core-util-is/1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + dev: true + + /create-polyfill-service-url/2.2.6: + resolution: {integrity: sha512-uhipnEEH6SkC176Za0Rt3D7pTV60nTiS2Vn4wZBIQacUtfPBxuTG4dox/jy2yXDCEiFX62gYhehvCIYPudjzFA==} + hasBin: true + dependencies: + '@babel/core': 7.18.10 + '@financial-times/js-features-analyser': 0.0.4 + browserslist: 4.21.3 + execa: 4.1.0 + polyfill-library: 3.111.0 + semver: 7.3.7 + snyk: 1.984.0 + yargs: 15.4.1 + transitivePeerDependencies: + - supports-color + dev: true + /cross-spawn/7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} @@ -553,6 +863,11 @@ packages: supports-color: 8.1.1 dev: true + /decamelize/1.2.0: + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} + dev: true + /decamelize/4.0.0: resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==} engines: {node: '>=10'} @@ -603,10 +918,24 @@ packages: webidl-conversions: 7.0.0 dev: true + /electron-to-chromium/1.4.211: + resolution: {integrity: sha512-BZSbMpyFQU0KBJ1JG26XGeFI3i4op+qOYGxftmZXFZoHkhLgsSv4DHDJfl8ogII3hIuzGt51PaZ195OVu0yJ9A==} + dev: true + + /emoji-regex/7.0.3: + resolution: {integrity: sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==} + dev: true + /emoji-regex/8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} dev: true + /end-of-stream/1.4.4: + resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + dependencies: + once: 1.4.0 + dev: true + /entities/2.1.0: resolution: {integrity: sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==} dev: true @@ -868,6 +1197,11 @@ packages: engines: {node: '>=6'} dev: true + /escape-string-regexp/1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + dev: true + /escape-string-regexp/2.0.0: resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} engines: {node: '>=8'} @@ -912,6 +1246,21 @@ packages: engines: {node: '>=6'} dev: true + /execa/4.1.0: + resolution: {integrity: sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==} + engines: {node: '>=10'} + dependencies: + cross-spawn: 7.0.3 + get-stream: 5.2.0 + human-signals: 1.1.1 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + dev: true + /fast-levenshtein/2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} dev: true @@ -933,6 +1282,21 @@ packages: to-regex-range: 5.0.1 dev: true + /find-up/3.0.0: + resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==} + engines: {node: '>=6'} + dependencies: + locate-path: 3.0.0 + dev: true + + /find-up/4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + dev: true + /find-up/5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} @@ -975,6 +1339,19 @@ packages: mime-types: 2.1.35 dev: true + /from2-string/1.1.0: + resolution: {integrity: sha512-m8vCh+KnXXXBtfF2VUbiYlQ+nczLcntB0BrtNgpmLkHylhObe9WF1b2LZjBBzrZzA6P4mkEla6ZYQoOUTG8cYA==} + dependencies: + from2: 2.3.0 + dev: true + + /from2/2.3.0: + resolution: {integrity: sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==} + dependencies: + inherits: 2.0.4 + readable-stream: 2.3.7 + dev: true + /fs-extra/10.1.0: resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} engines: {node: '>=12'} @@ -1028,6 +1405,11 @@ packages: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} dev: true + /gensync/1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + dev: true + /get-caller-file/2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} @@ -1045,6 +1427,13 @@ packages: has-symbols: 1.0.3 dev: true + /get-stream/5.2.0: + resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} + engines: {node: '>=8'} + dependencies: + pump: 3.0.0 + dev: true + /get-symbol-description/1.0.0: resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} engines: {node: '>= 0.4'} @@ -1082,6 +1471,11 @@ packages: path-is-absolute: 1.0.1 dev: true + /globals/11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + dev: true + /graceful-fs/4.2.10: resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} dev: true @@ -1090,6 +1484,11 @@ packages: resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} dev: true + /has-flag/3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + dev: true + /has-flag/4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} @@ -1171,6 +1570,11 @@ packages: - supports-color dev: true + /human-signals/1.1.1: + resolution: {integrity: sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==} + engines: {node: '>=8.12.0'} + dev: true + /iconv-lite/0.6.3: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} @@ -1244,6 +1648,11 @@ packages: engines: {node: '>=0.10.0'} dev: true + /is-fullwidth-code-point/2.0.0: + resolution: {integrity: sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==} + engines: {node: '>=4'} + dev: true + /is-fullwidth-code-point/3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} @@ -1303,6 +1712,11 @@ packages: call-bind: 1.0.2 dev: true + /is-stream/2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + dev: true + /is-string/1.0.7: resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} engines: {node: '>= 0.4'} @@ -1348,6 +1762,10 @@ packages: resolution: {integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==} dev: true + /isarray/1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + dev: true + /isexe/2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} dev: true @@ -1374,6 +1792,10 @@ packages: istanbul-lib-report: 3.0.0 dev: true + /js-tokens/4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + dev: true + /js-yaml/4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true @@ -1471,6 +1893,18 @@ packages: - utf-8-validate dev: true + /jsesc/2.5.2: + resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /json5/2.2.1: + resolution: {integrity: sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==} + engines: {node: '>=6'} + hasBin: true + dev: true + /jsonfile/6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} dependencies: @@ -1525,6 +1959,21 @@ packages: uc.micro: 1.0.6 dev: true + /locate-path/3.0.0: + resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} + engines: {node: '>=6'} + dependencies: + p-locate: 3.0.0 + path-exists: 3.0.0 + dev: true + + /locate-path/5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + dependencies: + p-locate: 4.1.0 + dev: true + /locate-path/6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -1558,6 +2007,13 @@ packages: resolution: {integrity: sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==} dev: true + /lru-cache/6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + dependencies: + yallist: 4.0.0 + dev: true + /make-dir/3.1.0: resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} engines: {node: '>=8'} @@ -1596,6 +2052,15 @@ packages: resolution: {integrity: sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==} dev: true + /merge-stream/2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + dev: true + + /merge2/1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: true + /mime-db/1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} @@ -1608,6 +2073,11 @@ packages: mime-db: 1.52.0 dev: true + /mimic-fn/2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + dev: true + /minimatch/3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} dependencies: @@ -1627,6 +2097,12 @@ packages: hasBin: true dev: true + /mnemonist/0.38.5: + resolution: {integrity: sha512-bZTFT5rrPKtPJxj8KSV0WkPyNxl72vQepqqVUAW2ARUpUSF2qXMB6jZj7hW5/k7C1rtpzqbD/IIbJwLXUjCHeg==} + dependencies: + obliterator: 2.0.4 + dev: true + /mocha/10.0.0: resolution: {integrity: sha512-0Wl+elVUD43Y0BqPZBzZt8Tnkw9CMUdNYnUsTfOM1vuhJVZL+kiesFYsqwBkEEuEixaiPe5ZQdqDgX2jddhmoA==} engines: {node: '>= 14.0.0'} @@ -1739,11 +2215,22 @@ packages: plantuml-encoder: 1.4.0 dev: true + /node-releases/2.0.6: + resolution: {integrity: sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==} + dev: true + /normalize-path/3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} dev: true + /npm-run-path/4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + dependencies: + path-key: 3.1.1 + dev: true + /nwsapi/2.2.1: resolution: {integrity: sha512-JYOWTeFoS0Z93587vRJgASD5Ut11fYl5NyihP3KrYBvMe1FRRs6RN7m20SA/16GM4P6hTnZjT+UmDOt38UeXNg==} dev: true @@ -1767,12 +2254,23 @@ packages: object-keys: 1.1.1 dev: true + /obliterator/2.0.4: + resolution: {integrity: sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==} + dev: true + /once/1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} dependencies: wrappy: 1.0.2 dev: true + /onetime/5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + dependencies: + mimic-fn: 2.1.0 + dev: true + /optionator/0.8.3: resolution: {integrity: sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==} engines: {node: '>= 0.8.0'} @@ -1785,6 +2283,13 @@ packages: word-wrap: 1.2.3 dev: true + /p-limit/2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + dependencies: + p-try: 2.2.0 + dev: true + /p-limit/3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} @@ -1792,6 +2297,20 @@ packages: yocto-queue: 0.1.0 dev: true + /p-locate/3.0.0: + resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==} + engines: {node: '>=6'} + dependencies: + p-limit: 2.3.0 + dev: true + + /p-locate/4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + dependencies: + p-limit: 2.3.0 + dev: true + /p-locate/5.0.0: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} @@ -1799,6 +2318,11 @@ packages: p-limit: 3.1.0 dev: true + /p-try/2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + dev: true + /param-case/2.1.1: resolution: {integrity: sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w==} dependencies: @@ -1811,6 +2335,11 @@ packages: entities: 4.3.1 dev: true + /path-exists/3.0.0: + resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} + engines: {node: '>=4'} + dev: true + /path-exists/4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -1836,6 +2365,10 @@ packages: resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} dev: true + /picocolors/1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + dev: true + /picomatch/2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} @@ -1845,15 +2378,44 @@ packages: resolution: {integrity: sha512-sxMwpDw/ySY1WB2CE3+IdMuEcWibJ72DDOsXLkSmEaSzwEUaYBT6DWgOfBiHGCux4q433X6+OEFWjlVqp7gL6g==} dev: true + /polyfill-library/3.111.0: + resolution: {integrity: sha512-BLh/1m/QPKrH3fEmIICpHHOVoYb9naWKImWkYuGoh26sHFiNO01arJ1KXEQMYLfVl4btpCoX/bRdqQY9kIR/eQ==} + engines: {node: '>=8'} + dependencies: + '@financial-times/polyfill-useragent-normaliser': 1.10.2 + from2-string: 1.1.0 + graceful-fs: 4.2.10 + merge2: 1.4.1 + mnemonist: 0.38.5 + stream-from-promise: 1.0.0 + stream-to-string: 1.2.0 + toposort: 2.0.2 + dev: true + /prelude-ls/1.1.2: resolution: {integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==} engines: {node: '>= 0.8.0'} dev: true + /process-nextick-args/2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + dev: true + + /promise-polyfill/1.1.6: + resolution: {integrity: sha512-7rrONfyLkDEc7OJ5QBkqa4KI4EBhCd340xRuIUPGCfu13znS+vx+VDdrT9ODAJHlXm7w4lbxN3DRjyv58EuzDg==} + dev: true + /psl/1.9.0: resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} dev: true + /pump/3.0.0: + resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} + dependencies: + end-of-stream: 1.4.4 + once: 1.4.0 + dev: true + /punycode/1.3.2: resolution: {integrity: sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==} dev: true @@ -1886,6 +2448,18 @@ packages: safe-buffer: 5.2.1 dev: true + /readable-stream/2.3.7: + resolution: {integrity: sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==} + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + dev: true + /readdirp/3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} @@ -1912,6 +2486,10 @@ packages: engines: {node: '>=0.10.0'} dev: true + /require-main-filename/2.0.0: + resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} + dev: true + /requizzle/0.2.3: resolution: {integrity: sha512-YanoyJjykPxGHii0fZP0uUPEXpvqfBDxWV7s6GKAiiOsiqhX6vHNyW3Qzdmqp/iq/ExbhaGbVrjB4ruEVSM4GQ==} dependencies: @@ -1949,12 +2527,24 @@ packages: hasBin: true dev: true + /semver/7.3.7: + resolution: {integrity: sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: true + /serialize-javascript/6.0.0: resolution: {integrity: sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==} dependencies: randombytes: 2.1.0 dev: true + /set-blocking/2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + dev: true + /shebang-command/2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -1990,11 +2580,37 @@ packages: supports-color: 7.2.0 dev: true + /snyk/1.984.0: + resolution: {integrity: sha512-e72gNXx6Epj1Tp8TEWoLJHEqkPTr370GvJQj/o73YlGFCILqfzFiFGq5cla8UffFM+mHEhuQDX4jAH53x2cqYA==} + engines: {node: '>=12'} + hasBin: true + dev: true + /source-map/0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} dev: true + /stream-from-promise/1.0.0: + resolution: {integrity: sha512-j84KLkudt+gr8KJ21RB02btPLx61uGbrLnewsWz6QKmsz8/c4ZFqXw6mJh5+G4oRN7DgDxdbjPxnpySpg1mUig==} + engines: {node: '>=0.10.0'} + dev: true + + /stream-to-string/1.2.0: + resolution: {integrity: sha512-8drZlFIKBHSMdX9GCWv8V9AAWnQcTqw0iAI6/GC7UJ0H0SwKeFKjOoZfGY1tOU00GGU7FYZQoJ/ZCUEoXhD7yQ==} + dependencies: + promise-polyfill: 1.1.6 + dev: true + + /string-width/3.1.0: + resolution: {integrity: sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==} + engines: {node: '>=6'} + dependencies: + emoji-regex: 7.0.3 + is-fullwidth-code-point: 2.0.0 + strip-ansi: 5.2.0 + dev: true + /string-width/4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -2020,6 +2636,19 @@ packages: es-abstract: 1.20.1 dev: true + /string_decoder/1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + dependencies: + safe-buffer: 5.1.2 + dev: true + + /strip-ansi/5.2.0: + resolution: {integrity: sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==} + engines: {node: '>=6'} + dependencies: + ansi-regex: 4.1.1 + dev: true + /strip-ansi/6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -2027,11 +2656,23 @@ packages: ansi-regex: 5.0.1 dev: true + /strip-final-newline/2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + dev: true + /strip-json-comments/3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} dev: true + /supports-color/5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + dependencies: + has-flag: 3.0.0 + dev: true + /supports-color/7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -2075,6 +2716,10 @@ packages: is-number: 7.0.0 dev: true + /toposort/2.0.2: + resolution: {integrity: sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==} + dev: true + /tough-cookie/4.0.0: resolution: {integrity: sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==} engines: {node: '>=6'} @@ -2140,6 +2785,17 @@ packages: engines: {node: '>= 10.0.0'} dev: true + /update-browserslist-db/1.0.5_browserslist@4.21.3: + resolution: {integrity: sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + dependencies: + browserslist: 4.21.3 + escalade: 3.1.1 + picocolors: 1.0.0 + dev: true + /upper-case/1.1.3: resolution: {integrity: sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==} dev: true @@ -2163,6 +2819,10 @@ packages: querystring: 0.2.0 dev: true + /util-deprecate/1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + dev: true + /util/0.12.4: resolution: {integrity: sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==} dependencies: @@ -2241,6 +2901,10 @@ packages: is-symbol: 1.0.4 dev: true + /which-module/2.0.0: + resolution: {integrity: sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==} + dev: true + /which-typed-array/1.1.8: resolution: {integrity: sha512-Jn4e5PItbcAHyLoRDwvPj1ypu27DJbtdYXUa5zsinrUx77Uvfb0cXwwnGMTn7cjUfhhqgVQnVJCwF+7cgU7tpw==} engines: {node: '>= 0.4'} @@ -2270,6 +2934,24 @@ packages: resolution: {integrity: sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==} dev: true + /wrap-ansi/5.1.0: + resolution: {integrity: sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==} + engines: {node: '>=6'} + dependencies: + ansi-styles: 3.2.1 + string-width: 3.1.0 + strip-ansi: 5.2.0 + dev: true + + /wrap-ansi/6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + dev: true + /wrap-ansi/7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -2309,11 +2991,34 @@ packages: resolution: {integrity: sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==} dev: true + /y18n/4.0.3: + resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} + dev: true + /y18n/5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} dev: true + /yallist/4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: true + + /yargs-parser/13.1.2: + resolution: {integrity: sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==} + dependencies: + camelcase: 5.3.1 + decamelize: 1.2.0 + dev: true + + /yargs-parser/18.1.3: + resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} + engines: {node: '>=6'} + dependencies: + camelcase: 5.3.1 + decamelize: 1.2.0 + dev: true + /yargs-parser/20.2.4: resolution: {integrity: sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==} engines: {node: '>=10'} @@ -2334,6 +3039,38 @@ packages: is-plain-obj: 2.1.0 dev: true + /yargs/13.3.2: + resolution: {integrity: sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==} + dependencies: + cliui: 5.0.0 + find-up: 3.0.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + require-main-filename: 2.0.0 + set-blocking: 2.0.0 + string-width: 3.1.0 + which-module: 2.0.0 + y18n: 4.0.3 + yargs-parser: 13.1.2 + dev: true + + /yargs/15.4.1: + resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} + engines: {node: '>=8'} + dependencies: + cliui: 6.0.0 + decamelize: 1.2.0 + find-up: 4.1.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + require-main-filename: 2.0.0 + set-blocking: 2.0.0 + string-width: 4.2.3 + which-module: 2.0.0 + y18n: 4.0.3 + yargs-parser: 18.1.3 + dev: true + /yargs/16.2.0: resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} engines: {node: '>=10'} diff --git a/development/script/create-polyfill.sh b/development/script/create-polyfill.sh index ca4faca09394815f23778f2134982b6e71587b89..a22fd07eec4bdecccd63471a2b0bf79e5b171911 100755 --- a/development/script/create-polyfill.sh +++ b/development/script/create-polyfill.sh @@ -1,13 +1,19 @@ #!/bin/bash -SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )/" +PROJECT_ROOT="$(realpath $SCRIPT_DIR'../../')/" +SOURCE_PATH="${PROJECT_ROOT}application/source/" +TEST_PATH="$(realpath $SCRIPT_DIR'../test/')/" -url="$(${SCRIPT_DIR}/../tests/node_modules/.bin/create-polyfill-service-url analyse --file "$@")" +TMPFILE=monster.temp +touch $TMPFILE +trap "rm -f $TMPFILE" 0 2 3 15 + +${PROJECT_ROOT}development/node_modules/.bin/esbuild --platform=browser --bundle ${SOURCE_PATH}monster.mjs --outfile=${TMPFILE} +url="$(${PROJECT_ROOT}development/node_modules/.bin/create-polyfill-service-url analyse --file "$TMPFILE")" if [ ! -x {$url} ] then - sed -i -E "/id=\"polyfill\"/s|.*| <script id=\"polyfill\" src=\"${url}\"|g" ${SCRIPT_DIR}/../tests/web/test.html - sed -i -E "/id=\"polyfill\"/s|.*| <script id=\"polyfill\" src=\"${url}\"|g" ${SCRIPT_DIR}/../../application/README.md + sed -i -E "/id=\"polyfill\"/s|.*| <script id=\"polyfill\" src=\"${url}\"|g" ${TEST_PATH}web/test.html + sed -i -E "/id=\"polyfill\"/s|.*| <script id=\"polyfill\" src=\"${url}\"|g" ${PROJECT_ROOT}application/README.md fi - - diff --git a/development/script/web-test.sh b/development/script/web-test.sh new file mode 100755 index 0000000000000000000000000000000000000000..01df06bcd8d75f31fddd0a6d0f1cdcb8ef130760 --- /dev/null +++ b/development/script/web-test.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +PROJECT_ROOT="$(realpath "$SCRIPT_DIR/../../")" +TEST_PATH=$(realpath "$SCRIPT_DIR/../test/")/ +TEST_CASES_PATH=$(realpath "$TEST_PATH/cases/")/ + +VERSION=$(cat "$PROJECT_ROOT/release.json" | jq -r '.version') + +$SCRIPT_DIR/create-polyfill.sh + +find ${TEST_CASES_PATH} -type f | sed "s|^$TEST_CASES_PATH||" > ${TEST_PATH}web/import.js +sed -i 's|^|import "../cases/|' ${TEST_PATH}web/import.js +sed -i 's|$|";|' ${TEST_PATH}web/import.js +sed -i "1 i import \"./prepare.js\";" ${TEST_PATH}web/import.js +sed -i "1 i /** this file was created automatically by the make target test-browser-monster */" ${TEST_PATH}web/import.js + +npx esbuild --platform=browser --sourcemap=inline --external:jsdom --external:process --external:crypto --bundle ${TEST_PATH}web/import.js --outfile=${TEST_PATH}web/tests.js + +sed -i -E "/<h1/s_.*_ <h1 style='margin-bottom: 0.1em;'>Monster ${VERSION}</h1>_" ${TEST_PATH}web/test.html ${TEST_PATH}web/monster.html ${TEST_PATH}web/monster-dev.html +sed -i -E "/id=\"lastupdate\"/s_.*_ <div id=\"lastupdate\" style='font-size:0.7em'>last update $(date)</div>_" ${TEST_PATH}web/test.html ${TEST_PATH}web/monster.html ${TEST_PATH}web/monster-dev.html +sed -i -E "s_src=\"([\"]*)\.js.*\"_src=\"\1.js?r=$(date +"%T")\"_" ${TEST_PATH}web/test.html ${TEST_PATH}web/monster.html ${TEST_PATH}web/monster-dev.html +sed -i -E "s_dist/([0-9]+\.[0-9]+\.[0-9]+)*/dist/monster_dist/${VERSION}/dist/monster_" ${TEST_PATH}web/monster.html ${TEST_PATH}web/monster-dev.html + diff --git a/development/test/package.json b/development/test/package.json index 69d4afffd09aeba6284f3ffad113c01a8e3296f7..ca58c28a258475dc5bab025d8ca4813e6832fe28 100644 --- a/development/test/package.json +++ b/development/test/package.json @@ -17,7 +17,6 @@ "chai": "^4.3.6", "chai-dom": "^1.11.0", "clean-jsdoc-theme": "^4.1.6", - "create-polyfill-service-url": "^2.2.6", "crypt": "^0.0.2", "esbuild": "^0.14.53", "flow-bin": "^0.184.0", diff --git a/development/test/pnpm-lock.yaml b/development/test/pnpm-lock.yaml index 57555063a2f7a0fcc4878bd0f06167248d6152b3..3a7c7b0450936d3055c90306a181d0a0ccf96907 100644 --- a/development/test/pnpm-lock.yaml +++ b/development/test/pnpm-lock.yaml @@ -7,7 +7,6 @@ specifiers: chai: ^4.3.6 chai-dom: ^1.11.0 clean-jsdoc-theme: ^4.1.6 - create-polyfill-service-url: ^2.2.6 crypt: ^0.0.2 esbuild: ^0.14.53 flow-bin: ^0.184.0 @@ -30,7 +29,6 @@ devDependencies: chai: 4.3.6 chai-dom: 1.11.0_chai@4.3.6+mocha@10.0.0 clean-jsdoc-theme: 4.1.7_jsdoc@3.6.11 - create-polyfill-service-url: 2.2.6 crypt: 0.0.2 esbuild: 0.14.53 flow-bin: 0.184.0 @@ -48,128 +46,6 @@ devDependencies: packages: - /@ampproject/remapping/2.2.0: - resolution: {integrity: sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==} - engines: {node: '>=6.0.0'} - dependencies: - '@jridgewell/gen-mapping': 0.1.1 - '@jridgewell/trace-mapping': 0.3.14 - dev: true - - /@babel/code-frame/7.18.6: - resolution: {integrity: sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/highlight': 7.18.6 - dev: true - - /@babel/compat-data/7.18.8: - resolution: {integrity: sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==} - engines: {node: '>=6.9.0'} - dev: true - - /@babel/core/7.18.10: - resolution: {integrity: sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==} - engines: {node: '>=6.9.0'} - dependencies: - '@ampproject/remapping': 2.2.0 - '@babel/code-frame': 7.18.6 - '@babel/generator': 7.18.12 - '@babel/helper-compilation-targets': 7.18.9_@babel+core@7.18.10 - '@babel/helper-module-transforms': 7.18.9 - '@babel/helpers': 7.18.9 - '@babel/parser': 7.18.11 - '@babel/template': 7.18.10 - '@babel/traverse': 7.18.11 - '@babel/types': 7.18.10 - convert-source-map: 1.8.0 - debug: 4.3.4 - gensync: 1.0.0-beta.2 - json5: 2.2.1 - semver: 6.3.0 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/generator/7.18.12: - resolution: {integrity: sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.18.10 - '@jridgewell/gen-mapping': 0.3.2 - jsesc: 2.5.2 - dev: true - - /@babel/helper-compilation-targets/7.18.9_@babel+core@7.18.10: - resolution: {integrity: sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/compat-data': 7.18.8 - '@babel/core': 7.18.10 - '@babel/helper-validator-option': 7.18.6 - browserslist: 4.21.3 - semver: 6.3.0 - dev: true - - /@babel/helper-environment-visitor/7.18.9: - resolution: {integrity: sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==} - engines: {node: '>=6.9.0'} - dev: true - - /@babel/helper-function-name/7.18.9: - resolution: {integrity: sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/template': 7.18.10 - '@babel/types': 7.18.10 - dev: true - - /@babel/helper-hoist-variables/7.18.6: - resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.18.10 - dev: true - - /@babel/helper-module-imports/7.18.6: - resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.18.10 - dev: true - - /@babel/helper-module-transforms/7.18.9: - resolution: {integrity: sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-module-imports': 7.18.6 - '@babel/helper-simple-access': 7.18.6 - '@babel/helper-split-export-declaration': 7.18.6 - '@babel/helper-validator-identifier': 7.18.6 - '@babel/template': 7.18.10 - '@babel/traverse': 7.18.11 - '@babel/types': 7.18.10 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/helper-simple-access/7.18.6: - resolution: {integrity: sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.18.10 - dev: true - - /@babel/helper-split-export-declaration/7.18.6: - resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.18.10 - dev: true - /@babel/helper-string-parser/7.18.10: resolution: {integrity: sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==} engines: {node: '>=6.9.0'} @@ -180,31 +56,6 @@ packages: engines: {node: '>=6.9.0'} dev: true - /@babel/helper-validator-option/7.18.6: - resolution: {integrity: sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==} - engines: {node: '>=6.9.0'} - dev: true - - /@babel/helpers/7.18.9: - resolution: {integrity: sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/template': 7.18.10 - '@babel/traverse': 7.18.11 - '@babel/types': 7.18.10 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/highlight/7.18.6: - resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-validator-identifier': 7.18.6 - chalk: 2.4.2 - js-tokens: 4.0.0 - dev: true - /@babel/parser/7.18.11: resolution: {integrity: sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ==} engines: {node: '>=6.0.0'} @@ -213,33 +64,6 @@ packages: '@babel/types': 7.18.10 dev: true - /@babel/template/7.18.10: - resolution: {integrity: sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/code-frame': 7.18.6 - '@babel/parser': 7.18.11 - '@babel/types': 7.18.10 - dev: true - - /@babel/traverse/7.18.11: - resolution: {integrity: sha512-TG9PiM2R/cWCAy6BPJKeHzNbu4lPzOSZpeMfeNErskGpTJx6trEvFaVCbDvpcxwy49BKWmEPwiW8mrysNiDvIQ==} - engines: {node: '>=6.9.0'} - dependencies: - '@babel/code-frame': 7.18.6 - '@babel/generator': 7.18.12 - '@babel/helper-environment-visitor': 7.18.9 - '@babel/helper-function-name': 7.18.9 - '@babel/helper-hoist-variables': 7.18.6 - '@babel/helper-split-export-declaration': 7.18.6 - '@babel/parser': 7.18.11 - '@babel/types': 7.18.10 - debug: 4.3.4 - globals: 11.12.0 - transitivePeerDependencies: - - supports-color - dev: true - /@babel/types/7.18.10: resolution: {integrity: sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ==} engines: {node: '>=6.9.0'} @@ -262,61 +86,16 @@ packages: dev: true optional: true - /@financial-times/js-features-analyser/0.0.4: - resolution: {integrity: sha512-AouQ5I0a9TeRdHSN093WX5yz3ZlMbRyQ1xHn7K4OmXcbsRnS96Biy5Q2sTkqLwwTouAk/SNeWIjzrDZFQHp9Mw==} - hasBin: true - dependencies: - '@babel/core': 7.18.10 - globals: 11.12.0 - yargs: 13.3.2 - transitivePeerDependencies: - - supports-color - dev: true - - /@financial-times/polyfill-useragent-normaliser/1.10.2: - resolution: {integrity: sha512-/9xHARfrKdWHt1ZXoT+/GpKx2N7uX88U1m6tF61AYSGaJFYaFlSoL1I4WbQOGH4eTQVb1z0a9LfwXaWblpRTBg==} - engines: {node: '>=8'} - dependencies: - '@financial-times/useragent_parser': 1.6.3 - semver: 7.3.7 - dev: true - - /@financial-times/useragent_parser/1.6.3: - resolution: {integrity: sha512-TlQiXt/vS5ZwY0V3salvlyQzIzMGZEyw9inmJA25A8heL2kBVENbToiEc64R6ETNf5YHa2lwnc2I7iNHP9SqeQ==} - dev: true - /@istanbuljs/schema/0.1.3: resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} engines: {node: '>=8'} dev: true - /@jridgewell/gen-mapping/0.1.1: - resolution: {integrity: sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==} - engines: {node: '>=6.0.0'} - dependencies: - '@jridgewell/set-array': 1.1.2 - '@jridgewell/sourcemap-codec': 1.4.14 - dev: true - - /@jridgewell/gen-mapping/0.3.2: - resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==} - engines: {node: '>=6.0.0'} - dependencies: - '@jridgewell/set-array': 1.1.2 - '@jridgewell/sourcemap-codec': 1.4.14 - '@jridgewell/trace-mapping': 0.3.14 - dev: true - /@jridgewell/resolve-uri/3.1.0: resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} engines: {node: '>=6.0.0'} dev: true - /@jridgewell/set-array/1.1.2: - resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} - engines: {node: '>=6.0.0'} - dev: true - /@jridgewell/sourcemap-codec/1.4.14: resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} dev: true @@ -455,23 +234,11 @@ packages: engines: {node: '>=6'} dev: true - /ansi-regex/4.1.1: - resolution: {integrity: sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==} - engines: {node: '>=6'} - dev: true - /ansi-regex/5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} dev: true - /ansi-styles/3.2.1: - resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} - engines: {node: '>=4'} - dependencies: - color-convert: 1.9.3 - dev: true - /ansi-styles/4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} @@ -559,17 +326,6 @@ packages: resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==} dev: true - /browserslist/4.21.3: - resolution: {integrity: sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - dependencies: - caniuse-lite: 1.0.30001374 - electron-to-chromium: 1.4.211 - node-releases: 2.0.6 - update-browserslist-db: 1.0.5_browserslist@4.21.3 - dev: true - /btoa/1.2.1: resolution: {integrity: sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==} engines: {node: '>= 0.4.0'} @@ -609,20 +365,11 @@ packages: upper-case: 1.1.3 dev: true - /camelcase/5.3.1: - resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} - engines: {node: '>=6'} - dev: true - /camelcase/6.3.0: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} dev: true - /caniuse-lite/1.0.30001374: - resolution: {integrity: sha512-mWvzatRx3w+j5wx/mpFN5v5twlPrabG8NqX2c6e45LCpymdoGqNvRkRutFUqpRTXKFQFNQJasvK0YT7suW6/Hw==} - dev: true - /catharsis/0.9.0: resolution: {integrity: sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==} engines: {node: '>= 10'} @@ -654,15 +401,6 @@ packages: type-detect: 4.0.8 dev: true - /chalk/2.4.2: - resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} - engines: {node: '>=4'} - dependencies: - ansi-styles: 3.2.1 - escape-string-regexp: 1.0.5 - supports-color: 5.5.0 - dev: true - /chalk/4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -708,22 +446,6 @@ packages: nanoid: 3.3.4 dev: true - /cliui/5.0.0: - resolution: {integrity: sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==} - dependencies: - string-width: 3.1.0 - strip-ansi: 5.2.0 - wrap-ansi: 5.1.0 - dev: true - - /cliui/6.0.0: - resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 6.2.0 - dev: true - /cliui/7.0.4: resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} dependencies: @@ -732,12 +454,6 @@ packages: wrap-ansi: 7.0.0 dev: true - /color-convert/1.9.3: - resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} - dependencies: - color-name: 1.1.3 - dev: true - /color-convert/2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -745,10 +461,6 @@ packages: color-name: 1.1.4 dev: true - /color-name/1.1.3: - resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} - dev: true - /color-name/1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} dev: true @@ -774,26 +486,6 @@ packages: safe-buffer: 5.1.2 dev: true - /core-util-is/1.0.3: - resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - dev: true - - /create-polyfill-service-url/2.2.6: - resolution: {integrity: sha512-uhipnEEH6SkC176Za0Rt3D7pTV60nTiS2Vn4wZBIQacUtfPBxuTG4dox/jy2yXDCEiFX62gYhehvCIYPudjzFA==} - hasBin: true - dependencies: - '@babel/core': 7.18.10 - '@financial-times/js-features-analyser': 0.0.4 - browserslist: 4.21.3 - execa: 4.1.0 - polyfill-library: 3.111.0 - semver: 7.3.7 - snyk: 1.984.0 - yargs: 15.4.1 - transitivePeerDependencies: - - supports-color - dev: true - /cross-spawn/7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} @@ -861,11 +553,6 @@ packages: supports-color: 8.1.1 dev: true - /decamelize/1.2.0: - resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} - engines: {node: '>=0.10.0'} - dev: true - /decamelize/4.0.0: resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==} engines: {node: '>=10'} @@ -916,24 +603,10 @@ packages: webidl-conversions: 7.0.0 dev: true - /electron-to-chromium/1.4.211: - resolution: {integrity: sha512-BZSbMpyFQU0KBJ1JG26XGeFI3i4op+qOYGxftmZXFZoHkhLgsSv4DHDJfl8ogII3hIuzGt51PaZ195OVu0yJ9A==} - dev: true - - /emoji-regex/7.0.3: - resolution: {integrity: sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==} - dev: true - /emoji-regex/8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} dev: true - /end-of-stream/1.4.4: - resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} - dependencies: - once: 1.4.0 - dev: true - /entities/2.1.0: resolution: {integrity: sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==} dev: true @@ -1190,11 +863,6 @@ packages: engines: {node: '>=6'} dev: true - /escape-string-regexp/1.0.5: - resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} - engines: {node: '>=0.8.0'} - dev: true - /escape-string-regexp/2.0.0: resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} engines: {node: '>=8'} @@ -1239,21 +907,6 @@ packages: engines: {node: '>=6'} dev: true - /execa/4.1.0: - resolution: {integrity: sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==} - engines: {node: '>=10'} - dependencies: - cross-spawn: 7.0.3 - get-stream: 5.2.0 - human-signals: 1.1.1 - is-stream: 2.0.1 - merge-stream: 2.0.0 - npm-run-path: 4.0.1 - onetime: 5.1.2 - signal-exit: 3.0.7 - strip-final-newline: 2.0.0 - dev: true - /fast-levenshtein/2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} dev: true @@ -1275,21 +928,6 @@ packages: to-regex-range: 5.0.1 dev: true - /find-up/3.0.0: - resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==} - engines: {node: '>=6'} - dependencies: - locate-path: 3.0.0 - dev: true - - /find-up/4.1.0: - resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} - engines: {node: '>=8'} - dependencies: - locate-path: 5.0.0 - path-exists: 4.0.0 - dev: true - /find-up/5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} @@ -1332,19 +970,6 @@ packages: mime-types: 2.1.35 dev: true - /from2-string/1.1.0: - resolution: {integrity: sha512-m8vCh+KnXXXBtfF2VUbiYlQ+nczLcntB0BrtNgpmLkHylhObe9WF1b2LZjBBzrZzA6P4mkEla6ZYQoOUTG8cYA==} - dependencies: - from2: 2.3.0 - dev: true - - /from2/2.3.0: - resolution: {integrity: sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==} - dependencies: - inherits: 2.0.4 - readable-stream: 2.3.7 - dev: true - /fs-extra/10.1.0: resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} engines: {node: '>=12'} @@ -1398,11 +1023,6 @@ packages: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} dev: true - /gensync/1.0.0-beta.2: - resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} - engines: {node: '>=6.9.0'} - dev: true - /get-caller-file/2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} @@ -1420,13 +1040,6 @@ packages: has-symbols: 1.0.3 dev: true - /get-stream/5.2.0: - resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} - engines: {node: '>=8'} - dependencies: - pump: 3.0.0 - dev: true - /get-symbol-description/1.0.0: resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} engines: {node: '>= 0.4'} @@ -1464,11 +1077,6 @@ packages: path-is-absolute: 1.0.1 dev: true - /globals/11.12.0: - resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} - engines: {node: '>=4'} - dev: true - /graceful-fs/4.2.10: resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} dev: true @@ -1477,11 +1085,6 @@ packages: resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} dev: true - /has-flag/3.0.0: - resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} - engines: {node: '>=4'} - dev: true - /has-flag/4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} @@ -1563,11 +1166,6 @@ packages: - supports-color dev: true - /human-signals/1.1.1: - resolution: {integrity: sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==} - engines: {node: '>=8.12.0'} - dev: true - /iconv-lite/0.6.3: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} @@ -1641,11 +1239,6 @@ packages: engines: {node: '>=0.10.0'} dev: true - /is-fullwidth-code-point/2.0.0: - resolution: {integrity: sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==} - engines: {node: '>=4'} - dev: true - /is-fullwidth-code-point/3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} @@ -1705,11 +1298,6 @@ packages: call-bind: 1.0.2 dev: true - /is-stream/2.0.1: - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} - engines: {node: '>=8'} - dev: true - /is-string/1.0.7: resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} engines: {node: '>= 0.4'} @@ -1755,10 +1343,6 @@ packages: resolution: {integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==} dev: true - /isarray/1.0.0: - resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - dev: true - /isexe/2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} dev: true @@ -1785,10 +1369,6 @@ packages: istanbul-lib-report: 3.0.0 dev: true - /js-tokens/4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - dev: true - /js-yaml/4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true @@ -1886,18 +1466,6 @@ packages: - utf-8-validate dev: true - /jsesc/2.5.2: - resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} - engines: {node: '>=4'} - hasBin: true - dev: true - - /json5/2.2.1: - resolution: {integrity: sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==} - engines: {node: '>=6'} - hasBin: true - dev: true - /jsonfile/6.1.0: resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} dependencies: @@ -1952,21 +1520,6 @@ packages: uc.micro: 1.0.6 dev: true - /locate-path/3.0.0: - resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} - engines: {node: '>=6'} - dependencies: - p-locate: 3.0.0 - path-exists: 3.0.0 - dev: true - - /locate-path/5.0.0: - resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} - engines: {node: '>=8'} - dependencies: - p-locate: 4.1.0 - dev: true - /locate-path/6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -2000,13 +1553,6 @@ packages: resolution: {integrity: sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==} dev: true - /lru-cache/6.0.0: - resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} - engines: {node: '>=10'} - dependencies: - yallist: 4.0.0 - dev: true - /make-dir/3.1.0: resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} engines: {node: '>=8'} @@ -2045,15 +1591,6 @@ packages: resolution: {integrity: sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==} dev: true - /merge-stream/2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - dev: true - - /merge2/1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - dev: true - /mime-db/1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} @@ -2066,11 +1603,6 @@ packages: mime-db: 1.52.0 dev: true - /mimic-fn/2.1.0: - resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} - engines: {node: '>=6'} - dev: true - /minimatch/3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} dependencies: @@ -2090,12 +1622,6 @@ packages: hasBin: true dev: true - /mnemonist/0.38.5: - resolution: {integrity: sha512-bZTFT5rrPKtPJxj8KSV0WkPyNxl72vQepqqVUAW2ARUpUSF2qXMB6jZj7hW5/k7C1rtpzqbD/IIbJwLXUjCHeg==} - dependencies: - obliterator: 2.0.4 - dev: true - /mocha/10.0.0: resolution: {integrity: sha512-0Wl+elVUD43Y0BqPZBzZt8Tnkw9CMUdNYnUsTfOM1vuhJVZL+kiesFYsqwBkEEuEixaiPe5ZQdqDgX2jddhmoA==} engines: {node: '>= 14.0.0'} @@ -2208,22 +1734,11 @@ packages: plantuml-encoder: 1.4.0 dev: true - /node-releases/2.0.6: - resolution: {integrity: sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==} - dev: true - /normalize-path/3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} dev: true - /npm-run-path/4.0.1: - resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} - engines: {node: '>=8'} - dependencies: - path-key: 3.1.1 - dev: true - /nwsapi/2.2.1: resolution: {integrity: sha512-JYOWTeFoS0Z93587vRJgASD5Ut11fYl5NyihP3KrYBvMe1FRRs6RN7m20SA/16GM4P6hTnZjT+UmDOt38UeXNg==} dev: true @@ -2247,23 +1762,12 @@ packages: object-keys: 1.1.1 dev: true - /obliterator/2.0.4: - resolution: {integrity: sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==} - dev: true - /once/1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} dependencies: wrappy: 1.0.2 dev: true - /onetime/5.1.2: - resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} - engines: {node: '>=6'} - dependencies: - mimic-fn: 2.1.0 - dev: true - /optionator/0.8.3: resolution: {integrity: sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==} engines: {node: '>= 0.8.0'} @@ -2276,13 +1780,6 @@ packages: word-wrap: 1.2.3 dev: true - /p-limit/2.3.0: - resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} - engines: {node: '>=6'} - dependencies: - p-try: 2.2.0 - dev: true - /p-limit/3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} @@ -2290,20 +1787,6 @@ packages: yocto-queue: 0.1.0 dev: true - /p-locate/3.0.0: - resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==} - engines: {node: '>=6'} - dependencies: - p-limit: 2.3.0 - dev: true - - /p-locate/4.1.0: - resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} - engines: {node: '>=8'} - dependencies: - p-limit: 2.3.0 - dev: true - /p-locate/5.0.0: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} @@ -2311,11 +1794,6 @@ packages: p-limit: 3.1.0 dev: true - /p-try/2.2.0: - resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} - engines: {node: '>=6'} - dev: true - /param-case/2.1.1: resolution: {integrity: sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w==} dependencies: @@ -2326,11 +1804,6 @@ packages: resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} dev: true - /path-exists/3.0.0: - resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} - engines: {node: '>=4'} - dev: true - /path-exists/4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -2356,10 +1829,6 @@ packages: resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} dev: true - /picocolors/1.0.0: - resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} - dev: true - /picomatch/2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} @@ -2369,44 +1838,15 @@ packages: resolution: {integrity: sha512-sxMwpDw/ySY1WB2CE3+IdMuEcWibJ72DDOsXLkSmEaSzwEUaYBT6DWgOfBiHGCux4q433X6+OEFWjlVqp7gL6g==} dev: true - /polyfill-library/3.111.0: - resolution: {integrity: sha512-BLh/1m/QPKrH3fEmIICpHHOVoYb9naWKImWkYuGoh26sHFiNO01arJ1KXEQMYLfVl4btpCoX/bRdqQY9kIR/eQ==} - engines: {node: '>=8'} - dependencies: - '@financial-times/polyfill-useragent-normaliser': 1.10.2 - from2-string: 1.1.0 - graceful-fs: 4.2.10 - merge2: 1.4.1 - mnemonist: 0.38.5 - stream-from-promise: 1.0.0 - stream-to-string: 1.2.0 - toposort: 2.0.2 - dev: true - /prelude-ls/1.1.2: resolution: {integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==} engines: {node: '>= 0.8.0'} dev: true - /process-nextick-args/2.0.1: - resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - dev: true - - /promise-polyfill/1.1.6: - resolution: {integrity: sha512-7rrONfyLkDEc7OJ5QBkqa4KI4EBhCd340xRuIUPGCfu13znS+vx+VDdrT9ODAJHlXm7w4lbxN3DRjyv58EuzDg==} - dev: true - /psl/1.9.0: resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} dev: true - /pump/3.0.0: - resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} - dependencies: - end-of-stream: 1.4.4 - once: 1.4.0 - dev: true - /punycode/1.3.2: resolution: {integrity: sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==} dev: true @@ -2439,18 +1879,6 @@ packages: safe-buffer: 5.2.1 dev: true - /readable-stream/2.3.7: - resolution: {integrity: sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==} - dependencies: - core-util-is: 1.0.3 - inherits: 2.0.4 - isarray: 1.0.0 - process-nextick-args: 2.0.1 - safe-buffer: 5.1.2 - string_decoder: 1.1.1 - util-deprecate: 1.0.2 - dev: true - /readdirp/3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} @@ -2477,10 +1905,6 @@ packages: engines: {node: '>=0.10.0'} dev: true - /require-main-filename/2.0.0: - resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} - dev: true - /requizzle/0.2.3: resolution: {integrity: sha512-YanoyJjykPxGHii0fZP0uUPEXpvqfBDxWV7s6GKAiiOsiqhX6vHNyW3Qzdmqp/iq/ExbhaGbVrjB4ruEVSM4GQ==} dependencies: @@ -2518,24 +1942,12 @@ packages: hasBin: true dev: true - /semver/7.3.7: - resolution: {integrity: sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==} - engines: {node: '>=10'} - hasBin: true - dependencies: - lru-cache: 6.0.0 - dev: true - /serialize-javascript/6.0.0: resolution: {integrity: sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==} dependencies: randombytes: 2.1.0 dev: true - /set-blocking/2.0.0: - resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} - dev: true - /shebang-command/2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -2571,37 +1983,11 @@ packages: supports-color: 7.2.0 dev: true - /snyk/1.984.0: - resolution: {integrity: sha512-e72gNXx6Epj1Tp8TEWoLJHEqkPTr370GvJQj/o73YlGFCILqfzFiFGq5cla8UffFM+mHEhuQDX4jAH53x2cqYA==} - engines: {node: '>=12'} - hasBin: true - dev: true - /source-map/0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} dev: true - /stream-from-promise/1.0.0: - resolution: {integrity: sha512-j84KLkudt+gr8KJ21RB02btPLx61uGbrLnewsWz6QKmsz8/c4ZFqXw6mJh5+G4oRN7DgDxdbjPxnpySpg1mUig==} - engines: {node: '>=0.10.0'} - dev: true - - /stream-to-string/1.2.0: - resolution: {integrity: sha512-8drZlFIKBHSMdX9GCWv8V9AAWnQcTqw0iAI6/GC7UJ0H0SwKeFKjOoZfGY1tOU00GGU7FYZQoJ/ZCUEoXhD7yQ==} - dependencies: - promise-polyfill: 1.1.6 - dev: true - - /string-width/3.1.0: - resolution: {integrity: sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==} - engines: {node: '>=6'} - dependencies: - emoji-regex: 7.0.3 - is-fullwidth-code-point: 2.0.0 - strip-ansi: 5.2.0 - dev: true - /string-width/4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -2627,19 +2013,6 @@ packages: es-abstract: 1.20.1 dev: true - /string_decoder/1.1.1: - resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} - dependencies: - safe-buffer: 5.1.2 - dev: true - - /strip-ansi/5.2.0: - resolution: {integrity: sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==} - engines: {node: '>=6'} - dependencies: - ansi-regex: 4.1.1 - dev: true - /strip-ansi/6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -2647,23 +2020,11 @@ packages: ansi-regex: 5.0.1 dev: true - /strip-final-newline/2.0.0: - resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} - engines: {node: '>=6'} - dev: true - /strip-json-comments/3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} dev: true - /supports-color/5.5.0: - resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} - engines: {node: '>=4'} - dependencies: - has-flag: 3.0.0 - dev: true - /supports-color/7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -2707,10 +2068,6 @@ packages: is-number: 7.0.0 dev: true - /toposort/2.0.2: - resolution: {integrity: sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==} - dev: true - /tough-cookie/4.0.0: resolution: {integrity: sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==} engines: {node: '>=6'} @@ -2776,17 +2133,6 @@ packages: engines: {node: '>= 10.0.0'} dev: true - /update-browserslist-db/1.0.5_browserslist@4.21.3: - resolution: {integrity: sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - dependencies: - browserslist: 4.21.3 - escalade: 3.1.1 - picocolors: 1.0.0 - dev: true - /upper-case/1.1.3: resolution: {integrity: sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==} dev: true @@ -2810,10 +2156,6 @@ packages: querystring: 0.2.0 dev: true - /util-deprecate/1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - dev: true - /util/0.12.4: resolution: {integrity: sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==} dependencies: @@ -2900,10 +2242,6 @@ packages: is-symbol: 1.0.4 dev: true - /which-module/2.0.0: - resolution: {integrity: sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==} - dev: true - /which-typed-array/1.1.8: resolution: {integrity: sha512-Jn4e5PItbcAHyLoRDwvPj1ypu27DJbtdYXUa5zsinrUx77Uvfb0cXwwnGMTn7cjUfhhqgVQnVJCwF+7cgU7tpw==} engines: {node: '>= 0.4'} @@ -2933,24 +2271,6 @@ packages: resolution: {integrity: sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==} dev: true - /wrap-ansi/5.1.0: - resolution: {integrity: sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==} - engines: {node: '>=6'} - dependencies: - ansi-styles: 3.2.1 - string-width: 3.1.0 - strip-ansi: 5.2.0 - dev: true - - /wrap-ansi/6.2.0: - resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} - engines: {node: '>=8'} - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - dev: true - /wrap-ansi/7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -2990,34 +2310,11 @@ packages: resolution: {integrity: sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==} dev: true - /y18n/4.0.3: - resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} - dev: true - /y18n/5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} dev: true - /yallist/4.0.0: - resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - dev: true - - /yargs-parser/13.1.2: - resolution: {integrity: sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==} - dependencies: - camelcase: 5.3.1 - decamelize: 1.2.0 - dev: true - - /yargs-parser/18.1.3: - resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} - engines: {node: '>=6'} - dependencies: - camelcase: 5.3.1 - decamelize: 1.2.0 - dev: true - /yargs-parser/20.2.4: resolution: {integrity: sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==} engines: {node: '>=10'} @@ -3038,38 +2335,6 @@ packages: is-plain-obj: 2.1.0 dev: true - /yargs/13.3.2: - resolution: {integrity: sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==} - dependencies: - cliui: 5.0.0 - find-up: 3.0.0 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - require-main-filename: 2.0.0 - set-blocking: 2.0.0 - string-width: 3.1.0 - which-module: 2.0.0 - y18n: 4.0.3 - yargs-parser: 13.1.2 - dev: true - - /yargs/15.4.1: - resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} - engines: {node: '>=8'} - dependencies: - cliui: 6.0.0 - decamelize: 1.2.0 - find-up: 4.1.0 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - require-main-filename: 2.0.0 - set-blocking: 2.0.0 - string-width: 4.2.3 - which-module: 2.0.0 - y18n: 4.0.3 - yargs-parser: 18.1.3 - dev: true - /yargs/16.2.0: resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} engines: {node: '>=10'} diff --git a/development/test/web/monster-dev.html b/development/test/web/monster-dev.html index 672f968dadf767fe8c9eee92bc155c9006e9a16f..55b1d9f5d9e8aa5de7fdde60e1017524ab0ae9b4 100644 --- a/development/test/web/monster-dev.html +++ b/development/test/web/monster-dev.html @@ -10,13 +10,13 @@ crossorigin="anonymous" referrerpolicy="no-referrer"></script> - <script src="https://monsterjs.org/dist/0.1.22/dist/monster.dev.mjs"></script> + <script src="https://monsterjs.org/dist/0.1.8/dist/monster.dev.mjs"></script> </head> <body> <div id="headline" style="display: flex;align-items: center;justify-content: center;flex-direction: column;"> - <h1 style='margin-bottom: 0.1em;'>Monster 0.1.22</h1> - <div id="lastupdate" style='font-size:0.7em'>last update So 7. Aug 15:17:24 CEST 2022</div> + <h1 style='margin-bottom: 0.1em;'>Monster 0.1.8</h1> + <div id="lastupdate" style='font-size:0.7em'>last update So 7. Aug 19:45:23 CEST 2022</div> </div> </body> </html> diff --git a/development/test/web/monster.html b/development/test/web/monster.html index 4512440e26fe59112521990efe280d9f62f77622..26ef978097d6519a0f8dc79c3277ecae4204c02a 100644 --- a/development/test/web/monster.html +++ b/development/test/web/monster.html @@ -14,8 +14,8 @@ </head> <body> <div id="headline" style="display: flex;align-items: center;justify-content: center;flex-direction: column;"> - <h1 style='margin-bottom: 0.1em;'>Monster 0.1.22</h1> - <div id="lastupdate" style='font-size:0.7em'>last update So 7. Aug 15:17:24 CEST 2022</div> + <h1 style='margin-bottom: 0.1em;'>Monster 0.1.8</h1> + <div id="lastupdate" style='font-size:0.7em'>last update So 7. Aug 19:45:23 CEST 2022</div> </div> </body> </html> diff --git a/development/test/web/test.html b/development/test/web/test.html index d7e607327b04630bd55c7a4e817fc37688eb232c..9eacc4844bb5f6fef70fd7a2340ae4079a1cdfb2 100644 --- a/development/test/web/test.html +++ b/development/test/web/test.html @@ -5,7 +5,7 @@ <title>Mocha Monster</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"/> <link rel="stylesheet" href="mocha.css"/> - <script id="polyfill" src="https://polyfill.io/v3/polyfill.min.js?features=Array.from,Array.isArray,Array.prototype.entries,Array.prototype.fill,Array.prototype.filter,Array.prototype.forEach,Array.prototype.indexOf,Array.prototype.keys,Array.prototype.lastIndexOf,Array.prototype.map,Array.prototype.reduce,Array.prototype.sort,ArrayBuffer,atob,CustomEvent,DataView,document,Document,DocumentFragment,Element,Event,fetch,globalThis,HTMLDocument,HTMLTemplateElement,Intl,JSON,Map,Math.log2,Number.isInteger,Object.assign,Object.defineProperty,Object.entries,Object.freeze,Object.getOwnPropertyDescriptor,Object.getOwnPropertyNames,Object.getPrototypeOf,Object.keys,Promise,Reflect,Reflect.defineProperty,Reflect.get,Reflect.getOwnPropertyDescriptor,Reflect.setPrototypeOf,Set,String.prototype.endsWith,String.prototype.matchAll,String.prototype.padStart,String.prototype.startsWith,String.prototype.trim,Symbol,Symbol.iterator,Uint16Array,Uint8Array,URL,WeakMap,WeakSet" + <script id="polyfill" src="https://polyfill.io/v3/polyfill.min.js?features=Array.from,Array.isArray,Array.prototype.entries,Array.prototype.fill,Array.prototype.forEach,Array.prototype.indexOf,Array.prototype.keys,Array.prototype.lastIndexOf,Array.prototype.map,Array.prototype.reduce,Array.prototype.sort,ArrayBuffer,atob,DataView,document,DocumentFragment,Element,Event,globalThis,HTMLDocument,HTMLTemplateElement,JSON,Map,Math.log2,Number.isInteger,Object.assign,Object.defineProperty,Object.entries,Object.getOwnPropertyDescriptor,Object.getPrototypeOf,Object.keys,Promise,Reflect,Reflect.defineProperty,Reflect.get,Reflect.getOwnPropertyDescriptor,Reflect.setPrototypeOf,Set,String.prototype.endsWith,String.prototype.matchAll,String.prototype.padStart,String.prototype.startsWith,String.prototype.trim,Symbol,Symbol.iterator,WeakMap,WeakSet" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <script src="https://cdn.jsdelivr.net/npm/element-internals-polyfill@0.1.52/dist/index.min.mjs"></script> @@ -13,8 +13,8 @@ </head> <body> <div id="headline" style="display: flex;align-items: center;justify-content: center;flex-direction: column;"> - <h1 style='margin-bottom: 0.1em;'>Monster 0.1.22</h1> - <div id="lastupdate" style='font-size:0.7em'>last update So 7. Aug 15:17:24 CEST 2022</div> + <h1 style='margin-bottom: 0.1em;'>Monster 0.1.8</h1> + <div id="lastupdate" style='font-size:0.7em'>last update So 7. Aug 19:45:23 CEST 2022</div> </div> <div id="mocks"></div> <div id="mocha"></div> diff --git a/development/test/web/tests.js b/development/test/web/tests.js index 397bededa6f43455174ae85f996d5946aa369c3c..7b802c0c4620a8fbab8cd247cf116d1b75b574dd 100644 --- a/development/test/web/tests.js +++ b/development/test/web/tests.js @@ -35,9 +35,9 @@ mod )); - // development/test/node_modules/.pnpm/assertion-error@1.1.0/node_modules/assertion-error/index.js + // test/node_modules/.pnpm/assertion-error@1.1.0/node_modules/assertion-error/index.js var require_assertion_error = __commonJS({ - "development/test/node_modules/.pnpm/assertion-error@1.1.0/node_modules/assertion-error/index.js"(exports, module) { + "test/node_modules/.pnpm/assertion-error@1.1.0/node_modules/assertion-error/index.js"(exports, module) { function exclude() { var excludes = [].slice.call(arguments); function excludeProps(res, obj) { @@ -86,9 +86,9 @@ } }); - // development/test/node_modules/.pnpm/pathval@1.1.1/node_modules/pathval/index.js + // test/node_modules/.pnpm/pathval@1.1.1/node_modules/pathval/index.js var require_pathval = __commonJS({ - "development/test/node_modules/.pnpm/pathval@1.1.1/node_modules/pathval/index.js"(exports, module) { + "test/node_modules/.pnpm/pathval@1.1.1/node_modules/pathval/index.js"(exports, module) { "use strict"; function hasProperty(obj, name) { if (typeof obj === "undefined" || obj === null) { @@ -186,9 +186,9 @@ } }); - // development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/flag.js + // test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/flag.js var require_flag = __commonJS({ - "development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/flag.js"(exports, module) { + "test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/flag.js"(exports, module) { module.exports = function flag(obj, key, value) { var flags = obj.__flags || (obj.__flags = /* @__PURE__ */ Object.create(null)); if (arguments.length === 3) { @@ -200,9 +200,9 @@ } }); - // development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/test.js + // test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/test.js var require_test = __commonJS({ - "development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/test.js"(exports, module) { + "test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/test.js"(exports, module) { var flag = require_flag(); module.exports = function test(obj, args) { var negate = flag(obj, "negate"), expr = args[0]; @@ -211,9 +211,9 @@ } }); - // development/test/node_modules/.pnpm/type-detect@4.0.8/node_modules/type-detect/type-detect.js + // test/node_modules/.pnpm/type-detect@4.0.8/node_modules/type-detect/type-detect.js var require_type_detect = __commonJS({ - "development/test/node_modules/.pnpm/type-detect@4.0.8/node_modules/type-detect/type-detect.js"(exports, module) { + "test/node_modules/.pnpm/type-detect@4.0.8/node_modules/type-detect/type-detect.js"(exports, module) { (function(global4, factory) { typeof exports === "object" && typeof module !== "undefined" ? module.exports = factory() : typeof define === "function" && define.amd ? define(factory) : global4.typeDetect = factory(); })(exports, function() { @@ -330,9 +330,9 @@ } }); - // development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/expectTypes.js + // test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/expectTypes.js var require_expectTypes = __commonJS({ - "development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/expectTypes.js"(exports, module) { + "test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/expectTypes.js"(exports, module) { var AssertionError2 = require_assertion_error(); var flag = require_flag(); var type = require_type_detect(); @@ -364,18 +364,18 @@ } }); - // development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/getActual.js + // test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/getActual.js var require_getActual = __commonJS({ - "development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/getActual.js"(exports, module) { + "test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/getActual.js"(exports, module) { module.exports = function getActual(obj, args) { return args.length > 4 ? args[4] : obj._obj; }; } }); - // development/test/node_modules/.pnpm/get-func-name@2.0.0/node_modules/get-func-name/index.js + // test/node_modules/.pnpm/get-func-name@2.0.0/node_modules/get-func-name/index.js var require_get_func_name = __commonJS({ - "development/test/node_modules/.pnpm/get-func-name@2.0.0/node_modules/get-func-name/index.js"(exports, module) { + "test/node_modules/.pnpm/get-func-name@2.0.0/node_modules/get-func-name/index.js"(exports, module) { "use strict"; var toString = Function.prototype.toString; var functionNameMatch = /\s*function(?:\s|\s*\/\*[^(?:*\/)]+\*\/\s*)*([^\s\(\/]+)/; @@ -398,15 +398,15 @@ } }); - // (disabled):development/test/node_modules/.pnpm/util@0.12.4/node_modules/util/util.js + // (disabled):test/node_modules/.pnpm/util@0.12.4/node_modules/util/util.js var require_util = __commonJS({ - "(disabled):development/test/node_modules/.pnpm/util@0.12.4/node_modules/util/util.js"() { + "(disabled):test/node_modules/.pnpm/util@0.12.4/node_modules/util/util.js"() { } }); - // development/test/node_modules/.pnpm/loupe@2.3.4/node_modules/loupe/loupe.js + // test/node_modules/.pnpm/loupe@2.3.4/node_modules/loupe/loupe.js var require_loupe = __commonJS({ - "development/test/node_modules/.pnpm/loupe@2.3.4/node_modules/loupe/loupe.js"(exports, module) { + "test/node_modules/.pnpm/loupe@2.3.4/node_modules/loupe/loupe.js"(exports, module) { (function(global4, factory) { typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define(["exports"], factory) : (global4 = typeof globalThis !== "undefined" ? globalThis : global4 || self, factory(global4.loupe = {})); })(exports, function(exports2) { @@ -1050,9 +1050,9 @@ } }); - // development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/config.js + // test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/config.js var require_config = __commonJS({ - "development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/config.js"(exports, module) { + "test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/config.js"(exports, module) { module.exports = { includeStack: false, showDiff: true, @@ -1063,9 +1063,9 @@ } }); - // development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/inspect.js + // test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/inspect.js var require_inspect = __commonJS({ - "development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/inspect.js"(exports, module) { + "test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/inspect.js"(exports, module) { var getName = require_get_func_name(); var loupe = require_loupe(); var config2 = require_config(); @@ -1082,9 +1082,9 @@ } }); - // development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/objDisplay.js + // test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/objDisplay.js var require_objDisplay = __commonJS({ - "development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/objDisplay.js"(exports, module) { + "test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/objDisplay.js"(exports, module) { var inspect = require_inspect(); var config2 = require_config(); module.exports = function objDisplay(obj) { @@ -1107,9 +1107,9 @@ } }); - // development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/getMessage.js + // test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/getMessage.js var require_getMessage = __commonJS({ - "development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/getMessage.js"(exports, module) { + "test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/getMessage.js"(exports, module) { var flag = require_flag(); var getActual = require_getActual(); var objDisplay = require_objDisplay(); @@ -1130,9 +1130,9 @@ } }); - // development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/transferFlags.js + // test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/transferFlags.js var require_transferFlags = __commonJS({ - "development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/transferFlags.js"(exports, module) { + "test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/transferFlags.js"(exports, module) { module.exports = function transferFlags(assertion, object, includeAll) { var flags = assertion.__flags || (assertion.__flags = /* @__PURE__ */ Object.create(null)); if (!object.__flags) { @@ -1148,9 +1148,9 @@ } }); - // development/test/node_modules/.pnpm/deep-eql@3.0.1/node_modules/deep-eql/index.js + // test/node_modules/.pnpm/deep-eql@3.0.1/node_modules/deep-eql/index.js var require_deep_eql = __commonJS({ - "development/test/node_modules/.pnpm/deep-eql@3.0.1/node_modules/deep-eql/index.js"(exports, module) { + "test/node_modules/.pnpm/deep-eql@3.0.1/node_modules/deep-eql/index.js"(exports, module) { "use strict"; var type = require_type_detect(); function FakeMap() { @@ -1404,9 +1404,9 @@ } }); - // development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/isProxyEnabled.js + // test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/isProxyEnabled.js var require_isProxyEnabled = __commonJS({ - "development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/isProxyEnabled.js"(exports, module) { + "test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/isProxyEnabled.js"(exports, module) { var config2 = require_config(); module.exports = function isProxyEnabled() { return config2.useProxy && typeof Proxy !== "undefined" && typeof Reflect !== "undefined"; @@ -1414,9 +1414,9 @@ } }); - // development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/addProperty.js + // test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/addProperty.js var require_addProperty = __commonJS({ - "development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/addProperty.js"(exports, module) { + "test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/addProperty.js"(exports, module) { var chai2 = require_chai(); var flag = require_flag(); var isProxyEnabled = require_isProxyEnabled(); @@ -1446,9 +1446,9 @@ } }); - // development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/addLengthGuard.js + // test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/addLengthGuard.js var require_addLengthGuard = __commonJS({ - "development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/addLengthGuard.js"(exports, module) { + "test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/addLengthGuard.js"(exports, module) { var fnLengthDesc = Object.getOwnPropertyDescriptor(function() { }, "length"); module.exports = function addLengthGuard(fn, assertionName, isChainable) { @@ -1467,9 +1467,9 @@ } }); - // development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/getProperties.js + // test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/getProperties.js var require_getProperties = __commonJS({ - "development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/getProperties.js"(exports, module) { + "test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/getProperties.js"(exports, module) { module.exports = function getProperties(object) { var result = Object.getOwnPropertyNames(object); function addProperty(property) { @@ -1487,9 +1487,9 @@ } }); - // development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/proxify.js + // test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/proxify.js var require_proxify = __commonJS({ - "development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/proxify.js"(exports, module) { + "test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/proxify.js"(exports, module) { var config2 = require_config(); var flag = require_flag(); var getProperties = require_getProperties(); @@ -1563,9 +1563,9 @@ } }); - // development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/addMethod.js + // test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/addMethod.js var require_addMethod = __commonJS({ - "development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/addMethod.js"(exports, module) { + "test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/addMethod.js"(exports, module) { var addLengthGuard = require_addLengthGuard(); var chai2 = require_chai(); var flag = require_flag(); @@ -1589,9 +1589,9 @@ } }); - // development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/overwriteProperty.js + // test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/overwriteProperty.js var require_overwriteProperty = __commonJS({ - "development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/overwriteProperty.js"(exports, module) { + "test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/overwriteProperty.js"(exports, module) { var chai2 = require_chai(); var flag = require_flag(); var isProxyEnabled = require_isProxyEnabled(); @@ -1627,9 +1627,9 @@ } }); - // development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/overwriteMethod.js + // test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/overwriteMethod.js var require_overwriteMethod = __commonJS({ - "development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/overwriteMethod.js"(exports, module) { + "test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/overwriteMethod.js"(exports, module) { var addLengthGuard = require_addLengthGuard(); var chai2 = require_chai(); var flag = require_flag(); @@ -1662,9 +1662,9 @@ } }); - // development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/addChainableMethod.js + // test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/addChainableMethod.js var require_addChainableMethod = __commonJS({ - "development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/addChainableMethod.js"(exports, module) { + "test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/addChainableMethod.js"(exports, module) { var addLengthGuard = require_addLengthGuard(); var chai2 = require_chai(); var flag = require_flag(); @@ -1738,9 +1738,9 @@ } }); - // development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/overwriteChainableMethod.js + // test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/overwriteChainableMethod.js var require_overwriteChainableMethod = __commonJS({ - "development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/overwriteChainableMethod.js"(exports, module) { + "test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/overwriteChainableMethod.js"(exports, module) { var chai2 = require_chai(); var transferFlags = require_transferFlags(); module.exports = function overwriteChainableMethod(ctx, name, method, chainingBehavior) { @@ -1769,9 +1769,9 @@ } }); - // development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/compareByInspect.js + // test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/compareByInspect.js var require_compareByInspect = __commonJS({ - "development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/compareByInspect.js"(exports, module) { + "test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/compareByInspect.js"(exports, module) { var inspect = require_inspect(); module.exports = function compareByInspect(a2, b) { return inspect(a2) < inspect(b) ? -1 : 1; @@ -1779,9 +1779,9 @@ } }); - // development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/getOwnEnumerablePropertySymbols.js + // test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/getOwnEnumerablePropertySymbols.js var require_getOwnEnumerablePropertySymbols = __commonJS({ - "development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/getOwnEnumerablePropertySymbols.js"(exports, module) { + "test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/getOwnEnumerablePropertySymbols.js"(exports, module) { module.exports = function getOwnEnumerablePropertySymbols(obj) { if (typeof Object.getOwnPropertySymbols !== "function") return []; @@ -1792,9 +1792,9 @@ } }); - // development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/getOwnEnumerableProperties.js + // test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/getOwnEnumerableProperties.js var require_getOwnEnumerableProperties = __commonJS({ - "development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/getOwnEnumerableProperties.js"(exports, module) { + "test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/getOwnEnumerableProperties.js"(exports, module) { var getOwnEnumerablePropertySymbols = require_getOwnEnumerablePropertySymbols(); module.exports = function getOwnEnumerableProperties(obj) { return Object.keys(obj).concat(getOwnEnumerablePropertySymbols(obj)); @@ -1802,9 +1802,9 @@ } }); - // development/test/node_modules/.pnpm/check-error@1.0.2/node_modules/check-error/index.js + // test/node_modules/.pnpm/check-error@1.0.2/node_modules/check-error/index.js var require_check_error = __commonJS({ - "development/test/node_modules/.pnpm/check-error@1.0.2/node_modules/check-error/index.js"(exports, module) { + "test/node_modules/.pnpm/check-error@1.0.2/node_modules/check-error/index.js"(exports, module) { "use strict"; function compatibleInstance(thrown, errorLike) { return errorLike instanceof Error && thrown === errorLike; @@ -1867,9 +1867,9 @@ } }); - // development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/isNaN.js + // test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/isNaN.js var require_isNaN = __commonJS({ - "development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/isNaN.js"(exports, module) { + "test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/isNaN.js"(exports, module) { function isNaN2(value) { return value !== value; } @@ -1877,9 +1877,9 @@ } }); - // development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/getOperator.js + // test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/getOperator.js var require_getOperator = __commonJS({ - "development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/getOperator.js"(exports, module) { + "test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/getOperator.js"(exports, module) { var type = require_type_detect(); var flag = require_flag(); function isObjectType(obj) { @@ -1913,9 +1913,9 @@ } }); - // development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/index.js + // test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/index.js var require_utils = __commonJS({ - "development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/index.js"(exports) { + "test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/utils/index.js"(exports) { var pathval = require_pathval(); exports.test = require_test(); exports.type = require_type_detect(); @@ -1948,9 +1948,9 @@ } }); - // development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/assertion.js + // test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/assertion.js var require_assertion = __commonJS({ - "development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/assertion.js"(exports, module) { + "test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/assertion.js"(exports, module) { var config2 = require_config(); module.exports = function(_chai, util2) { var AssertionError2 = _chai.AssertionError, flag = util2.flag; @@ -2043,9 +2043,9 @@ } }); - // development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/core/assertions.js + // test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/core/assertions.js var require_assertions = __commonJS({ - "development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/core/assertions.js"(exports, module) { + "test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/core/assertions.js"(exports, module) { module.exports = function(chai2, _) { var Assertion2 = chai2.Assertion, AssertionError2 = chai2.AssertionError, flag = _.flag; [ @@ -3241,9 +3241,9 @@ } }); - // development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/interface/expect.js + // test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/interface/expect.js var require_expect = __commonJS({ - "development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/interface/expect.js"(exports, module) { + "test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/interface/expect.js"(exports, module) { module.exports = function(chai2, util2) { chai2.expect = function(val, message) { return new chai2.Assertion(val, message); @@ -3264,9 +3264,9 @@ } }); - // development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/interface/should.js + // test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/interface/should.js var require_should = __commonJS({ - "development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/interface/should.js"(exports, module) { + "test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/interface/should.js"(exports, module) { module.exports = function(chai2, util2) { var Assertion2 = chai2.Assertion; function loadShould() { @@ -3332,9 +3332,9 @@ } }); - // development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/interface/assert.js + // test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/interface/assert.js var require_assert = __commonJS({ - "development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/interface/assert.js"(exports, module) { + "test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai/interface/assert.js"(exports, module) { module.exports = function(chai2, util2) { var Assertion2 = chai2.Assertion, flag = util2.flag; var assert2 = chai2.assert = function(express, errmsg) { @@ -3895,9 +3895,9 @@ } }); - // development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai.js + // test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai.js var require_chai = __commonJS({ - "development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai.js"(exports) { + "test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/lib/chai.js"(exports) { var used = []; exports.version = "4.3.3"; exports.AssertionError = require_assertion_error(); @@ -3925,17 +3925,17 @@ } }); - // development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/index.js + // test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/index.js var require_chai2 = __commonJS({ - "development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/index.js"(exports, module) { + "test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/index.js"(exports, module) { module.exports = require_chai(); } }); - // application/source/types/base.mjs + // ../application/source/types/base.mjs var Base; var init_base = __esm({ - "application/source/types/base.mjs"() { + "../application/source/types/base.mjs"() { "use strict"; Base = class extends Object { toString() { @@ -3945,7 +3945,7 @@ } }); - // application/source/types/is.mjs + // ../application/source/types/is.mjs function isIterable(value) { if (value === void 0) return false; @@ -4015,12 +4015,12 @@ return Number.isInteger(value); } var init_is = __esm({ - "application/source/types/is.mjs"() { + "../application/source/types/is.mjs"() { "use strict"; } }); - // application/source/types/validate.mjs + // ../application/source/types/validate.mjs function validateIterable(value) { if (!isIterable(value)) { throw new TypeError("value is not iterable"); @@ -4089,13 +4089,13 @@ return value; } var init_validate = __esm({ - "application/source/types/validate.mjs"() { + "../application/source/types/validate.mjs"() { "use strict"; init_is(); } }); - // application/source/types/global.mjs + // ../application/source/types/global.mjs function getGlobal() { return globalReference; } @@ -4117,7 +4117,7 @@ } var globalReference; var init_global = __esm({ - "application/source/types/global.mjs"() { + "../application/source/types/global.mjs"() { "use strict"; init_validate(); (function() { @@ -4153,17 +4153,17 @@ } }); - // application/source/constants.mjs + // ../application/source/constants.mjs var internalSymbol, internalStateSymbol; var init_constants = __esm({ - "application/source/constants.mjs"() { + "../application/source/constants.mjs"() { "use strict"; internalSymbol = Symbol("internalData"); internalStateSymbol = Symbol("state"); } }); - // application/source/types/typeof.mjs + // ../application/source/types/typeof.mjs function typeOf(value) { let type = {}.toString.call(value).match(/\s([a-zA-Z]+)/)[1]; if ("Object" === type) { @@ -4177,12 +4177,12 @@ return type.toLowerCase(); } var init_typeof = __esm({ - "application/source/types/typeof.mjs"() { + "../application/source/types/typeof.mjs"() { "use strict"; } }); - // application/source/data/extend.mjs + // ../application/source/data/extend.mjs function extend() { let o, i; for (i = 0; i < arguments.length; i++) { @@ -4220,17 +4220,17 @@ return o; } var init_extend = __esm({ - "application/source/data/extend.mjs"() { + "../application/source/data/extend.mjs"() { "use strict"; init_is(); init_typeof(); } }); - // application/source/types/id.mjs + // ../application/source/types/id.mjs var internalCounter, ID; var init_id = __esm({ - "application/source/types/id.mjs"() { + "../application/source/types/id.mjs"() { "use strict"; init_base(); init_validate(); @@ -4256,7 +4256,7 @@ } }); - // application/source/util/clone.mjs + // ../application/source/util/clone.mjs function clone(obj) { if (null === obj) { return obj; @@ -4336,7 +4336,7 @@ return copy; } var init_clone = __esm({ - "application/source/util/clone.mjs"() { + "../application/source/util/clone.mjs"() { "use strict"; init_global(); init_is(); @@ -4345,10 +4345,10 @@ } }); - // application/source/types/stack.mjs + // ../application/source/types/stack.mjs var Stack; var init_stack = __esm({ - "application/source/types/stack.mjs"() { + "../application/source/types/stack.mjs"() { "use strict"; init_base(); Stack = class extends Base { @@ -4383,7 +4383,7 @@ } }); - // application/source/data/pathfinder.mjs + // ../application/source/data/pathfinder.mjs function iterate(subject, path, check) { const result = /* @__PURE__ */ new Map(); if (isObject(subject) || isArray(subject)) { @@ -4515,7 +4515,7 @@ } var DELIMITER, WILDCARD, Pathfinder; var init_pathfinder = __esm({ - "application/source/data/pathfinder.mjs"() { + "../application/source/data/pathfinder.mjs"() { "use strict"; init_base(); init_is(); @@ -4563,7 +4563,7 @@ } }); - // application/source/data/transformer.mjs + // ../application/source/data/transformer.mjs function disassemble(command) { validateString(command); let placeholder = /* @__PURE__ */ new Map(); @@ -4816,7 +4816,7 @@ } var Transformer; var init_transformer = __esm({ - "application/source/data/transformer.mjs"() { + "../application/source/data/transformer.mjs"() { "use strict"; init_base(); init_global(); @@ -4851,10 +4851,10 @@ } }); - // application/source/data/pipe.mjs + // ../application/source/data/pipe.mjs var DELIMITER2, Pipe; var init_pipe = __esm({ - "application/source/data/pipe.mjs"() { + "../application/source/data/pipe.mjs"() { "use strict"; init_base(); init_validate(); @@ -4883,10 +4883,10 @@ } }); - // application/source/dom/constants.mjs + // ../application/source/dom/constants.mjs var DEFAULT_THEME, ATTRIBUTE_PREFIX, ATTRIBUTE_OPTIONS, ATTRIBUTE_OPTIONS_SELECTOR, ATTRIBUTE_THEME_PREFIX, ATTRIBUTE_THEME_NAME, ATTRIBUTE_UPDATER_ATTRIBUTES, ATTRIBUTE_UPDATER_SELECT_THIS, ATTRIBUTE_UPDATER_REPLACE, ATTRIBUTE_UPDATER_INSERT, ATTRIBUTE_UPDATER_INSERT_REFERENCE, ATTRIBUTE_UPDATER_REMOVE, ATTRIBUTE_UPDATER_BIND, ATTRIBUTE_TEMPLATE_PREFIX, ATTRIBUTE_ROLE, ATTRIBUTE_DISABLED, ATTRIBUTE_VALUE, ATTRIBUTE_OBJECTLINK, ATTRIBUTE_ERRORMESSAGE, objectUpdaterLinkSymbol, TAG_SCRIPT, TAG_LINK, ATTRIBUTE_ID, ATTRIBUTE_CLASS, ATTRIBUTE_TITLE, ATTRIBUTE_SRC, ATTRIBUTE_HREF, ATTRIBUTE_TYPE, ATTRIBUTE_NONCE; var init_constants2 = __esm({ - "application/source/dom/constants.mjs"() { + "../application/source/dom/constants.mjs"() { "use strict"; DEFAULT_THEME = "monster"; ATTRIBUTE_PREFIX = "data-monster-"; @@ -4920,7 +4920,7 @@ } }); - // application/source/types/tokenlist.mjs + // ../application/source/types/tokenlist.mjs function toggleValue(token) { if (!(this instanceof TokenList)) throw Error("must be called with TokenList.call"); @@ -4935,7 +4935,7 @@ } var TokenList; var init_tokenlist = __esm({ - "application/source/types/tokenlist.mjs"() { + "../application/source/types/tokenlist.mjs"() { "use strict"; init_is(); init_validate(); @@ -5065,10 +5065,10 @@ } }); - // application/source/types/queue.mjs + // ../application/source/types/queue.mjs var Queue; var init_queue = __esm({ - "application/source/types/queue.mjs"() { + "../application/source/types/queue.mjs"() { "use strict"; init_base(); Queue = class extends Base { @@ -5103,10 +5103,10 @@ } }); - // application/source/types/uniquequeue.mjs + // ../application/source/types/uniquequeue.mjs var UniqueQueue; var init_uniquequeue = __esm({ - "application/source/types/uniquequeue.mjs"() { + "../application/source/types/uniquequeue.mjs"() { "use strict"; init_queue(); init_validate(); @@ -5140,10 +5140,10 @@ } }); - // application/source/types/observer.mjs + // ../application/source/types/observer.mjs var Observer; var init_observer = __esm({ - "application/source/types/observer.mjs"() { + "../application/source/types/observer.mjs"() { "use strict"; init_base(); init_is(); @@ -5205,10 +5205,10 @@ } }); - // application/source/types/observerlist.mjs + // ../application/source/types/observerlist.mjs var ObserverList; var init_observerlist = __esm({ - "application/source/types/observerlist.mjs"() { + "../application/source/types/observerlist.mjs"() { "use strict"; init_base(); init_observer(); @@ -5255,7 +5255,7 @@ } }); - // application/source/types/proxyobserver.mjs + // ../application/source/types/proxyobserver.mjs function getHandler() { const proxy = this; const handler = { @@ -5340,7 +5340,7 @@ } var ProxyObserver; var init_proxyobserver = __esm({ - "application/source/types/proxyobserver.mjs"() { + "../application/source/types/proxyobserver.mjs"() { "use strict"; init_base(); init_is(); @@ -5391,7 +5391,7 @@ } }); - // application/source/types/mediatype.mjs + // ../application/source/types/mediatype.mjs function parseMediaType(mediatype) { const regex = /(?<type>[A-Za-z]+|\*)\/(?<subtype>([a-zA-Z0-9.\+_\-]+)|\*|)(?<parameter>\s*;\s*([a-zA-Z0-9]+)\s*(=\s*("?[A-Za-z0-9_\-]+"?))?)*/g; const result = regex.exec(validateString(mediatype)); @@ -5429,7 +5429,7 @@ } var internal, MediaType; var init_mediatype = __esm({ - "application/source/types/mediatype.mjs"() { + "../application/source/types/mediatype.mjs"() { "use strict"; init_base(); init_is(); @@ -5478,7 +5478,7 @@ } }); - // application/source/types/dataurl.mjs + // ../application/source/types/dataurl.mjs function parseDataURL(dataurl) { validateString(dataurl); dataurl = dataurl.trim(); @@ -5511,7 +5511,7 @@ } var internal2, DataUrl; var init_dataurl = __esm({ - "application/source/types/dataurl.mjs"() { + "../application/source/types/dataurl.mjs"() { "use strict"; init_base(); init_is(); @@ -5549,7 +5549,7 @@ } }); - // application/source/data/diff.mjs + // ../application/source/data/diff.mjs function diff(first, second) { return doDiff(first, second); } @@ -5638,14 +5638,14 @@ return operator; } var init_diff = __esm({ - "application/source/data/diff.mjs"() { + "../application/source/data/diff.mjs"() { "use strict"; init_is(); init_typeof(); } }); - // application/source/util/trimspaces.mjs + // ../application/source/util/trimspaces.mjs function trimSpaces(value) { validateString(value); let placeholder = /* @__PURE__ */ new Map(); @@ -5671,7 +5671,7 @@ return value; } var init_trimspaces = __esm({ - "application/source/util/trimspaces.mjs"() { + "../application/source/util/trimspaces.mjs"() { "use strict"; init_id(); init_is(); @@ -5679,7 +5679,7 @@ } }); - // application/source/dom/util.mjs + // ../application/source/dom/util.mjs function getDocument() { let document2 = getGlobal()?.["document"]; if (typeof document2 !== "object") { @@ -5702,14 +5702,14 @@ return template.content; } var init_util = __esm({ - "application/source/dom/util.mjs"() { + "../application/source/dom/util.mjs"() { "use strict"; init_global(); init_validate(); } }); - // application/source/dom/events.mjs + // ../application/source/dom/events.mjs function fireEvent(element2, type) { const document2 = getDocument(); if (element2 instanceof HTMLElement) { @@ -5767,7 +5767,7 @@ return void 0; } var init_events = __esm({ - "application/source/dom/events.mjs"() { + "../application/source/dom/events.mjs"() { "use strict"; init_is(); init_validate(); @@ -5775,7 +5775,7 @@ } }); - // application/source/dom/theme.mjs + // ../application/source/dom/theme.mjs function getDocumentTheme() { let document2 = getGlobalObject("document"); let name = DEFAULT_THEME; @@ -5790,7 +5790,7 @@ } var Theme; var init_theme = __esm({ - "application/source/dom/theme.mjs"() { + "../application/source/dom/theme.mjs"() { "use strict"; init_base(); init_global(); @@ -5809,7 +5809,7 @@ } }); - // application/source/dom/template.mjs + // ../application/source/dom/template.mjs function findDocumentTemplate(id, currentNode) { validateString(id); const document2 = getGlobalObject("document"); @@ -5865,7 +5865,7 @@ } var Template; var init_template = __esm({ - "application/source/dom/template.mjs"() { + "../application/source/dom/template.mjs"() { "use strict"; init_base(); init_global(); @@ -5889,7 +5889,7 @@ } }); - // application/source/dom/updater.mjs + // ../application/source/dom/updater.mjs function getCheckStateCallback() { const self2 = this; return function(current) { @@ -6274,7 +6274,7 @@ } var Updater, symbol; var init_updater = __esm({ - "application/source/dom/updater.mjs"() { + "../application/source/dom/updater.mjs"() { "use strict"; init_constants(); init_diff(); @@ -6359,7 +6359,7 @@ } }); - // application/source/dom/attributes.mjs + // ../application/source/dom/attributes.mjs function findClosestObjectLink(element2) { return findClosestByAttribute(element2, ATTRIBUTE_OBJECTLINK); } @@ -6491,7 +6491,7 @@ return void 0; } var init_attributes = __esm({ - "application/source/dom/attributes.mjs"() { + "../application/source/dom/attributes.mjs"() { "use strict"; init_global(); init_tokenlist(); @@ -6500,7 +6500,7 @@ } }); - // application/source/dom/customelement.mjs + // ../application/source/dom/customelement.mjs var customelement_exports = {}; __export(customelement_exports, { CustomElement: () => CustomElement, @@ -6771,7 +6771,7 @@ } var initMethodSymbol, assembleMethodSymbol, attributeObserverSymbol, CustomElement; var init_customelement = __esm({ - "application/source/dom/customelement.mjs"() { + "../application/source/dom/customelement.mjs"() { "use strict"; init_constants(); init_extend(); @@ -6924,7 +6924,7 @@ } }); - // application/source/dom/customcontrol.mjs + // ../application/source/dom/customcontrol.mjs var customcontrol_exports = {}; __export(customcontrol_exports, { CustomControl: () => CustomControl @@ -6944,7 +6944,7 @@ } var attachedInternalSymbol, CustomControl; var init_customcontrol = __esm({ - "application/source/dom/customcontrol.mjs"() { + "../application/source/dom/customcontrol.mjs"() { "use strict"; init_extend(); init_constants2(); @@ -7015,7 +7015,7 @@ } }); - // application/source/dom/ready.mjs + // ../application/source/dom/ready.mjs var ready_exports = {}; __export(ready_exports, { domReady: () => domReady, @@ -7023,7 +7023,7 @@ }); var domReady, windowReady; var init_ready = __esm({ - "application/source/dom/ready.mjs"() { + "../application/source/dom/ready.mjs"() { "use strict"; init_util(); domReady = new Promise((resolve) => { @@ -7046,9 +7046,9 @@ } }); - // development/test/node_modules/.pnpm/pvtsutils@1.3.2/node_modules/pvtsutils/build/index.js + // test/node_modules/.pnpm/pvtsutils@1.3.2/node_modules/pvtsutils/build/index.js var require_build = __commonJS({ - "development/test/node_modules/.pnpm/pvtsutils@1.3.2/node_modules/pvtsutils/build/index.js"(exports) { + "test/node_modules/.pnpm/pvtsutils@1.3.2/node_modules/pvtsutils/build/index.js"(exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var ARRAY_BUFFER_NAME = "[object ArrayBuffer]"; @@ -7395,7 +7395,7 @@ } }); - // development/test/node_modules/.pnpm/pvutils@1.1.3/node_modules/pvutils/build/utils.es.js + // test/node_modules/.pnpm/pvutils@1.1.3/node_modules/pvutils/build/utils.es.js function utilFromBase(inputBuffer, inputBase) { let result = 0; if (inputBuffer.length === 1) { @@ -7533,12 +7533,12 @@ } var log2; var init_utils_es = __esm({ - "development/test/node_modules/.pnpm/pvutils@1.1.3/node_modules/pvutils/build/utils.es.js"() { + "test/node_modules/.pnpm/pvutils@1.1.3/node_modules/pvutils/build/utils.es.js"() { log2 = Math.log(2); } }); - // development/test/node_modules/.pnpm/asn1js@3.0.5/node_modules/asn1js/build/index.es.js + // test/node_modules/.pnpm/asn1js@3.0.5/node_modules/asn1js/build/index.es.js var index_es_exports = {}; __export(index_es_exports, { Any: () => Any, @@ -8310,7 +8310,7 @@ } var pvtsutils, ViewWriter, powers2, digitsString, NAME, VALUE_HEX_VIEW, IS_HEX_ONLY, ID_BLOCK, TAG_CLASS, TAG_NUMBER, IS_CONSTRUCTED, FROM_BER, TO_BER, LOCAL, EMPTY_STRING, EMPTY_BUFFER, EMPTY_VIEW, END_OF_CONTENT_NAME, OCTET_STRING_NAME, BIT_STRING_NAME, LocalBaseBlock, ValueBlock, LocalIdentificationBlock, LocalLengthBlock, typeStore, BaseBlock, BaseStringBlock, LocalPrimitiveValueBlock, _a$w, Primitive, LocalConstructedValueBlock, _a$v, Constructed, LocalEndOfContentValueBlock, _a$u, EndOfContent, _a$t, Null, LocalBooleanValueBlock, _a$s, Boolean2, LocalOctetStringValueBlock, _a$r, OctetString, LocalBitStringValueBlock, _a$q, BitString, _a$p, LocalIntegerValueBlock, _a$o, Integer, _a$n, Enumerated, LocalSidValueBlock, LocalObjectIdentifierValueBlock, _a$m, ObjectIdentifier, LocalRelativeSidValueBlock, LocalRelativeObjectIdentifierValueBlock, _a$l, RelativeObjectIdentifier, _a$k, Sequence, _a$j, Set2, LocalStringValueBlock, LocalSimpleStringValueBlock, LocalSimpleStringBlock, LocalUtf8StringValueBlock, _a$i, Utf8String, LocalBmpStringValueBlock, _a$h, BmpString, LocalUniversalStringValueBlock, _a$g, UniversalString, _a$f, NumericString, _a$e, PrintableString, _a$d, TeletexString, _a$c, VideotexString, _a$b, IA5String, _a$a, GraphicString, _a$9, VisibleString, _a$8, GeneralString, _a$7, CharacterString, _a$6, UTCTime, _a$5, GeneralizedTime, _a$4, DATE, _a$3, TimeOfDay, _a$2, DateTime, _a$1, Duration, _a, TIME, Any, Choice, Repeated, RawData; var init_index_es = __esm({ - "development/test/node_modules/.pnpm/asn1js@3.0.5/node_modules/asn1js/build/index.es.js"() { + "test/node_modules/.pnpm/asn1js@3.0.5/node_modules/asn1js/build/index.es.js"() { pvtsutils = __toESM(require_build()); init_utils_es(); ViewWriter = class { @@ -10537,10 +10537,10 @@ ${values.join("\n")}` : `${blockName} :`; } }); - // development/test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/enums.js + // test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/enums.js var AsnTypeTypes, AsnPropTypes; var init_enums = __esm({ - "development/test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/enums.js"() { + "test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/enums.js"() { (function(AsnTypeTypes2) { AsnTypeTypes2[AsnTypeTypes2["Sequence"] = 0] = "Sequence"; AsnTypeTypes2[AsnTypeTypes2["Set"] = 1] = "Set"; @@ -10578,7 +10578,7 @@ ${values.join("\n")}` : `${blockName} :`; } }); - // development/test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/converters.js + // test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/converters.js function createStringConverter(Asn1Type) { return { fromASN: (value) => value.valueBlock.value, @@ -10637,7 +10637,7 @@ ${values.join("\n")}` : `${blockName} :`; } var AsnAnyConverter, AsnIntegerConverter, AsnEnumeratedConverter, AsnBitStringConverter, AsnObjectIdentifierConverter, AsnBooleanConverter, AsnOctetStringConverter, AsnUtf8StringConverter, AsnBmpStringConverter, AsnUniversalStringConverter, AsnNumericStringConverter, AsnPrintableStringConverter, AsnTeletexStringConverter, AsnVideotexStringConverter, AsnIA5StringConverter, AsnGraphicStringConverter, AsnVisibleStringConverter, AsnGeneralStringConverter, AsnCharacterStringConverter, AsnUTCTimeConverter, AsnGeneralizedTimeConverter, AsnNullConverter; var init_converters = __esm({ - "development/test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/converters.js"() { + "test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/converters.js"() { init_index_es(); init_enums(); AsnAnyConverter = { @@ -10706,33 +10706,33 @@ ${values.join("\n")}` : `${blockName} :`; } }); - // development/test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/types/bit_string.js + // test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/types/bit_string.js var import_pvtsutils; var init_bit_string = __esm({ - "development/test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/types/bit_string.js"() { + "test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/types/bit_string.js"() { init_index_es(); import_pvtsutils = __toESM(require_build()); } }); - // development/test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/types/octet_string.js + // test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/types/octet_string.js var import_pvtsutils2; var init_octet_string = __esm({ - "development/test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/types/octet_string.js"() { + "test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/types/octet_string.js"() { init_index_es(); import_pvtsutils2 = __toESM(require_build()); } }); - // development/test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/types/index.js + // test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/types/index.js var init_types = __esm({ - "development/test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/types/index.js"() { + "test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/types/index.js"() { init_bit_string(); init_octet_string(); } }); - // development/test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/helper.js + // test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/helper.js function isConvertible(target) { if (typeof target === "function" && target.prototype) { if (target.prototype.toASN && target.prototype.fromASN) { @@ -10772,14 +10772,14 @@ ${values.join("\n")}` : `${blockName} :`; return true; } var init_helper = __esm({ - "development/test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/helper.js"() { + "test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/helper.js"() { } }); - // development/test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/schema.js + // test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/schema.js var AsnSchemaStorage; var init_schema = __esm({ - "development/test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/schema.js"() { + "test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/schema.js"() { init_index_es(); init_enums(); init_helper(); @@ -10929,19 +10929,19 @@ ${values.join("\n")}` : `${blockName} :`; } }); - // development/test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/storage.js + // test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/storage.js var schemaStorage; var init_storage = __esm({ - "development/test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/storage.js"() { + "test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/storage.js"() { init_schema(); schemaStorage = new AsnSchemaStorage(); } }); - // development/test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/decorators.js + // test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/decorators.js var AsnType, AsnProp; var init_decorators = __esm({ - "development/test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/decorators.js"() { + "test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/decorators.js"() { init_converters(); init_enums(); init_storage(); @@ -10976,10 +10976,10 @@ ${values.join("\n")}` : `${blockName} :`; } }); - // development/test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/errors/schema_validation.js + // test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/errors/schema_validation.js var AsnSchemaValidationError; var init_schema_validation = __esm({ - "development/test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/errors/schema_validation.js"() { + "test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/errors/schema_validation.js"() { AsnSchemaValidationError = class extends Error { constructor() { super(...arguments); @@ -10989,17 +10989,17 @@ ${values.join("\n")}` : `${blockName} :`; } }); - // development/test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/errors/index.js + // test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/errors/index.js var init_errors = __esm({ - "development/test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/errors/index.js"() { + "test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/errors/index.js"() { init_schema_validation(); } }); - // development/test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/parser.js + // test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/parser.js var AsnParser; var init_parser = __esm({ - "development/test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/parser.js"() { + "test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/parser.js"() { init_index_es(); init_enums(); init_converters(); @@ -11128,10 +11128,10 @@ ${values.join("\n")}` : `${blockName} :`; } }); - // development/test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/serializer.js + // test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/serializer.js var AsnSerializer; var init_serializer = __esm({ - "development/test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/serializer.js"() { + "test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/serializer.js"() { init_index_es(); init_converters(); init_enums(); @@ -11272,16 +11272,16 @@ ${values.join("\n")}` : `${blockName} :`; } }); - // development/test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/objects.js + // test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/objects.js var init_objects = __esm({ - "development/test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/objects.js"() { + "test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/objects.js"() { } }); - // development/test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/convert.js + // test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/convert.js var import_pvtsutils3, AsnConvert; var init_convert = __esm({ - "development/test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/convert.js"() { + "test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/convert.js"() { init_index_es(); import_pvtsutils3 = __toESM(require_build()); init_parser(); @@ -11305,9 +11305,9 @@ ${values.join("\n")}` : `${blockName} :`; } }); - // development/test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/index.js + // test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/index.js var init_es2015 = __esm({ - "development/test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/index.js"() { + "test/node_modules/.pnpm/@peculiar+asn1-schema@2.3.0/node_modules/@peculiar/asn1-schema/build/es2015/index.js"() { init_converters(); init_types(); init_decorators(); @@ -11320,9 +11320,9 @@ ${values.join("\n")}` : `${blockName} :`; } }); - // development/test/node_modules/.pnpm/tslib@2.4.0/node_modules/tslib/tslib.js + // test/node_modules/.pnpm/tslib@2.4.0/node_modules/tslib/tslib.js var require_tslib = __commonJS({ - "development/test/node_modules/.pnpm/tslib@2.4.0/node_modules/tslib/tslib.js"(exports, module) { + "test/node_modules/.pnpm/tslib@2.4.0/node_modules/tslib/tslib.js"(exports, module) { var __extends2; var __assign2; var __rest2; @@ -11760,10 +11760,10 @@ ${values.join("\n")}` : `${blockName} :`; } }); - // development/test/node_modules/.pnpm/tslib@2.4.0/node_modules/tslib/modules/index.js + // test/node_modules/.pnpm/tslib@2.4.0/node_modules/tslib/modules/index.js var import_tslib, __extends, __assign, __rest, __decorate, __param, __metadata, __awaiter, __generator, __exportStar, __createBinding, __values, __read, __spread, __spreadArrays, __spreadArray, __await, __asyncGenerator, __asyncDelegator, __asyncValues, __makeTemplateObject, __importStar, __importDefault, __classPrivateFieldGet, __classPrivateFieldSet, __classPrivateFieldIn; var init_modules = __esm({ - "development/test/node_modules/.pnpm/tslib@2.4.0/node_modules/tslib/modules/index.js"() { + "test/node_modules/.pnpm/tslib@2.4.0/node_modules/tslib/modules/index.js"() { import_tslib = __toESM(require_tslib(), 1); ({ __extends, @@ -11795,7 +11795,7 @@ ${values.join("\n")}` : `${blockName} :`; } }); - // development/test/node_modules/.pnpm/@peculiar+json-schema@1.1.12/node_modules/@peculiar/json-schema/build/index.es.js + // test/node_modules/.pnpm/@peculiar+json-schema@1.1.12/node_modules/@peculiar/json-schema/build/index.es.js function checkType(value, type) { switch (type) { case JsonPropTypes.Boolean: @@ -11848,7 +11848,7 @@ ${values.join("\n")}` : `${blockName} :`; } var JsonError, TransformError, ParserError, ValidationError, SerializerError, KeyError, JsonPropTypes, JsonSchemaStorage, DEFAULT_SCHEMA, schemaStorage2, PatternValidation, InclusiveValidation, ExclusiveValidation, LengthValidation, EnumerationValidation, JsonTransform, JsonSerializer, JsonParser, JsonProp; var init_index_es2 = __esm({ - "development/test/node_modules/.pnpm/@peculiar+json-schema@1.1.12/node_modules/@peculiar/json-schema/build/index.es.js"() { + "test/node_modules/.pnpm/@peculiar+json-schema@1.1.12/node_modules/@peculiar/json-schema/build/index.es.js"() { JsonError = class extends Error { constructor(message, innerError) { super(innerError ? `${message}. See the inner exception for more details.` : message); @@ -12238,13 +12238,13 @@ ${values.join("\n")}` : `${blockName} :`; } }); - // development/test/node_modules/.pnpm/webcrypto-core@1.7.5/node_modules/webcrypto-core/build/webcrypto-core.es.js + // test/node_modules/.pnpm/webcrypto-core@1.7.5/node_modules/webcrypto-core/build/webcrypto-core.es.js function isJWK(data) { return typeof data === "object" && "kty" in data; } var import_pvtsutils4, import_pvtsutils5, CryptoError, AlgorithmError, UnsupportedOperationError, OperationError, RequiredPropertyError, ProviderCrypto, AesProvider, AesCbcProvider, AesCmacProvider, AesCtrProvider, AesEcbProvider, AesGcmProvider, AesKwProvider, DesProvider, RsaProvider, RsaSsaProvider, RsaPssProvider, RsaOaepProvider, EllipticProvider, EcdsaProvider, KEY_TYPES, CryptoKey, EcdhProvider, EcdhEsProvider, EdDsaProvider, ObjectIdentifier2, AlgorithmIdentifier, PrivateKeyInfo, PublicKeyInfo, JsonBase64UrlArrayBufferConverter, AsnIntegerArrayBufferConverter, RsaPrivateKey, RsaPublicKey, EcPublicKey, EcPrivateKey, AsnIntegerWithoutPaddingConverter, index$2, EcUtils, EcDsaSignature, OneAsymmetricKey, EdPrivateKey, EdPublicKey, CurvePrivateKey, idSecp256r1, idEllipticCurve, idSecp384r1, idSecp521r1, idSecp256k1, idVersionOne, idBrainpoolP160r1, idBrainpoolP160t1, idBrainpoolP192r1, idBrainpoolP192t1, idBrainpoolP224r1, idBrainpoolP224t1, idBrainpoolP256r1, idBrainpoolP256t1, idBrainpoolP320r1, idBrainpoolP320t1, idBrainpoolP384r1, idBrainpoolP384t1, idBrainpoolP512r1, idBrainpoolP512t1, idX25519, idX448, idEd25519, idEd448, index$1, EcCurves, HmacProvider, Pbkdf2Provider, HkdfProvider, ShakeProvider, Shake128Provider, Shake256Provider, Crypto, ProviderStorage, SubtleCrypto; var init_webcrypto_core_es = __esm({ - "development/test/node_modules/.pnpm/webcrypto-core@1.7.5/node_modules/webcrypto-core/build/webcrypto-core.es.js"() { + "test/node_modules/.pnpm/webcrypto-core@1.7.5/node_modules/webcrypto-core/build/webcrypto-core.es.js"() { import_pvtsutils4 = __toESM(require_build()); import_pvtsutils5 = __toESM(require_build()); init_es2015(); @@ -13629,7 +13629,7 @@ ${values.join("\n")}` : `${blockName} :`; } }); - // development/test/node_modules/.pnpm/@peculiar+webcrypto@1.4.0/node_modules/@peculiar/webcrypto/build/webcrypto.es.js + // test/node_modules/.pnpm/@peculiar+webcrypto@1.4.0/node_modules/@peculiar/webcrypto/build/webcrypto.es.js var webcrypto_es_exports = {}; __export(webcrypto_es_exports, { Crypto: () => Crypto2, @@ -13760,7 +13760,7 @@ ${values.join("\n")}` : `${blockName} :`; } var crypto, import_crypto, process2, import_pvtsutils6, JsonBase64UrlConverter, CryptoKey2, SymmetricKey, AsymmetricKey, AesCryptoKey, keyStorage, AesCrypto, AesCbcProvider2, zero, rb, blockSize, AesCmacProvider2, AesCtrProvider2, AesGcmProvider2, AesKwProvider2, AesEcbProvider2, DesCryptoKey, DesCrypto, DesCbcProvider, DesEde3CbcProvider, RsaPrivateKey2, RsaPublicKey2, RsaCrypto, RsaSsaProvider2, RsaPssProvider2, ShaCrypto, RsaOaepProvider2, RsaEsProvider, namedOIDs, EcPrivateKey2, EcPublicKey3, Sha1Provider, Sha256Provider, Sha384Provider, Sha512Provider, Sha3256Provider, Sha3384Provider, Sha3512Provider, EcCrypto, EcdsaProvider2, EcdhProvider2, edOIDs, EdPrivateKey3, EdPublicKey3, EdCrypto, EdDsaProvider2, EcdhEsProvider2, PbkdfCryptoKey, Pbkdf2Provider2, HmacCryptoKey, HmacProvider2, HkdfCryptoKey, HkdfProvider2, ShakeCrypto, Shake128Provider2, Shake256Provider2, SubtleCrypto2, Crypto2; var init_webcrypto_es = __esm({ - "development/test/node_modules/.pnpm/@peculiar+webcrypto@1.4.0/node_modules/@peculiar/webcrypto/build/webcrypto.es.js"() { + "test/node_modules/.pnpm/@peculiar+webcrypto@1.4.0/node_modules/@peculiar/webcrypto/build/webcrypto.es.js"() { init_webcrypto_core_es(); init_webcrypto_core_es(); init_webcrypto_core_es(); @@ -15880,7 +15880,7 @@ ${key.data.toString("base64")} } }); - // application/source/math/random.mjs + // ../application/source/math/random.mjs var random_exports = {}; __export(random_exports, { random: () => random @@ -15933,7 +15933,7 @@ ${key.data.toString("base64")} } var MAX; var init_random = __esm({ - "application/source/math/random.mjs"() { + "../application/source/math/random.mjs"() { "use strict"; init_global(); MAX = 1e9; @@ -15943,14 +15943,14 @@ ${key.data.toString("base64")} } }); - // application/source/types/randomid.mjs + // ../application/source/types/randomid.mjs var randomid_exports = {}; __export(randomid_exports, { RandomID: () => RandomID }); var internalCounter2, RandomID; var init_randomid = __esm({ - "application/source/types/randomid.mjs"() { + "../application/source/types/randomid.mjs"() { "use strict"; init_random(); init_global(); @@ -15966,7 +15966,7 @@ ${key.data.toString("base64")} } }); - // application/source/types/uuid.mjs + // ../application/source/types/uuid.mjs var uuid_exports = {}; __export(uuid_exports, { UUID: () => UUID @@ -15987,7 +15987,7 @@ ${key.data.toString("base64")} } var UUID; var init_uuid = __esm({ - "application/source/types/uuid.mjs"() { + "../application/source/types/uuid.mjs"() { "use strict"; init_constants(); init_random(); @@ -16015,7 +16015,7 @@ ${key.data.toString("base64")} } }); - // development/test/web/prepare.js + // test/web/prepare.js if (typeof window === "object") { (function() { let sayswho = function() { @@ -16039,7 +16039,7 @@ ${key.data.toString("base64")} })(); } - // development/test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/index.mjs + // test/node_modules/.pnpm/chai@4.3.6/node_modules/chai/index.mjs var import_index = __toESM(require_chai2(), 1); var expect = import_index.default.expect; var version = import_index.default.version; @@ -16053,7 +16053,7 @@ ${key.data.toString("base64")} var core = import_index.default.core; var chai_default = import_index.default; - // application/source/types/version.mjs + // ../application/source/types/version.mjs init_base(); var Version = class extends Base { constructor(major, minor, patch) { @@ -16121,7 +16121,7 @@ ${key.data.toString("base64")} return monsterVersion; } - // development/test/cases/monster.mjs + // test/cases/monster.mjs describe("Monster", function() { describe(".getMonsterVersion()", function() { let monsterVersion2; @@ -16133,11 +16133,11 @@ ${key.data.toString("base64")} }); }); - // application/source/logging/handler.mjs + // ../application/source/logging/handler.mjs init_base(); init_validate(); - // application/source/logging/logentry.mjs + // ../application/source/logging/logentry.mjs init_base(); init_validate(); var LogEntry = class extends Base { @@ -16155,7 +16155,7 @@ ${key.data.toString("base64")} } }; - // application/source/logging/handler.mjs + // ../application/source/logging/handler.mjs var Handler = class extends Base { constructor() { super(); @@ -16210,7 +16210,7 @@ ${key.data.toString("base64")} } }; - // application/source/logging/logger.mjs + // ../application/source/logging/logger.mjs init_base(); init_validate(); var ALL = 255; @@ -16315,7 +16315,7 @@ ${key.data.toString("base64")} return logger; } - // development/test/cases/logging/logger.mjs + // test/cases/logging/logger.mjs describe("Logging", function() { describe("new Logger", function() { it("should return instanceof Logger", function() { @@ -16390,7 +16390,7 @@ ${key.data.toString("base64")} }); }); - // development/test/cases/logging/logentry.mjs + // test/cases/logging/logentry.mjs describe("Log", function() { describe("new Log", function() { it("should return instanceof Log", function() { @@ -16411,7 +16411,7 @@ ${key.data.toString("base64")} }); }); - // application/source/logging/handler/console.mjs + // ../application/source/logging/handler/console.mjs init_base(); init_global(); var ConsoleHandler = class extends Handler { @@ -16430,7 +16430,7 @@ ${key.data.toString("base64")} } }; - // development/test/cases/logging/handler/console.mjs + // test/cases/logging/handler/console.mjs describe("Logging", function() { describe("ConsoleHandler", function() { it("should create ConsoleHandler", function() { @@ -16442,7 +16442,7 @@ ${key.data.toString("base64")} }); }); - // development/test/cases/logging/handler.mjs + // test/cases/logging/handler.mjs describe("Logging", function() { describe("Handler.setLogLevel().getLogLevel()", function() { [ @@ -16573,12 +16573,12 @@ ${key.data.toString("base64")} }); }); - // application/source/text/formatter.mjs + // ../application/source/text/formatter.mjs init_constants(); init_extend(); init_pipe(); - // application/source/types/basewithoptions.mjs + // ../application/source/types/basewithoptions.mjs init_constants(); init_extend(); init_pathfinder(); @@ -16607,7 +16607,7 @@ ${key.data.toString("base64")} } }; - // application/source/text/formatter.mjs + // ../application/source/text/formatter.mjs init_is(); init_validate(); var internalObjectSymbol = Symbol("internalObject"); @@ -16747,7 +16747,7 @@ ${key.data.toString("base64")} return formatted.join(""); } - // development/test/cases/text/formatter.mjs + // test/cases/text/formatter.mjs describe("Formatter", function() { describe("examples", function() { it("rfc example should run", function() { @@ -16890,14 +16890,14 @@ ${key.data.toString("base64")} }); }); - // application/source/dom/resource/link/stylesheet.mjs + // ../application/source/dom/resource/link/stylesheet.mjs init_extend(); - // application/source/dom/resource/link.mjs + // ../application/source/dom/resource/link.mjs init_extend(); init_constants2(); - // application/source/dom/resource.mjs + // ../application/source/dom/resource.mjs init_constants(); init_extend(); init_global(); @@ -17017,7 +17017,7 @@ ${key.data.toString("base64")} return self2; } - // application/source/dom/resource/link.mjs + // ../application/source/dom/resource/link.mjs var Link = class extends Resource { get defaults() { return extend({}, super.defaults, { @@ -17058,7 +17058,7 @@ ${key.data.toString("base64")} return self2; } - // application/source/dom/resource/link/stylesheet.mjs + // ../application/source/dom/resource/link/stylesheet.mjs var Stylesheet = class extends Link { get defaults() { return extend({}, super.defaults, { @@ -17067,11 +17067,11 @@ ${key.data.toString("base64")} } }; - // development/test/cases/dom/resource/link/stylesheet.mjs + // test/cases/dom/resource/link/stylesheet.mjs init_dataurl(); init_id(); - // development/test/util/chai-dom.mjs + // test/util/chai-dom.mjs function chaiDom(chai2, utils) { var flag = utils.flag, elToString = function(el) { var desc; @@ -17380,7 +17380,7 @@ ${key.data.toString("base64")} }); } - // development/test/util/cleanupdom.mjs + // test/util/cleanupdom.mjs var addedNodes; var mutationobserver; function init() { @@ -17410,7 +17410,7 @@ ${key.data.toString("base64")} } } - // development/test/util/jsdom.mjs + // test/util/jsdom.mjs init_extend(); init_global(); var isBrowser = new Function("try {return this===window;}catch(e){ return false;}"); @@ -17478,7 +17478,7 @@ ${key.data.toString("base64")} }); } - // development/test/cases/dom/resource/link/stylesheet.mjs + // test/cases/dom/resource/link/stylesheet.mjs var expect2 = chai_default.expect; chai_default.use(chaiDom); describe("Stylesheet", function() { @@ -17537,7 +17537,7 @@ ${key.data.toString("base64")} }); }); - // application/source/dom/resource/data.mjs + // ../application/source/dom/resource/data.mjs init_constants(); init_extend(); init_global(); @@ -17609,7 +17609,7 @@ ${key.data.toString("base64")} return self2; } - // development/test/cases/dom/resource/data.mjs + // test/cases/dom/resource/data.mjs init_dataurl(); init_id(); var expect3 = chai_default.expect; @@ -17687,7 +17687,7 @@ ${key.data.toString("base64")} }); }); - // development/test/cases/dom/resource/link.mjs + // test/cases/dom/resource/link.mjs init_dataurl(); init_id(); var expect4 = chai_default.expect; @@ -17748,7 +17748,7 @@ ${key.data.toString("base64")} }); }); - // application/source/dom/resource/script.mjs + // ../application/source/dom/resource/script.mjs init_extend(); init_constants2(); var Script = class extends Resource { @@ -17784,7 +17784,7 @@ ${key.data.toString("base64")} return self2; } - // development/test/cases/dom/resource/script.mjs + // test/cases/dom/resource/script.mjs init_dataurl(); init_id(); var expect5 = chai_default.expect; @@ -17858,7 +17858,7 @@ ${key.data.toString("base64")} }); }); - // development/test/cases/dom/updater.mjs + // test/cases/dom/updater.mjs init_updater(); init_id(); init_observer(); @@ -18425,7 +18425,7 @@ ${key.data.toString("base64")} }); }); - // development/test/cases/dom/customcontrol.mjs + // test/cases/dom/customcontrol.mjs init_constants2(); init_util(); var expect7 = chai_default.expect; @@ -18590,7 +18590,7 @@ ${key.data.toString("base64")} }); }); - // application/source/i18n/locale.mjs + // ../application/source/i18n/locale.mjs init_base(); init_validate(); init_clone(); @@ -18684,7 +18684,7 @@ ${key.data.toString("base64")} return new Locale(language, region, script, variants, extlang); } - // application/source/dom/locale.mjs + // ../application/source/dom/locale.mjs init_util(); var DEFAULT_LANGUAGE = "en"; function getLocaleOfDocument() { @@ -18699,7 +18699,7 @@ ${key.data.toString("base64")} return parseLocale(DEFAULT_LANGUAGE); } - // development/test/cases/dom/locale.mjs + // test/cases/dom/locale.mjs describe("Attributes", function() { before(function(done) { initJSDOM().then(() => { @@ -18723,7 +18723,7 @@ ${key.data.toString("base64")} }); }); - // development/test/cases/dom/theme.mjs + // test/cases/dom/theme.mjs init_util(); init_theme(); describe("Theme", function() { @@ -18767,7 +18767,7 @@ ${key.data.toString("base64")} }); }); - // application/source/dom/resourcemanager.mjs + // ../application/source/dom/resourcemanager.mjs init_extend(); init_global(); init_is(); @@ -18850,7 +18850,7 @@ ${key.data.toString("base64")} return self2; } - // development/test/cases/dom/resourcemanager.mjs + // test/cases/dom/resourcemanager.mjs describe("ResourceManager", function() { let fetchReference2, returnStatus; before(function(done) { @@ -18934,7 +18934,7 @@ ${key.data.toString("base64")} }); }); - // development/test/cases/dom/util.mjs + // test/cases/dom/util.mjs init_util(); describe("DOM", function() { before(async function() { @@ -18973,7 +18973,7 @@ ${key.data.toString("base64")} }); }); - // development/test/cases/dom/customelement.mjs + // test/cases/dom/customelement.mjs init_constants(); init_constants2(); init_util(); @@ -19303,7 +19303,7 @@ ${key.data.toString("base64")} }); }); - // development/test/cases/dom/attributes.mjs + // test/cases/dom/attributes.mjs init_attributes(); var html14; var html22; @@ -19489,7 +19489,7 @@ ${key.data.toString("base64")} }); }); - // development/test/cases/dom/events.mjs + // test/cases/dom/events.mjs init_events(); describe("Events", function() { before(async function() { @@ -19602,7 +19602,7 @@ ${key.data.toString("base64")} }); }); - // application/source/dom/worker/factory.mjs + // ../application/source/dom/worker/factory.mjs init_constants(); init_base(); init_global(); @@ -19653,7 +19653,7 @@ ${key.data.toString("base64")} } }; - // development/test/cases/dom/worker/factory.mjs + // test/cases/dom/worker/factory.mjs init_global(); var global2 = getGlobal(); describe("Worker", function() { @@ -19690,7 +19690,7 @@ ${key.data.toString("base64")} }); }); - // development/test/cases/dom/template.mjs + // test/cases/dom/template.mjs init_constants2(); init_template(); describe("Template", function() { @@ -19800,7 +19800,7 @@ ${key.data.toString("base64")} }); }); - // development/test/cases/dom/ready.mjs + // test/cases/dom/ready.mjs var windowReady2; var domReady2; describe("Ready", function() { @@ -19825,7 +19825,7 @@ ${key.data.toString("base64")} }); }); - // application/source/dom/focusmanager.mjs + // ../application/source/dom/focusmanager.mjs init_extend(); init_global(); init_is(); @@ -19935,7 +19935,7 @@ ${key.data.toString("base64")} } }; - // development/test/cases/dom/focusmanager.mjs + // test/cases/dom/focusmanager.mjs describe("FocusManager", function() { before(function(done) { initJSDOM().then(() => { @@ -20015,7 +20015,7 @@ ${key.data.toString("base64")} }); }); - // application/source/dom/assembler.mjs + // ../application/source/dom/assembler.mjs init_base(); init_global(); init_proxyobserver(); @@ -20046,7 +20046,7 @@ ${key.data.toString("base64")} } }; - // development/test/cases/dom/assembler.mjs + // test/cases/dom/assembler.mjs describe("Assembler", function() { before(async function() { initJSDOM(); @@ -20086,7 +20086,7 @@ ${key.data.toString("base64")} }); }); - // application/source/i18n/translations.mjs + // ../application/source/i18n/translations.mjs init_base(); init_is(); init_validate(); @@ -20153,7 +20153,7 @@ ${key.data.toString("base64")} } }; - // development/test/cases/i18n/translations.mjs + // test/cases/i18n/translations.mjs describe("Translations", function() { describe("Instance and Init", function() { let translation; @@ -20192,7 +20192,7 @@ ${key.data.toString("base64")} }); }); - // development/test/cases/i18n/locale.mjs + // test/cases/i18n/locale.mjs describe("Locale", function() { describe("new instance", function() { [ @@ -20246,7 +20246,7 @@ ${key.data.toString("base64")} }); }); - // application/source/i18n/formatter.mjs + // ../application/source/i18n/formatter.mjs init_constants(); init_extend(); init_validate(); @@ -20294,7 +20294,7 @@ ${key.data.toString("base64")} } }; - // development/test/cases/i18n/formatter.mjs + // test/cases/i18n/formatter.mjs describe("Formatter", function() { describe("example", function() { it("should run with marker", function() { @@ -20329,14 +20329,14 @@ ${key.data.toString("base64")} }); }); - // application/source/i18n/providers/fetch.mjs + // ../application/source/i18n/providers/fetch.mjs init_constants(); init_extend(); init_global(); init_is(); init_validate(); - // application/source/i18n/provider.mjs + // ../application/source/i18n/provider.mjs var Provider = class extends BaseWithOptions { getTranslations(locale) { return new Promise((resolve, reject) => { @@ -20349,7 +20349,7 @@ ${key.data.toString("base64")} } }; - // application/source/i18n/providers/fetch.mjs + // ../application/source/i18n/providers/fetch.mjs var Fetch = class extends Provider { constructor(url, options) { super(options); @@ -20386,7 +20386,7 @@ ${key.data.toString("base64")} } }; - // development/test/cases/i18n/providers/fetch.mjs + // test/cases/i18n/providers/fetch.mjs init_global(); var global3 = getGlobal(); var fetchReference; @@ -20426,7 +20426,7 @@ ${key.data.toString("base64")} }); }); - // development/test/cases/i18n/provider.mjs + // test/cases/i18n/provider.mjs describe("Provider", function() { describe("Instance and Init", function() { it("create instance", function() { @@ -20435,7 +20435,7 @@ ${key.data.toString("base64")} }); }); - // development/test/cases/types/mediatype.mjs + // test/cases/types/mediatype.mjs init_mediatype(); describe("Dataurl", function() { [ @@ -20478,7 +20478,7 @@ ${key.data.toString("base64")} }); }); - // development/test/cases/types/typeof.mjs + // test/cases/types/typeof.mjs init_stack(); init_typeof(); describe("typeOf", function() { @@ -20513,7 +20513,7 @@ ${key.data.toString("base64")} }); }); - // development/test/cases/types/observerlist.mjs + // test/cases/types/observerlist.mjs init_observerlist(); init_observer(); describe("ObserverList", function() { @@ -20588,7 +20588,7 @@ ${key.data.toString("base64")} }); }); - // development/test/cases/types/randomid.mjs + // test/cases/types/randomid.mjs describe("RandomID", function() { class RandomID2 { } @@ -20617,7 +20617,7 @@ ${key.data.toString("base64")} }); }); - // development/test/cases/types/uuid.mjs + // test/cases/types/uuid.mjs describe("UUID", function() { class UUID2 { } @@ -20661,7 +20661,7 @@ ${key.data.toString("base64")} }); }); - // development/test/cases/types/observer.mjs + // test/cases/types/observer.mjs init_observer(); describe("Observer", function() { let callback = function() { @@ -20759,7 +20759,7 @@ ${key.data.toString("base64")} }); }); - // development/test/cases/types/tokenlist.mjs + // test/cases/types/tokenlist.mjs init_tokenlist(); describe("TokenList", function() { describe(".toString()", function() { @@ -20952,7 +20952,7 @@ ${key.data.toString("base64")} }); }); - // development/test/cases/types/queue.mjs + // test/cases/types/queue.mjs init_queue(); describe("Queue", function() { let queue; @@ -20988,7 +20988,7 @@ ${key.data.toString("base64")} }); }); - // development/test/cases/types/stack.mjs + // test/cases/types/stack.mjs init_stack(); describe("Stack", function() { let stack; @@ -21024,7 +21024,7 @@ ${key.data.toString("base64")} }); }); - // application/source/types/binary.mjs + // ../application/source/types/binary.mjs init_validate(); function toBinary(string) { const codeUnits = new Uint16Array(validateString(string).length); @@ -21051,7 +21051,7 @@ ${key.data.toString("base64")} return result; } - // development/test/cases/types/binary.mjs + // test/cases/types/binary.mjs describe("Binary", function() { [ ["a", "a\0"], @@ -21068,7 +21068,7 @@ ${key.data.toString("base64")} }); }); - // development/test/cases/types/basewithoptions.mjs + // test/cases/types/basewithoptions.mjs describe("BaseWithOptions", function() { describe("new BaseWithOptions", function() { it("is instance of BaseWithOptions", function() { @@ -21083,13 +21083,13 @@ ${key.data.toString("base64")} }); }); - // application/source/types/regex.mjs + // ../application/source/types/regex.mjs init_validate(); function escapeString(value) { return validateString(value).replace(/[|\\{}()[\]^$+*?.]/g, "\\$&").replace(/-/g, "\\x2d"); } - // development/test/cases/types/regex.mjs + // test/cases/types/regex.mjs describe("escapeString", function() { before(function(done) { let promises = []; @@ -21119,7 +21119,7 @@ ${key.data.toString("base64")} }); }); - // development/test/cases/types/proxyobserver.mjs + // test/cases/types/proxyobserver.mjs init_extend(); init_is(); init_observer(); @@ -21286,11 +21286,11 @@ ${key.data.toString("base64")} }); }); - // application/source/types/node.mjs + // ../application/source/types/node.mjs init_base(); init_is(); - // application/source/types/nodelist.mjs + // ../application/source/types/nodelist.mjs init_is(); init_validate(); var NodeList2 = class extends Set { @@ -21343,7 +21343,7 @@ ${key.data.toString("base64")} } }; - // application/source/types/node.mjs + // ../application/source/types/node.mjs init_validate(); var internalValueSymbol = Symbol("internalData"); var treeStructureSymbol = Symbol("treeStructure"); @@ -21429,7 +21429,7 @@ ${key.data.toString("base64")} return this; } - // development/test/cases/types/nodelist.mjs + // test/cases/types/nodelist.mjs describe("NodeList", function() { describe("#constructor", function() { it("should create an empty NodeList", function() { @@ -21479,7 +21479,7 @@ ${key.data.toString("base64")} }); }); - // development/test/cases/types/version.mjs + // test/cases/types/version.mjs describe("Version", function() { describe(".compareTo()", function() { [ @@ -21512,7 +21512,7 @@ ${key.data.toString("base64")} }); }); - // application/source/types/noderecursiveiterator.mjs + // ../application/source/types/noderecursiveiterator.mjs init_constants(); init_base(); init_is(); @@ -21553,7 +21553,7 @@ ${key.data.toString("base64")} } }; - // development/test/cases/types/node.mjs + // test/cases/types/node.mjs describe("NodeList", function() { describe("#constructor", function() { it("should create an empty NodeList", function() { @@ -21741,7 +21741,7 @@ ${key.data.toString("base64")} }); }); - // development/test/cases/types/noderecursiveiterator.mjs + // test/cases/types/noderecursiveiterator.mjs describe("NodeRecursiveIterator", function() { it("should throw exeption", function() { expect(() => new NodeRecursiveIterator()).to.throw(Error); @@ -21790,7 +21790,7 @@ ${key.data.toString("base64")} }); }); - // development/test/cases/types/global.mjs + // test/cases/types/global.mjs init_global(); describe("Global", function() { describe("getGlobal", function() { @@ -21813,7 +21813,7 @@ ${key.data.toString("base64")} }); }); - // development/test/cases/types/dataurl.mjs + // test/cases/types/dataurl.mjs init_dataurl(); describe("Dataurl", function() { [ @@ -21852,7 +21852,7 @@ ${key.data.toString("base64")} }); }); - // development/test/cases/types/validate.mjs + // test/cases/types/validate.mjs init_validate(); init_base(); init_id(); @@ -22165,7 +22165,7 @@ ${key.data.toString("base64")} }); }); - // development/test/cases/types/uniquequeue.mjs + // test/cases/types/uniquequeue.mjs init_uniquequeue(); describe("UniqueQueue", function() { let queue; @@ -22227,7 +22227,7 @@ ${key.data.toString("base64")} }); }); - // development/test/cases/types/base.mjs + // test/cases/types/base.mjs init_base(); describe("Base", function() { describe("new Base", function() { @@ -22240,7 +22240,7 @@ ${key.data.toString("base64")} }); }); - // development/test/cases/types/is.mjs + // test/cases/types/is.mjs init_is(); init_id(); describe("Is", function() { @@ -22492,7 +22492,7 @@ ${key.data.toString("base64")} }); }); - // development/test/cases/types/id.mjs + // test/cases/types/id.mjs init_id(); describe("ID", function() { before(function(done) { @@ -22521,10 +22521,10 @@ ${key.data.toString("base64")} }); }); - // application/source/constraints/isobject.mjs + // ../application/source/constraints/isobject.mjs init_is(); - // application/source/constraints/abstract.mjs + // ../application/source/constraints/abstract.mjs init_base(); var AbstractConstraint = class extends Base { constructor() { @@ -22535,7 +22535,7 @@ ${key.data.toString("base64")} } }; - // application/source/constraints/isobject.mjs + // ../application/source/constraints/isobject.mjs var IsObject = class extends AbstractConstraint { isValid(value) { if (isObject(value)) { @@ -22545,7 +22545,7 @@ ${key.data.toString("base64")} } }; - // development/test/cases/constraint/isobject.mjs + // test/cases/constraint/isobject.mjs describe("IsObject", function() { describe(".isValid()", function() { let constraint = new IsObject(); @@ -22560,14 +22560,14 @@ ${key.data.toString("base64")} }); }); - // application/source/constraints/invalid.mjs + // ../application/source/constraints/invalid.mjs var Invalid = class extends AbstractConstraint { isValid(value) { return Promise.reject(value); } }; - // development/test/cases/constraint/invalid.mjs + // test/cases/constraint/invalid.mjs describe("Invalid", function() { describe(".isValid()", function() { let isvalid = new Invalid(); @@ -22580,14 +22580,14 @@ ${key.data.toString("base64")} }); }); - // application/source/constraints/valid.mjs + // ../application/source/constraints/valid.mjs var Valid = class extends AbstractConstraint { isValid(value) { return Promise.resolve(value); } }; - // application/source/constraints/abstractoperator.mjs + // ../application/source/constraints/abstractoperator.mjs var AbstractOperator = class extends AbstractConstraint { constructor(operantA, operantB) { super(); @@ -22599,14 +22599,14 @@ ${key.data.toString("base64")} } }; - // application/source/constraints/andoperator.mjs + // ../application/source/constraints/andoperator.mjs var AndOperator = class extends AbstractOperator { isValid(value) { return Promise.all([this.operantA.isValid(value), this.operantB.isValid(value)]); } }; - // development/test/cases/constraint/andoperator.mjs + // test/cases/constraint/andoperator.mjs describe("AndOperator", function() { describe(".isValid()", function() { [ @@ -22630,7 +22630,7 @@ ${key.data.toString("base64")} }); }); - // application/source/constraints/oroperator.mjs + // ../application/source/constraints/oroperator.mjs var OrOperator = class extends AbstractOperator { isValid(value) { var self2 = this; @@ -22656,7 +22656,7 @@ ${key.data.toString("base64")} } }; - // development/test/cases/constraint/oroperator.mjs + // test/cases/constraint/oroperator.mjs describe("OrOperator", function() { describe(".isValid()", function() { [ @@ -22680,7 +22680,7 @@ ${key.data.toString("base64")} }); }); - // application/source/constraints/isarray.mjs + // ../application/source/constraints/isarray.mjs init_is(); var IsArray = class extends AbstractConstraint { isValid(value) { @@ -22691,7 +22691,7 @@ ${key.data.toString("base64")} } }; - // development/test/cases/constraint/isarray.mjs + // test/cases/constraint/isarray.mjs describe("IsArray", function() { describe(".isValid()", function() { let constraint = new IsArray(); @@ -22706,7 +22706,7 @@ ${key.data.toString("base64")} }); }); - // development/test/cases/constraint/valid.mjs + // test/cases/constraint/valid.mjs describe("Valid", function() { describe(".isValid()", function() { let constraint = new Valid(); @@ -22720,7 +22720,7 @@ ${key.data.toString("base64")} }); }); - // development/test/cases/util/trimspaces.mjs + // test/cases/util/trimspaces.mjs init_trimspaces(); describe("trimSpaces", function() { [ @@ -22738,7 +22738,7 @@ ${key.data.toString("base64")} }); }); - // application/source/util/deadmansswitch.mjs + // ../application/source/util/deadmansswitch.mjs init_constants(); init_base(); init_is(); @@ -22780,7 +22780,7 @@ ${key.data.toString("base64")} initCallback.call(self2); } - // development/test/cases/util/deadmansswitch.mjs + // test/cases/util/deadmansswitch.mjs describe("DeadMansSwitch", function() { describe("run instance", function() { it("should run", function(done) { @@ -22822,7 +22822,7 @@ ${key.data.toString("base64")} }); }); - // application/source/util/freeze.mjs + // ../application/source/util/freeze.mjs init_validate(); function deepFreeze(object) { validateObject(object); @@ -22834,7 +22834,7 @@ ${key.data.toString("base64")} return Object.freeze(object); } - // development/test/cases/util/freeze.mjs + // test/cases/util/freeze.mjs describe("deepFreeze", function() { describe("deepFreeze an object", function() { it("should freeze object", function() { @@ -22866,7 +22866,7 @@ ${key.data.toString("base64")} }); }); - // application/source/util/comparator.mjs + // ../application/source/util/comparator.mjs init_base(); init_is(); var Comparator = class extends Base { @@ -22910,7 +22910,7 @@ ${key.data.toString("base64")} } }; - // development/test/cases/util/comparator.mjs + // test/cases/util/comparator.mjs describe("Comparator", function() { describe("create new instance", function() { it("should return a comparator object", function() { @@ -23073,7 +23073,7 @@ ${key.data.toString("base64")} }); }); - // development/test/cases/util/clone.mjs + // test/cases/util/clone.mjs init_clone(); describe("Clone", function() { class A { @@ -23199,7 +23199,7 @@ ${key.data.toString("base64")} }); }); - // application/source/util/processing.mjs + // ../application/source/util/processing.mjs init_constants(); init_base(); init_global(); @@ -23261,7 +23261,7 @@ ${key.data.toString("base64")} } }; - // development/test/cases/util/processing.mjs + // test/cases/util/processing.mjs describe("Processing", function() { describe("run instance", function() { it("should run many function", function(done) { @@ -23335,7 +23335,7 @@ ${key.data.toString("base64")} }); }); - // development/test/cases/data/extend.mjs + // test/cases/data/extend.mjs init_extend(); var MockForExtends = class { constructor() { @@ -23438,7 +23438,7 @@ ${key.data.toString("base64")} }); }); - // development/test/cases/data/pipe.mjs + // test/cases/data/pipe.mjs init_pipe(); describe("Pipe", function() { before(function(done) { @@ -23500,7 +23500,7 @@ ${key.data.toString("base64")} }); }); - // application/source/data/buildmap.mjs + // ../application/source/data/buildmap.mjs init_is(); init_validate(); init_clone(); @@ -23604,7 +23604,7 @@ ${key.data.toString("base64")} return definition; } - // development/test/cases/data/buildmap.mjs + // test/cases/data/buildmap.mjs describe("buildMap", function() { let convertMapResult = function(r) { if (r instanceof Map) { @@ -23918,7 +23918,7 @@ ${key.data.toString("base64")} }); }); - // application/source/data/datasource.mjs + // ../application/source/data/datasource.mjs init_constants(); init_base(); init_dataurl(); @@ -24006,7 +24006,7 @@ ${key.data.toString("base64")} return {}; } - // development/test/cases/data/datasource.mjs + // test/cases/data/datasource.mjs describe("Datasource", function() { it("should instance of Datasource ", function() { expect(new Datasource()).to.be.instanceof(Datasource); @@ -24047,7 +24047,7 @@ ${key.data.toString("base64")} }); }); - // application/source/data/buildtree.mjs + // ../application/source/data/buildtree.mjs init_is(); init_extend(); var parentSymbol = Symbol("parent"); @@ -24097,7 +24097,7 @@ ${key.data.toString("base64")} return list; } - // development/test/cases/data/buildtree.mjs + // test/cases/data/buildtree.mjs describe("buildTree", function() { describe("example", function() { it("should run example", function() { @@ -24266,7 +24266,7 @@ ${key.data.toString("base64")} }); }); - // development/test/cases/data/transformer.mjs + // test/cases/data/transformer.mjs init_transformer(); describe("Transformer", function() { before(function(done) { @@ -24436,7 +24436,7 @@ ${key.data.toString("base64")} }); }); - // development/test/cases/data/pathfinder.mjs + // test/cases/data/pathfinder.mjs init_pathfinder(); describe("Pathfinder", function() { let convertMapResult = function(r) { @@ -24756,7 +24756,7 @@ ${key.data.toString("base64")} }); }); - // development/test/cases/data/diff.mjs + // test/cases/data/diff.mjs init_diff(); init_queue(); describe("Diff", function() { @@ -24930,13 +24930,13 @@ ${key.data.toString("base64")} }); }); - // application/source/data/datasource/restapi.mjs + // ../application/source/data/datasource/restapi.mjs init_constants(); init_is(); init_pathfinder(); init_pipe(); - // application/source/data/datasource/restapi/writeerror.mjs + // ../application/source/data/datasource/restapi/writeerror.mjs init_constants(); var WriteError = class extends Error { constructor(message, response, validation) { @@ -24954,7 +24954,7 @@ ${key.data.toString("base64")} } }; - // application/source/data/datasource/restapi.mjs + // ../application/source/data/datasource/restapi.mjs var RestAPI = class extends Datasource { constructor(readDefinition, writeDefinition) { super(); @@ -25077,7 +25077,7 @@ ${key.data.toString("base64")} } }; - // development/test/cases/data/datasource/restapi.mjs + // test/cases/data/datasource/restapi.mjs init_validate(); describe("RestAPI", function() { let fetchReference2; @@ -25141,11 +25141,11 @@ ${key.data.toString("base64")} }); }); - // application/source/data/datasource/storage/sessionstorage.mjs + // ../application/source/data/datasource/storage/sessionstorage.mjs init_constants(); init_global(); - // application/source/data/datasource/storage.mjs + // ../application/source/data/datasource/storage.mjs init_constants(); init_validate(); var storageObjectSymbol = Symbol("storageObject"); @@ -25190,7 +25190,7 @@ ${key.data.toString("base64")} } }; - // application/source/data/datasource/storage/sessionstorage.mjs + // ../application/source/data/datasource/storage/sessionstorage.mjs var SessionStorage = class extends Storage { [storageObjectSymbol]() { return getGlobalObject("sessionStorage"); @@ -25201,7 +25201,7 @@ ${key.data.toString("base64")} } }; - // development/test/util/localstorage.mjs + // test/util/localstorage.mjs function createStorage() { let UNSET = Symbol(); let s = {}, noopCallback = () => { @@ -25278,7 +25278,7 @@ ${key.data.toString("base64")} return s; } - // development/test/cases/data/datasource/storage/sessionstorage.mjs + // test/cases/data/datasource/storage/sessionstorage.mjs var storageReference; describe("SessionStorage", function() { afterEach(() => { @@ -25307,7 +25307,7 @@ ${key.data.toString("base64")} }); }); - // application/source/data/datasource/storage/localstorage.mjs + // ../application/source/data/datasource/storage/localstorage.mjs init_constants(); init_global(); var LocalStorage = class extends Storage { @@ -25320,7 +25320,7 @@ ${key.data.toString("base64")} } }; - // development/test/cases/data/datasource/storage/localstorage.mjs + // test/cases/data/datasource/storage/localstorage.mjs var localStorageReference; describe("LocalStorage", function() { afterEach(() => { @@ -25349,7 +25349,7 @@ ${key.data.toString("base64")} }); }); - // development/test/cases/math/random.mjs + // test/cases/math/random.mjs describe("Math", function() { let random2 = () => { }; diff --git a/deployment/jsdoc.json b/documentation/config/jsdoc.json similarity index 97% rename from deployment/jsdoc.json rename to documentation/config/jsdoc.json index 718fed03e28df9d592dd88f773b7eb7f0d0a253b..b13767163cdf10e2bc9914c1fb94107520f22197 100644 --- a/deployment/jsdoc.json +++ b/documentation/config/jsdoc.json @@ -3,7 +3,7 @@ "allowUnknownTags": true }, "source": { - "include": "../application/source/", + "include": "../../application/source/", "includePattern": "\\.js$", "excludePattern": "" }, diff --git a/project.mk b/project.mk index 84dd5f5fa4e49becbd550fead3160939bcb55b24..37a79dfe9fdb0add93b768dab04f0c6a2975d8ea 100644 --- a/project.mk +++ b/project.mk @@ -1,9 +1,7 @@ -## Node Paths -NODE_ROOT_DIR=$(DEPLOYMENT_PATH) ## Project directory in which the Makefiles should be located -MAKEFILE_IMPORT_PATH=$(PROJECT_ROOT)deployment/makefiles/ +MAKEFILE_IMPORT_PATH=$(PROJECT_ROOT)development/makefiles/ diff --git a/release.json b/release.json index 412955b83feaa4ee202f3f89e06c61ea4bda34a1..094b803807956e91755b6f4f7ff45d3396fcdc24 100644 --- a/release.json +++ b/release.json @@ -1 +1 @@ -{"version":"0.1.22"} +{"version":"0.1.8"}