Compare commits
	
		
			138 Commits 
		
	
	
	| Author | SHA1 | Date | 
|---|---|---|
|  | 8708edd8bb | 2 months ago | 
|  | f831b195bd | 2 months ago | 
|  | 29f4de58de | 2 months ago | 
|  | 3e04d6c6c2 | 3 months ago | 
|  | 386ed6b8ab | 3 months ago | 
|  | 1b464b9ad7 | 5 months ago | 
|  | c3b90e60bf | 5 months ago | 
|  | c0171aa8d8 | 6 months ago | 
|  | 6933a89111 | 6 months ago | 
|  | 7a40c27712 | 6 months ago | 
|  | 6de0aca195 | 6 months ago | 
|  | 02203a5dd0 | 6 months ago | 
|  | a36190bde9 | 6 months ago | 
|  | 41d71f1523 | 6 months ago | 
|  | 1a51451e82 | 6 months ago | 
|  | 9fb347421d | 6 months ago | 
|  | 362d9f870c | 6 months ago | 
|  | cf6438d623 | 6 months ago | 
|  | c0b43e38d1 | 6 months ago | 
|  | d361a1bbee | 6 months ago | 
|  | c25f9bb93e | 6 months ago | 
|  | aa65dc1e54 | 6 months ago | 
|  | 5dab23dc14 | 6 months ago | 
|  | 46f12f517d | 6 months ago | 
|  | ab4d39d55d | 6 months ago | 
|  | 0496bb0295 | 6 months ago | 
|  | d578a56269 | 6 months ago | 
|  | 61a32a7bfe | 6 months ago | 
|  | 27cc2d930d | 6 months ago | 
|  | 73c85f4d2e | 6 months ago | 
|  | 025573a16c | 6 months ago | 
|  | 467fca6adf | 6 months ago | 
|  | 819ad6bee0 | 6 months ago | 
|  | e65c2f96cd | 6 months ago | 
|  | 3608ae5cf0 | 6 months ago | 
|  | 2b50ed88d1 | 6 months ago | 
|  | 60776d6fc3 | 6 months ago | 
|  | b9a92a4df2 | 6 months ago | 
|  | 74974f6c80 | 6 months ago | 
|  | 92a86aa880 | 6 months ago | 
|  | fc1dfd7e21 | 6 months ago | 
|  | 9f374eeaa4 | 6 months ago | 
|  | 20710fc461 | 6 months ago | 
|  | db5750ca16 | 6 months ago | 
|  | 0110253876 | 6 months ago | 
|  | e495aa72ad | 6 months ago | 
|  | 439a98eb33 | 6 months ago | 
|  | 70e5d0d66f | 6 months ago | 
|  | 60faa1a062 | 6 months ago | 
|  | e539ace5a0 | 6 months ago | 
|  | 23e30b8ffb | 7 months ago | 
|  | 8d6eb00ae4 | 6 months ago | 
|  | c2a34dd540 | 6 months ago | 
|  | 12f52feca8 | 6 months ago | 
|  | 89bc8106c9 | 6 months ago | 
|  | c34114c176 | 6 months ago | 
|  | 6ff45892cb | 6 months ago | 
|  | 6f186b64d8 | 6 months ago | 
|  | dd3d986485 | 6 months ago | 
|  | d9cef5210d | 6 months ago | 
|  | 26becc9431 | 6 months ago | 
|  | 3afeb8a5e6 | 6 months ago | 
|  | 0209c1012f | 6 months ago | 
|  | 1b81c4df0e | 6 months ago | 
|  | e4e4c60f27 | 6 months ago | 
|  | b53ffc3571 | 6 months ago | 
|  | eca487c2fe | 6 months ago | 
|  | e0fbe8bd96 | 6 months ago | 
|  | c228b251b2 | 6 months ago | 
|  | 208481a867 | 6 months ago | 
|  | aada47b8e0 | 6 months ago | 
|  | 512551c4f4 | 6 months ago | 
|  | 578d26c2e5 | 6 months ago | 
|  | 9f46eaba1a | 6 months ago | 
|  | 40d2546b36 | 6 months ago | 
|  | 6ccee1914c | 7 months ago | 
|  | 8b81393131 | 7 months ago | 
|  | e88623a3f5 | 7 months ago | 
|  | 614ce95a2b | 7 months ago | 
|  | 65b3debb9e | 7 months ago | 
|  | 653e82e6b6 | 7 months ago | 
|  | 1dd90bd242 | 7 months ago | 
|  | a30941888f | 7 months ago | 
|  | ac7bb1e1ff | 7 months ago | 
|  | 752ca8435b | 7 months ago | 
|  | df8ceb0bd8 | 7 months ago | 
|  | 90d446adb2 | 7 months ago | 
|  | 767893d9f9 | 7 months ago | 
|  | 9557335694 | 7 months ago | 
|  | 430411ff7f | 7 months ago | 
|  | 42ad432bdb | 7 months ago | 
|  | d8170fad15 | 7 months ago | 
|  | d5c4865ecf | 7 months ago | 
|  | 626f7ac1ac | 7 months ago | 
|  | 51a4da6638 | 7 months ago | 
|  | 7ff88c0e66 | 8 months ago | 
|  | 76fac29dd6 | 8 months ago | 
|  | 646afba4d3 | 8 months ago | 
|  | ea9261446d | 8 months ago | 
|  | e0a60593d2 | 8 months ago | 
|  | 882303a5fa | 8 months ago | 
|  | eae24e4bd5 | 8 months ago | 
|  | a0c6f68e23 | 8 months ago | 
|  | 27d0635119 | 8 months ago | 
|  | 0ba13cca94 | 8 months ago | 
|  | e0046c6aa8 | 8 months ago | 
|  | 12bf468e22 | 8 months ago | 
|  | 89207c163f | 8 months ago | 
|  | 170d00d852 | 8 months ago | 
|  | 4479c92bb4 | 8 months ago | 
|  | b7fff8604e | 8 months ago | 
|  | a7fcb5a3ca | 8 months ago | 
|  | 4b6f4006d9 | 8 months ago | 
|  | 9447216e53 | 8 months ago | 
|  | f41e7f1384 | 8 months ago | 
|  | c4b0fc9162 | 8 months ago | 
|  | 5e782912f7 | 8 months ago | 
|  | 1632528322 | 8 months ago | 
|  | b60b89cbea | 8 months ago | 
|  | 0cc60ad07c | 8 months ago | 
|  | 771a05dba8 | 8 months ago | 
|  | f9e7e188cb | 8 months ago | 
|  | 02f72d4648 | 8 months ago | 
|  | bb0ccb8904 | 8 months ago | 
|  | 6f78fa1cfb | 8 months ago | 
|  | 3782344e1f | 8 months ago | 
|  | 5b74731cf1 | 8 months ago | 
|  | 94ef6186cf | 8 months ago | 
|  | 3b8efc9a6d | 8 months ago | 
|  | 0c51679e04 | 8 months ago | 
|  | ec298757c2 | 8 months ago | 
|  | 8121c9ec6a | 10 months ago | 
|  | 9d073a8367 | 10 months ago | 
|  | cdc1188061 | 10 months ago | 
|  | ea1786f379 | 10 months ago | 
|  | 4a0bd90799 | 10 months ago | 
|  | 95d0649169 | 11 months ago | 
|  | fc2c6b15c0 | 11 months ago | 
				 53 changed files with 8098 additions and 1233 deletions
			
			
		| @ -1,3 +1,5 @@ | |||
| node_modules/ | |||
| samples/*/ | |||
| jpc-like-websites.js | |||
| jpc-like-websites.mjs | |||
| extensions/ | |||
|  | |||
| @ -1,5 +1,21 @@ | |||
| Copyright (C) Christian Martin - All Rights Reserved | |||
| Unauthorized copying of this file,  | |||
| via any medium is strictly prohibited | |||
| Proprietary and confidential | |||
| Written by Christian Martin, September 2024 | |||
| Copyright (c) 2024-2025 Christian Martin | |||
| 
 | |||
| All rights reserved. | |||
| 
 | |||
| This software  | |||
| and associated documentation files (the "Software")  | |||
| are the exclusive property of Christian Martin. | |||
| You may not use, copy, modify, merge, publish,  | |||
| distribute, sublicense, or sell copies of the Software,  | |||
| in whole or in part,  | |||
| without the express prior written permission of the copyright holder. | |||
| 
 | |||
| For inquiries regarding licensing, please contact:  | |||
| christian.martin3(at)gmx.net. | |||
| 
 | |||
| THE SOFTWARE IS PROVIDED "AS IS",  | |||
| WITHOUT WARRANTY OF ANY KIND,  | |||
| EXPRESS OR IMPLIED,  | |||
| INCLUDING BUT NOT LIMITED TO  | |||
| THE WARRANTIES OF MERCHANTABILITY,  | |||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |||
|  | |||
| @ -0,0 +1,193 @@ | |||
| const fs = require('fs/promises'); | |||
| const path = require('path'); | |||
| 
 | |||
| const copyright_disclaimer = ` | |||
| /** | |||
|  * This file is part of the jps-like-websites lib | |||
|  * URL: https://git.labos.goip.de/chris/jpc-like-websites
 | |||
|  * @copyright by its creator  | |||
|  */  | |||
| 
 | |||
| `;
 | |||
| 
 | |||
| /** | |||
|  * Kept for future reference, might be that we will return to the dependency resolvement approach. | |||
|  * @param {OAO} oao  | |||
|  * @returns {OAO} | |||
|  */ | |||
| function resolveDependencyOrder(oao) { | |||
|     let roots = oao.keys.filter(k => oao.objects[k].dependencies.length === 0); | |||
| 
 | |||
|     let folderList = [...new Set(oao.keys.map(k => oao.objects[k].folder))]; | |||
| 
 | |||
|     console.log("Folders", folderList); | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {OAO} oao  | |||
|      * @param {Array<Array<string>>} dealt  | |||
|      */ | |||
|     function recursiveResolvement(oao, dealtLvls, dealt) { | |||
|         const arrsEqualSize = (arr1, arr2) => arr1.length === arr2.length; | |||
|         const arrHasAll = (arr1, arr2) => arr2.every(k => arr1.includes(k)) | |||
| 
 | |||
|         if (oao.keys.length === dealt.length) { | |||
|             return [...dealt, ...current]; | |||
|         } else { | |||
|             console.log(`Received ${dealt.length} dealt of ${oao.keys.length}`); | |||
|         } | |||
| 
 | |||
|         let remaining = oao.keys | |||
|             .filter(k => !dealt.includes(k)); | |||
| 
 | |||
|         if (remaining.length < 2) { | |||
|             return [...dealt, ...remaining]; | |||
|         } | |||
| 
 | |||
|         let current = remaining | |||
|             .filter(k => oao.objects[k].dependencies.every(sk => dealt.includes(sk))); | |||
| 
 | |||
|         if (current.length === 0) { | |||
|             console.log("Couldn't resolve", remaining); | |||
|             return remaining; | |||
|         } | |||
| 
 | |||
|         dealtLvls.push(current); | |||
| 
 | |||
|         if (arrsEqualSize(remaining, current)) { | |||
|             return [...dealt, ...current]; | |||
|         } | |||
| 
 | |||
|         return recursiveResolvement(oao, dealtLvls, [...dealt, ...current]); | |||
|     } | |||
| 
 | |||
|     let recursiveResolved = recursiveResolvement(oao, [roots], roots); | |||
| 
 | |||
|     recursiveResolved | |||
|         .forEach((k, i) => { | |||
|             oao.objects[k].orderNr = i; | |||
|         }); | |||
| 
 | |||
|     oao.isOrdered = recursiveResolved.slice(-1).length <= 2; | |||
|     if (oao.isOrdered) { | |||
|         oao.keys = recursiveResolved; | |||
|     } | |||
|     return oao; | |||
| } | |||
| 
 | |||
| 
 | |||
| /** | |||
|  *  | |||
|  * @param {string} srcFile path to src file | |||
|  * @param {string} targetFile path of target file that will contain all scripts | |||
|  */ | |||
| async function appendContent(srcFile, targetFile, contentModifications = [], lineModifications = []) { | |||
|     try { | |||
|         let content = await fs.readFile(srcFile, 'utf8'); | |||
|         console.log(`Processing '${srcFile}'`); | |||
|         console.log(`           READ: successfully!`, `Attepting to append file`); | |||
| 
 | |||
|         /* Apply modifications */ | |||
|         content = contentModifications | |||
|             .reduce((a, c) => c(a), content.split('\r\n')) | |||
|             .map(line => lineModifications.reduce((a, c) => c(a), line)) | |||
|             .join('\r\n'); | |||
| 
 | |||
|         await fs.appendFile(targetFile, content); | |||
| 
 | |||
|         console.log(`           Append/Write: successfully!`); | |||
|     } catch (error) { | |||
|         console.error(`Error reading/writing files: ${error.message}`); | |||
|         console.error(srcFile); | |||
|     } | |||
| } | |||
| 
 | |||
| 
 | |||
| 
 | |||
| /** | |||
|  *  | |||
|  * @param {string} srcFile path to src file | |||
|  * @param {string} targetFile path of target file that will contain all scripts | |||
|  */ | |||
| async function bundleWithSpecifiedMode(targetFile, fileList, modulize = true) { | |||
|     let contentModifications = [ | |||
|         function (content) { | |||
|             return [ | |||
|                 ...copyright_disclaimer.split('\r\n'), | |||
|                 ...content | |||
|             ]; | |||
|         } | |||
|     ]; | |||
| 
 | |||
|     let lineModifications = [ | |||
|         function (line) { | |||
| 
 | |||
|             let starters = ["function", "const", "let", "class"]; | |||
|             if (starters.some(s => line.startsWith(s))) { | |||
|                 return "export " + line; | |||
|             } | |||
|             return line; | |||
|         } | |||
|     ]; | |||
| 
 | |||
| 
 | |||
|     fileList | |||
|         .reduce((prevPromise, filePath) => prevPromise | |||
|             .then( | |||
|                 () => appendContent(filePath, targetFile, contentModifications) | |||
|             ), Promise.resolve() | |||
|         ) | |||
| 
 | |||
|     if (modulize) { | |||
|         let targetModule = path.basename(targetFile).split('.', 1)[0] + '.mjs'; | |||
|         fileList | |||
|             .reduce((prevPromise, filePath) => prevPromise | |||
|                 .then( | |||
|                     () => appendContent(filePath, targetModule, contentModifications, lineModifications) | |||
|                 ), Promise.resolve() | |||
|             ) | |||
|     } | |||
| } | |||
| 
 | |||
| 
 | |||
| 
 | |||
| /** | |||
|  * @deprecated Currently this function takles the task of successfully bundling the jpc-framework. | |||
|  *              A custom bundler will replace this function/file. | |||
|  *              Some logic might be copied/used from here. | |||
|  *  | |||
|  * Takes the given order from the jpclw-bundle.json and bundles the scripts accordingly.  | |||
|  *  | |||
|  * @param {string|path} targetFile path to the targetfile (without file extension) | |||
|  * @param {boolean} modulize flag to decide to generate a mjs module as well  | |||
|  * @param {string|path} bundleFile path to the bundleFile if there is some  | |||
|  *                      - preparation for jpclw-bundler | |||
|  */ | |||
| function bundle(modulize = false, targetFile = "./jpc-like-websites.js", bundleFile = './jpclw-bundle.json') { | |||
|     let fileOrder = require(bundleFile); | |||
| 
 | |||
|     console.log("(Re-) Creating target file: '" + targetFile + "'"); | |||
|     /* EMPTY (create?) TARGET FILE */ | |||
|     fs.writeFile(targetFile, "", err => { }); | |||
| 
 | |||
|     if (modulize) { | |||
|         let targetModule = path.basename(targetFile).split('.', 1)[0] + '.mjs'; | |||
|         fs.writeFile(targetModule, "", err => { }); | |||
|     } | |||
| 
 | |||
|     let fileList = Object.keys(fileOrder.orderedGroups) | |||
|         .flatMap(groupName => fileOrder.orderedGroups[groupName]) | |||
|         .map(fileName => { | |||
|             let f = fileOrder.objects[fileName]; | |||
|             /* The Path build by name and folder */ | |||
|             return `${f.folder}/${f.name}`; | |||
|         }); | |||
| 
 | |||
| 
 | |||
|     bundleWithSpecifiedMode(targetFile, fileList, modulize); | |||
| 
 | |||
| } | |||
| 
 | |||
| let args = process.argv.slice(2); | |||
| bundle(Boolean(args[0]), args[1], args[2]); | |||
| 
 | |||
| @ -0,0 +1,15 @@ | |||
| import js from "@eslint/js"; | |||
| import globals from "globals"; | |||
| import json from "@eslint/json"; | |||
| import css from "@eslint/css"; | |||
| import { defineConfig } from "eslint/config"; | |||
| 
 | |||
| 
 | |||
| export default defineConfig([ | |||
|   { files: ["**/*.{js,mjs,cjs}"], plugins: { js }, extends: ["js/recommended"] }, | |||
|   { files: ["**/*.js"], languageOptions: { sourceType: "script" } }, | |||
|   { files: ["**/*.{js,mjs,cjs}"], languageOptions: { globals: globals.browser } }, | |||
|   { files: ["**/*.json"], plugins: { json }, language: "json/json", extends: ["json/recommended"] }, | |||
|   { files: ["**/*.jsonc"], plugins: { json }, language: "json/jsonc", extends: ["json/recommended"] }, | |||
|   { files: ["**/*.css"], plugins: { css }, language: "css/css", extends: ["css/recommended"] }, | |||
| ]); | |||
| @ -0,0 +1,683 @@ | |||
| { | |||
|     "orderedGroups": { | |||
|         "base": [ | |||
|             "webTrinity.js", | |||
|             "extStore.js" | |||
|         ], | |||
|         "helper": [ | |||
|             "ObjectAccessObject.js", | |||
|             "TwoDimPoint.js", | |||
|             "general.js", | |||
|             "indices.js" | |||
|         ], | |||
|         "chain_mixins": [ | |||
|             "mixinModSubChain.js", | |||
|             "ModificationSubChain.js" | |||
|         ], | |||
|         "decorators": [ | |||
|             "color.js", | |||
|             "alignment.js", | |||
|             "arrangement.js", | |||
|             "siding.js", | |||
|             "padding.js", | |||
|             "margin.js", | |||
|             "shapes.js", | |||
|             "border.js", | |||
|             "dimensions.js" | |||
|         ], | |||
|         "handlers": [ | |||
|             "commonEvents.js", | |||
|             "contextMenu.js", | |||
|             "dragAndDrop.js" | |||
|         ], | |||
|         "modifier": [ | |||
|             "Modifier.js", | |||
|             "ChainableModifier.js" | |||
|         ], | |||
|         "generators":[ | |||
|             "generator.js", | |||
|             "defaultGenerators.js" | |||
|         ], | |||
|         "component": [ | |||
|             "ChildbearerComponent.js", | |||
|             "ModifiableComponent.js", | |||
|             "StyleAndScriptStoringComponent.js", | |||
|             "Component.js", | |||
|             "FlexContainerComponent.js", | |||
|             "Column.js", | |||
|             "Row.js", | |||
|             "InputComponent.js" | |||
|         ], | |||
|         "context": [ | |||
|             "scriptAndStyleContext.js", | |||
|             "framework-controls.js", | |||
|             "context.js" | |||
|         ], | |||
|         "builder": [ | |||
|             "builder.js" | |||
|         ] | |||
|     }, | |||
|     "keys": [ | |||
|         "extStore.js", | |||
|         "webTrinity.js", | |||
|         "builder.js", | |||
|         "ChildbearerComponent.js", | |||
|         "Column.js", | |||
|         "Component.js", | |||
|         "FlexContainerComponent.js", | |||
|         "InputComponent.js", | |||
|         "ModifiableComponent.js", | |||
|         "Row.js", | |||
|         "StyleAndScriptStoringComponent.js", | |||
|         "context.js", | |||
|         "framework-controls.js", | |||
|         "scriptAndStyleContext.js", | |||
|         "alignment.js", | |||
|         "arrangement.js", | |||
|         "border.js", | |||
|         "color.js", | |||
|         "dimensions.js", | |||
|         "margin.js", | |||
|         "padding.js", | |||
|         "shapes.js", | |||
|         "siding.js", | |||
|         "simplePagingAndNavigation.js", | |||
|         "defaultGenerators.js", | |||
|         "generator.js", | |||
|         "commonEvents.js", | |||
|         "contextMenu.js", | |||
|         "dragAndDrop.js", | |||
|         "general.js", | |||
|         "indices.js", | |||
|         "ObjectAccessObject.js", | |||
|         "TwoDimPoint.js", | |||
|         "ChainableModifier.js", | |||
|         "mixinModSubChain.js", | |||
|         "ModificationSubChain.js", | |||
|         "Modifier.js" | |||
|     ], | |||
|     "objects": { | |||
|         "extStore.js": { | |||
|             "folder": "src/base", | |||
|             "name": "extStore.js", | |||
|             "exports": [ | |||
|                 "ESAggregation", | |||
|                 "ExtStorePosition", | |||
|                 "OverwriteBehaviour", | |||
|                 "clearFunctionDeclarationText", | |||
|                 "getScriptTagInjectionText", | |||
|                 "FunctionStoreBuffer", | |||
|                 "ExtStorage", | |||
|                 "ExtStoreType", | |||
|                 "SStoreDefinition", | |||
|                 "resolveOverwrite", | |||
|                 "identifyAndResolveOverwrite", | |||
|                 "generateAndFillScriptTag", | |||
|                 "getStylingInjectionText", | |||
|                 "generateAndFillStyleTag", | |||
|                 "executeOnExtStoreTypeCollectedTriple" | |||
|             ], | |||
|             "imports": { | |||
|                 "../Component": [ | |||
|                     "Component" | |||
|                 ], | |||
|                 "../context": [ | |||
|                     "Page" | |||
|                 ] | |||
|             } | |||
|         }, | |||
|         "webTrinity.js": { | |||
|             "folder": "src/base", | |||
|             "name": "webTrinity.js", | |||
|             "exports": [ | |||
|                 "WebTrinity" | |||
|             ] | |||
|         }, | |||
|         "builder.js": { | |||
|             "folder": "src", | |||
|             "name": "builder.js", | |||
|             "exports": [ | |||
|                 "builder" | |||
|             ], | |||
|             "imports": { | |||
|                 "./Component": [ | |||
|                     "Component", | |||
|                     "FlexContainerComponent", | |||
|                     "Row", | |||
|                     "Column", | |||
|                     "InputComponent" | |||
|                 ], | |||
|                 "./modifier": [ | |||
|                     "Modifier" | |||
|                 ], | |||
|                 "./context": [ | |||
|                     "Page" | |||
|                 ] | |||
|             } | |||
|         }, | |||
|         "ChildbearerComponent.js": { | |||
|             "folder": "src/component", | |||
|             "name": "ChildbearerComponent.js", | |||
|             "exports": [ | |||
|                 "ChildbearerComponent" | |||
|             ], | |||
|             "imports": { | |||
|                 "../decorators": [ | |||
|                     "Alignment", | |||
|                     "Arrangement" | |||
|                 ], | |||
|                 "../helper": [ | |||
|                     "helperFun" | |||
|                 ], | |||
|                 "./Component": [ | |||
|                     "Component" | |||
|                 ], | |||
|                 "../builder": [ | |||
|                     "builder" | |||
|                 ] | |||
|             } | |||
|         }, | |||
|         "Column.js": { | |||
|             "folder": "src/component", | |||
|             "name": "Column.js", | |||
|             "exports": [ | |||
|                 "Column" | |||
|             ], | |||
|             "imports": { | |||
|                 "./FlexContainerComponent": [ | |||
|                     "FlexContainerComponent" | |||
|                 ] | |||
|             } | |||
|         }, | |||
|         "Component.js": { | |||
|             "folder": "src/component", | |||
|             "name": "Component.js", | |||
|             "exports": [ | |||
|                 "Component" | |||
|             ], | |||
|             "imports": { | |||
|                 "../base": [ | |||
|                     "ExtStorage" | |||
|                 ], | |||
|                 "../modifier": [ | |||
|                     "Modifier" | |||
|                 ], | |||
|                 "../context": [ | |||
|                     "Page", | |||
|                     "CommonCompelGroups" | |||
|                 ], | |||
|                 "../handlers": [ | |||
|                     "DefaultContextMenu", | |||
|                     "DragAndDropImplementation" | |||
|                 ], | |||
|                 "../generators": [ | |||
|                     "CompelGenerator" | |||
|                 ] | |||
|             } | |||
|         }, | |||
|         "FlexContainerComponent.js": { | |||
|             "folder": "src/component", | |||
|             "name": "FlexContainerComponent.js", | |||
|             "exports": [ | |||
|                 "FlexContainerComponent" | |||
|             ], | |||
|             "imports": { | |||
|                 "./Component": [ | |||
|                     "Component" | |||
|                 ], | |||
|                 "../modifier": [ | |||
|                     "Modifier" | |||
|                 ] | |||
|             } | |||
|         }, | |||
|         "InputComponent.js": { | |||
|             "folder": "src/component", | |||
|             "name": "InputComponent.js", | |||
|             "exports": [ | |||
|                 "InputComponent" | |||
|             ], | |||
|             "imports": { | |||
|                 "./Component": [ | |||
|                     "Component" | |||
|                 ] | |||
|             } | |||
|         }, | |||
|         "ModifiableComponent.js": { | |||
|             "folder": "src/component", | |||
|             "name": "ModifiableComponent.js", | |||
|             "exports": [ | |||
|                 "ModifiableComponent" | |||
|             ], | |||
|             "imports": { | |||
|                 "./ChildbearerComponent": [ | |||
|                     "ChildbearerComponent" | |||
|                 ], | |||
|                 "../base": [ | |||
|                     "SStoreDefinition" | |||
|                 ], | |||
|                 "../modifier": [ | |||
|                     "ChainableModifier" | |||
|                 ] | |||
|             } | |||
|         }, | |||
|         "Row.js": { | |||
|             "folder": "src/component", | |||
|             "name": "Row.js", | |||
|             "exports": [ | |||
|                 "Row" | |||
|             ], | |||
|             "imports": { | |||
|                 "./FlexContainerComponent": [ | |||
|                     "FlexContainerComponent" | |||
|                 ], | |||
|                 "../helper": [ | |||
|                     "onSingleOrArray" | |||
|                 ] | |||
|             } | |||
|         }, | |||
|         "StyleAndScriptStoringComponent.js": { | |||
|             "folder": "src/component", | |||
|             "name": "StyleAndScriptStoringComponent.js", | |||
|             "exports": [ | |||
|                 "StyleAndScriptStoringComponent" | |||
|             ], | |||
|             "imports": { | |||
|                 "./ModifiableComponent": [ | |||
|                     "ModifiableComponent" | |||
|                 ], | |||
|                 "../decorators": [ | |||
|                     "Alignment", | |||
|                     "Arrangement" | |||
|                 ], | |||
|                 "../base": [ | |||
|                     "ExtStorage", | |||
|                     "ExtStoreType", | |||
|                     "SStoreDefinition" | |||
|                 ] | |||
|             } | |||
|         }, | |||
|         "context.js": { | |||
|             "folder": "src/context", | |||
|             "name": "context.js", | |||
|             "exports": [ | |||
|                 "PageBuilder", | |||
|                 "CommonCompelGroups", | |||
|                 "Page" | |||
|             ], | |||
|             "imports": { | |||
|                 "./scriptAndStyleContext": [ | |||
|                     "ScriptAndStyleContext" | |||
|                 ], | |||
|                 "../extensions": [ | |||
|                     "CompelExtension" | |||
|                 ] | |||
|             } | |||
|         }, | |||
|         "framework-controls.js": { | |||
|             "folder": "src/context", | |||
|             "name": "framework-controls.js", | |||
|             "exports": [ | |||
|                 "frameworkControlPanel" | |||
|             ], | |||
|             "imports": { | |||
|                 "../builder": [ | |||
|                     "builder" | |||
|                 ], | |||
|                 "../modifier": [ | |||
|                     "Modifier" | |||
|                 ], | |||
|                 "../decorators": [ | |||
|                     "Alignment", | |||
|                     "Arrangement", | |||
|                     "Colors", | |||
|                     "Border", | |||
|                     "MaterialFiveHundredlColors" | |||
|                 ], | |||
|                 "../base": [ | |||
|                     "ExtStoreType" | |||
|                 ], | |||
|                 "../Component": [ | |||
|                     "Component" | |||
|                 ] | |||
|             } | |||
|         }, | |||
|         "scriptAndStyleContext.js": { | |||
|             "folder": "src/context", | |||
|             "name": "scriptAndStyleContext.js", | |||
|             "exports": [ | |||
|                 "ScriptAndStyleContext" | |||
|             ], | |||
|             "imports": { | |||
|                 "../base": [ | |||
|                     "OverwriteBehaviour", | |||
|                     "FunctionStoreBuffer" | |||
|                 ] | |||
|             } | |||
|         }, | |||
|         "alignment.js": { | |||
|             "folder": "src/decorators", | |||
|             "name": "alignment.js", | |||
|             "exports": [ | |||
|                 "Alignment" | |||
|             ] | |||
|         }, | |||
|         "arrangement.js": { | |||
|             "folder": "src/decorators", | |||
|             "name": "arrangement.js", | |||
|             "exports": [ | |||
|                 "Arrangement" | |||
|             ] | |||
|         }, | |||
|         "border.js": { | |||
|             "folder": "src/decorators", | |||
|             "name": "border.js", | |||
|             "exports": [ | |||
|                 "LineStyles", | |||
|                 "BorderDefinition", | |||
|                 "Define", | |||
|                 "Border", | |||
|                 "BorderChain", | |||
|                 "BorderChainedModifier" | |||
|             ], | |||
|             "imports": { | |||
|                 "./siding": [ | |||
|                     "SizeUnits", | |||
|                     "Sides" | |||
|                 ], | |||
|                 "./color": [ | |||
|                     "Color", | |||
|                     "Colors" | |||
|                 ], | |||
|                 "./shapes": [ | |||
|                     "Shapes" | |||
|                 ], | |||
|                 "../modifier": [ | |||
|                     "mixinModSubChainEndings", | |||
|                     "mixinModSubChainComponentMethods" | |||
|                 ] | |||
|             } | |||
|         }, | |||
|         "color.js": { | |||
|             "folder": "src/decorators", | |||
|             "name": "color.js", | |||
|             "exports": [ | |||
|                 "Color", | |||
|                 "Colors", | |||
|                 "MaterialFiveHundredlColors" | |||
|             ] | |||
|         }, | |||
|         "dimensions.js": { | |||
|             "folder": "src/decorators", | |||
|             "name": "dimensions.js", | |||
|             "exports": [ | |||
|                 "Dimensions", | |||
|                 "DimensionsChain", | |||
|                 "DimensionsChainedModifier" | |||
|             ], | |||
|             "imports": { | |||
|                 "./siding": [ | |||
|                     "DirectionUnitDependentAttribute", | |||
|                     "SizeUnits" | |||
|                 ], | |||
|                 "../helper": [ | |||
|                     "TwoDimPoint" | |||
|                 ], | |||
|                 "../modifier": [ | |||
|                     "mixinModSubChainEndings", | |||
|                     "mixinModSubChainComponentMethods" | |||
|                 ] | |||
|             } | |||
|         }, | |||
|         "margin.js": { | |||
|             "folder": "src/decorators", | |||
|             "name": "margin.js", | |||
|             "exports": [ | |||
|                 "Margin", | |||
|                 "MarginChain", | |||
|                 "MarginChainedModifier" | |||
|             ], | |||
|             "imports": { | |||
|                 "./siding": [ | |||
|                     "Sides", | |||
|                     "SizeUnits" | |||
|                 ], | |||
|                 "../modifier": [ | |||
|                     "mixinModSubChainEndings", | |||
|                     "mixinModSubChainComponentMethods" | |||
|                 ] | |||
|             } | |||
|         }, | |||
|         "padding.js": { | |||
|             "folder": "src/decorators", | |||
|             "name": "padding.js", | |||
|             "exports": [ | |||
|                 "Padding", | |||
|                 "PaddingChain", | |||
|                 "PaddingChainedModifier" | |||
|             ], | |||
|             "imports": { | |||
|                 "./siding": [ | |||
|                     "Sides", | |||
|                     "SizeUnits" | |||
|                 ], | |||
|                 "../modifier": [ | |||
|                     "mixinModSubChainEndings", | |||
|                     "mixinModSubChainComponentMethods" | |||
|                 ] | |||
|             } | |||
|         }, | |||
|         "shapes.js": { | |||
|             "folder": "src/decorators", | |||
|             "name": "shapes.js", | |||
|             "exports": [ | |||
|                 "Shape", | |||
|                 "ShapeChain", | |||
|                 "ShapeChainedModifier", | |||
|                 "Shapes" | |||
|             ], | |||
|             "imports": { | |||
|                 "./siding": [ | |||
|                     "DirectionUnitDependentAttribute", | |||
|                     "SizeUnits", | |||
|                     "SidingRefCorners" | |||
|                 ], | |||
|                 "../modifier": [ | |||
|                     "mixinModSubChainEndings", | |||
|                     "mixinModSubChainComponentMethods" | |||
|                 ] | |||
|             } | |||
|         }, | |||
|         "siding.js": { | |||
|             "folder": "src/decorators", | |||
|             "name": "siding.js", | |||
|             "exports": [ | |||
|                 "SizeUnits", | |||
|                 "DirectionUnitDependentAttribute", | |||
|                 "SideDirections", | |||
|                 "SideTransitionDirection", | |||
|                 "Corners", | |||
|                 "CornerTransitionDirection", | |||
|                 "Sides" | |||
|             ] | |||
|         }, | |||
|         "defaultGenerators.js": { | |||
|             "folder": "src/generators", | |||
|             "name": "defaultGenerators.js", | |||
|             "exports": [ | |||
|                 "singlepage" | |||
|             ], | |||
|             "imports": { | |||
|                 "./generator": [ | |||
|                     "CompelGenerator" | |||
|                 ] | |||
|             } | |||
|         }, | |||
|         "generator.js": { | |||
|             "folder": "src/generators", | |||
|             "name": "generator.js", | |||
|             "exports": [ | |||
|                 "CompelGenerator" | |||
|             ], | |||
|             "imports": { | |||
|                 "../base": [ | |||
|                     "WebTrinity", | |||
|                     "ExtStoreType" | |||
|                 ], | |||
|                 "../Component": [ | |||
|                     "Component" | |||
|                 ], | |||
|                 "../modifier": [ | |||
|                     "Modifier" | |||
|                 ] | |||
|             } | |||
|         }, | |||
|         "commonEvents.js": { | |||
|             "folder": "src/handlers", | |||
|             "name": "commonEvents.js", | |||
|             "exports": [ | |||
|                 "CommonEvents" | |||
|             ] | |||
|         }, | |||
|         "contextMenu.js": { | |||
|             "folder": "src/handlers", | |||
|             "name": "contextMenu.js", | |||
|             "exports": [ | |||
|                 "DefaultContextMenu" | |||
|             ], | |||
|             "imports": { | |||
|                 "../decorators": [ | |||
|                     "Sides" | |||
|                 ], | |||
|                 "../helper": [ | |||
|                     "helperFun", | |||
|                     "getEnclosingBounds" | |||
|                 ] | |||
|             } | |||
|         }, | |||
|         "dragAndDrop.js": { | |||
|             "folder": "src/handlers", | |||
|             "name": "dragAndDrop.js", | |||
|             "exports": [ | |||
|                 "EventDrag", | |||
|                 "DragAndDropImplementation", | |||
|                 "DADInPlace" | |||
|             ], | |||
|             "imports": { | |||
|                 "../helper": [ | |||
|                     "TwoDimPoint" | |||
|                 ], | |||
|                 "../decorators": [ | |||
|                     "Dimensions" | |||
|                 ] | |||
|             } | |||
|         }, | |||
|         "general.js": { | |||
|             "folder": "src/helper", | |||
|             "name": "general.js", | |||
|             "exports": [ | |||
|                 "onSingleOrArray", | |||
|                 "helperFun" | |||
|             ] | |||
|         }, | |||
|         "indices.js": { | |||
|             "folder": "src/helper", | |||
|             "name": "indices.js", | |||
|             "exports": [ | |||
|                 "isValueInBounds", | |||
|                 "areXYInArea", | |||
|                 "isPointInArea", | |||
|                 "getEnclosingBounds" | |||
|             ], | |||
|             "imports": { | |||
|                 "../decorators": [ | |||
|                     "SideDirections" | |||
|                 ], | |||
|                 "./TwoDimPoint": [ | |||
|                     "TwoDimPoint" | |||
|                 ] | |||
|             } | |||
|         }, | |||
|         "ObjectAccessObject.js": { | |||
|             "folder": "src/helper", | |||
|             "name": "ObjectAccessObject.js", | |||
|             "exports": [ | |||
|                 "ObjectAccessObject" | |||
|             ] | |||
|         }, | |||
|         "TwoDimPoint.js": { | |||
|             "folder": "src/helper", | |||
|             "name": "TwoDimPoint.js", | |||
|             "exports": [ | |||
|                 "TwoDimPoint" | |||
|             ] | |||
|         }, | |||
|         "ChainableModifier.js": { | |||
|             "folder": "src/modifier", | |||
|             "name": "ChainableModifier.js", | |||
|             "exports": [ | |||
|                 "ChainableModifier" | |||
|             ], | |||
|             "imports": { | |||
|                 "./modifier": [ | |||
|                     "Modifier" | |||
|                 ], | |||
|                 "../Component": [ | |||
|                     "Component" | |||
|                 ], | |||
|                 "../decorators": [ | |||
|                     "PaddingChainedModifier", | |||
|                     "MarginChainedModifier", | |||
|                     "ShapeChainedModifier", | |||
|                     "BorderChainedModifier" | |||
|                 ] | |||
|             } | |||
|         }, | |||
|         "mixinModSubChain.js": { | |||
|             "folder": "src/modifier", | |||
|             "name": "mixinModSubChain.js", | |||
|             "exports": [ | |||
|                 "mixinModSubChainEndings", | |||
|                 "mixinModSubChainComponentMethods" | |||
|             ], | |||
|             "imports": { | |||
|                 "./modifier": [ | |||
|                     "Modifier" | |||
|                 ], | |||
|                 "./ChainableModifier": [ | |||
|                     "ChainableModifier" | |||
|                 ] | |||
|             } | |||
|         }, | |||
|         "ModificationSubChain.js": { | |||
|             "folder": "src/modifier", | |||
|             "name": "ModificationSubChain.js", | |||
|             "exports": [ | |||
|                 "ModificationSubChain", | |||
|                 "ModificationSubChainReComp" | |||
|             ] | |||
|         }, | |||
|         "Modifier.js": { | |||
|             "folder": "src/modifier", | |||
|             "name": "Modifier.js", | |||
|             "exports": [ | |||
|                 "Modifier" | |||
|             ], | |||
|             "imports": { | |||
|                 "../decorators": [ | |||
|                     "Sides", | |||
|                     "Border", | |||
|                     "BorderChain", | |||
|                     "Color", | |||
|                     "Dimensions", | |||
|                     "DimensionsChain", | |||
|                     "Margin", | |||
|                     "MarginChain", | |||
|                     "Padding", | |||
|                     "PaddingChain", | |||
|                     "Shape", | |||
|                     "ShapeChain" | |||
|                 ] | |||
|             } | |||
|         } | |||
|     } | |||
| } | |||
								
									
										File diff suppressed because it is too large
									
								
							
						
					| @ -1,20 +1,31 @@ | |||
| { | |||
|   "name": "websites-like-jpc", | |||
|   "version": "1.0.0", | |||
|   "name": "jpc-like-websites", | |||
|   "version": "1.9.13.34", | |||
|   "description": "Framework to build websites in a Jetpack Compose like manner, as well as an extensive use of method-chaingin.", | |||
|   "main": "lib/index.js", | |||
|   "typings": "lib/index.d.ts", | |||
|   "main": "jpc-like-websites.js", | |||
|   "exports": { | |||
|     "vanilla": "./jpc-like-websites.js", | |||
|     "module": "./jpc-like-websites.mjs" | |||
|   }, | |||
|   "files": [ | |||
|     "/lib" | |||
|     "/src" | |||
|   ], | |||
|   "scripts": { | |||
|     "prepare": "npm run build", | |||
|     "build": "tsc", | |||
|     "prepare": "node build.js true", | |||
|     "generate": "node build.js true", | |||
|     "build": "npm run generate", | |||
|     "test": "echo \"Error: no test specified\" && exit 1" | |||
|   }, | |||
|   "author": "", | |||
|   "license": "ISC", | |||
|   "author": "cm", | |||
|   "license": "SEE LICENCE.md", | |||
|   "dependencies": { | |||
|     "jpclw-extension": "git+https://git.labos.goip.de/chris/jpclw-extension.git" | |||
|   }, | |||
|   "devDependencies": { | |||
|     "typescript": "^5.6.2" | |||
|     "@eslint/css": "^0.7.0", | |||
|     "@eslint/js": "^9.26.0", | |||
|     "@eslint/json": "^0.12.0", | |||
|     "eslint": "^9.26.0", | |||
|     "globals": "^16.1.0" | |||
|   } | |||
| } | |||
| } | |||
| @ -1,19 +0,0 @@ | |||
| /** | |||
|  * This file is part of the jps-like-websites lib | |||
|  * URL: https://git.labos.goip.de/chris/jpc-like-websites
 | |||
|  * COPYRIGHT and LICENCE are owned by its creator Christian Martin  | |||
|  * Copy, altering or distribution without the allowance of the owner are prohibited | |||
|  */ | |||
| 
 | |||
| /** | |||
|  * Enum providing common alignment rules | |||
|  */ | |||
| const Arrangement = Object.freeze({ | |||
|     START: "start", | |||
|     END: "end", | |||
|     CENTER: "center", | |||
|     SPACE_BETWEEN: "space-between", | |||
|     SPACE_EVENLY: "space-evenly", | |||
|     SPACE_AROUND: "space-around", | |||
| 
 | |||
| }) | |||
| @ -0,0 +1,660 @@ | |||
| /** | |||
|  * ESAggregation := Extensions Storage Aggregation (method) | |||
|  */ | |||
| const ESAggregation = Object.freeze({ | |||
|     INTERNALIZED: "intern", | |||
|     INDIVIDUALLY: "individual", | |||
|     COLLECTED: "collected", | |||
|     CENTRALIZED: "centralized" | |||
| }); | |||
| 
 | |||
| /** | |||
|  * ExtStoragePos := Extensions Storage Position | |||
|  *  | |||
|  * Determines where the extensions are positioned. | |||
|  * Only relevant if ExtStorage is not 'internalized'. | |||
|  * Determines where the tag (if individually) or the extensions are positioned. | |||
|  */ | |||
| const ExtStorePosition = Object.freeze({ | |||
|     WITHIN: "WITHIN", | |||
|     BEFORE: "BEFORE", | |||
|     SEGMENT_BEGIN: "SEGMENT_BEGIN", | |||
|     DOC_HEAD: "DOC_HEAD", | |||
|     DOC_FOOTER: "DOC_FOOTER" | |||
| }); | |||
| 
 | |||
| /** | |||
|  * Defines how an identified dupplication should be "resolved"/dealt with. | |||
|  * REPLACE: | |||
|  * RENAME: | |||
|  * RENAME_OLD: | |||
|  * DROP_NEW: | |||
|  * MOVE_ELEMENT_SPECIFIC: @ATTENTION implementation pending | |||
|  */ | |||
| const OverwriteBehaviour = Object.freeze({ | |||
|     REPLACE: "REPLACE", | |||
|     RENAME: "RENAME", | |||
|     RENAME_OLD: "RENAME_OLD", | |||
|     DROP_NEW: "DROP_NEW", | |||
|     MOVE_ELEMENT_SPECIFIC: "MOVE_ELEMENT_SPECIFIC" | |||
| }); | |||
| 
 | |||
| 
 | |||
| /** | |||
|  * Is supposed to shrink all empty strings to length 1 | |||
|  * @param {Function} func  | |||
|  * @returns {string} | |||
|  */ | |||
| function clearFunctionDeclarationText(func) { | |||
|     function shrinkEmptyStrings(text) { | |||
|         for (let i = 1; i < 10; i++) { | |||
|             text = text.replaceAll("          ".slice(i), ' '); | |||
|         } | |||
|         return text; | |||
|     } | |||
|     return shrinkEmptyStrings( | |||
|         func.toString() | |||
|             .replaceAll('\r\n', ' ') | |||
|             .replaceAll('\n\r', ' ') | |||
|             .replaceAll('\n', ' ') | |||
|             .replaceAll('\r', ' ') | |||
|     ); | |||
| } | |||
| 
 | |||
| /** | |||
|  * @todo when ExtStorage-feature/logic is reevaluated  | |||
|  *      the resolvement of functions (as basically major or main target) | |||
|  *      needs to be overlooked improved and simplified.  | |||
|  *  | |||
|  * @param {Function} func  | |||
|  * @param {string} registrationName  | |||
|  * @returns {string} | |||
|  */ | |||
| function getScriptTagInjectionText(func, registrationName) { | |||
|     let funcHasName; | |||
|     if (typeof func === 'function') { | |||
|         funcHasName = ((func.name) && func.name.trim() !== ''); | |||
|     } | |||
| 
 | |||
|     if (func.startsWith('function')) { | |||
|         let label = ` function ${registrationName}`; | |||
|         let isNameInFuncText = func.startsWith(label); | |||
|         if (isNameInFuncText) { | |||
|             return func; | |||
|         } else { | |||
|             return [label, '(', func.split('(').slice(1).join('(')].join('') | |||
|         } | |||
|     } else { | |||
|         return ` const ${registrationName} = ${func}; `; | |||
|     } | |||
| } | |||
| 
 | |||
| /** | |||
|  * Stores a function until generate is called. | |||
|  * Then the additional informations of the store wil be applied  | |||
|  * and the funcitons added to the page. | |||
|  */ | |||
| class FunctionStoreBuffer { | |||
|     /** | |||
|      * Stores a function until generate is called. | |||
|      * Then the additional informations of the store wil be applied  | |||
|      * and the funcitons added to the page. | |||
|      * @param {Function} func the function that will be stored | |||
|      * @param {Array<any>} args additional arguments that will be given to the function | |||
|      * @param {boolean} repeats weither the funciton is supposed to execute repeatedly | |||
|      * @param {number} interval the time in milliseconds between executions | |||
|      * @param {boolean} execAfterStart weither the function is supposed to be executed after pageload | |||
|      * @param {number} delay the time in milliseconds the execution will be delayed | |||
|      */ | |||
|     constructor( | |||
|         func, | |||
|         args = [], | |||
|         repeats = false, | |||
|         interval = -1, | |||
|         execAfterStart = false, | |||
|         delay = -1 | |||
|     ) { | |||
|         this.func = func; | |||
|         this.args = args; | |||
|         this.execAfterStart = execAfterStart; | |||
|         this.delay = delay; | |||
|         this.repeats = repeats; | |||
|         this.interval = interval; | |||
|     } | |||
| } | |||
| 
 | |||
| /** | |||
|  * Extracted this super class to differentiate between  | |||
|  * internal and external store. | |||
|  */ | |||
| class ExtStorage { | |||
|     constructor( | |||
|         aggregation = ESAggregation.INTERNALIZED, | |||
|         position = ExtStorePosition.WITHIN, | |||
|         behaviour = OverwriteBehaviour.DROP_NEW | |||
|     ) { | |||
|         /** | |||
|          * @type {ESAggregation} | |||
|          */ | |||
|         this._aggregation = aggregation; | |||
|         /** | |||
|          * @type {ExtStorePosition} | |||
|          */ | |||
|         this._position = position; | |||
|         /** | |||
|          * @type {OverwriteBehaviour} | |||
|          */ | |||
|         this._overwriteBehaviour = behaviour; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {ESAggregation} position  | |||
|      */ | |||
|     setExtStoreAggregation(aggregation) { | |||
|         this._aggregation = aggregation; | |||
|         return this; | |||
|     } | |||
|     /** | |||
|      *  | |||
|      * @param {ExtStoreType} position  | |||
|     */ | |||
|     setExtStorePosition(position) { | |||
|         this._position = position; | |||
|         return this; | |||
|     } | |||
|     /** | |||
|      *  | |||
|      * @param {OverwriteBehaviour} behave  | |||
|      * @returns {ExtStorage} | |||
|      */ | |||
|     setOverwriteBehaviour(behave) { | |||
|         this._overwriteBehaviour = behave; | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {ExtStorage} extStore  | |||
|      * @returns {boolean} | |||
|      */ | |||
|     equals(extStore = null) { | |||
|         if (!extStore) return false; | |||
| 
 | |||
|         return extStore._type === this._type | |||
|             && extStore._overwriteBehaviour === this._overwriteBehaviour; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @returns {boolean} | |||
|      */ | |||
|     isMissing() { | |||
|         return this._type === null || this._overwriteBehaviour === null; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @returns {boolean} | |||
|      */ | |||
|     isNotInternalOrIndividual() { | |||
|         return !( | |||
|             this._aggregation === ESAggregation.INTERNALIZED | |||
|             || this._aggregation === ESAggregation.INDIVIDUALLY | |||
|         ); | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {ExtStorage} otherExtStore  | |||
|      * @returns {ExtStorage} | |||
|      */ | |||
|     fillBy(otherExtStore) { | |||
|         if (this._type === null) { | |||
|             this._type = otherExtStore._type; | |||
|         } | |||
| 
 | |||
|         if (this._overwriteBehaviour === null) { | |||
|             this._overwriteBehaviour = otherExtStore._overwriteBehaviour; | |||
|         } | |||
| 
 | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * @todo check if still implemented correctly | |||
|      * Takes the singleValue and an ExtStore object to copy all values from. | |||
|      * Then the singleValue will be compared to the three enums for the type of value. | |||
|      * After the type is identified the corresponding (copied) value will be updated. | |||
|      * @param {ExtStoreType|ExtStorePosition|OverwriteBehaviour} singleValue  | |||
|      * @param {ExtStorage} extStoreToClone  | |||
|      * @returns {ExtStorage} | |||
|      */ | |||
|     setSingleValueToClone(singleValue, extStoreToClone) { | |||
|         this._type = extStoreToClone._type; | |||
|         this._position = extStoreToClone._position; | |||
|         this._overwriteBehaviour = extStoreToClone._overwriteBehaviour; | |||
| 
 | |||
|         let target = [ | |||
|             ...Object.values(ExtStoreType).map(text => Object({ "value": text, "ref": "type" })), | |||
|             ...Object.values(ExtStorePosition).map(text => Object({ "value": text, "ref": "pos" })), | |||
|             ...Object.values(OverwriteBehaviour).map(text => Object({ "value": text, "ref": "over" })) | |||
|         ] | |||
|             .find(compareObj => compareObj["value"] === singleValue); | |||
| 
 | |||
|         if (target) { | |||
|             switch (target["ref"]) { | |||
|                 case "type": | |||
|                     this._type = singleValue; | |||
|                     break; | |||
|                 case "pos": | |||
|                     this._position = singleValue; | |||
|                     break; | |||
|                 case "over": | |||
|                     this._overwriteBehaviour = singleValue; | |||
| 
 | |||
|                     break; | |||
|             } | |||
|         } | |||
| 
 | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @returns {ExtStorage} this extStore (updated if rules were used, that don't work for functions) | |||
|      */ | |||
|     setupForFunctions() { | |||
|         if (this._type === ExtStoreType.INTERNALIZED_WITHIN) { | |||
|             console.log("Updated Functions extstore from INTERNALIZED_WITHIN to INDIVIDUALLY_BEFORE") | |||
|             this._type = ExtStoreType.INDIVIDUALLY_BEFORE; | |||
|         } | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @returns {ExtStorage} | |||
|      */ | |||
|     setupForGeneralStyling() { | |||
|         if (this === ExtStoreType.INTERNALIZED_WITHIN) { | |||
|             this._position = ExtStorePosition.WITHIN; | |||
|             this._aggregation = ESAggregation.INTERNALIZED; | |||
|             return this; | |||
|         } | |||
| 
 | |||
|         this._position = ExtStorePosition.DOC_HEAD; | |||
| 
 | |||
|         switch (this) { | |||
|             case ExtStoreType.INDIVIDUALLY_WITHIN: | |||
|             case ExtStoreType.INDIVIDUALLY_BEFORE: | |||
|             case ExtStoreType.INDIVIDUALLY_SEGMENT_BEGIN: | |||
|             case ExtStoreType.INDIVIDUALLY_DOC_FOOTER: | |||
|             case ExtStoreType.INDIVIDUALLY_DOC_HEAD: | |||
|                 this._aggregation = ESAggregation.INDIVIDUALLY; | |||
|                 break; | |||
| 
 | |||
|             case ExtStoreType.COLLECTED_BEFORE: | |||
|             case ExtStoreType.COLLECTED_SEGMENT_BEGIN: | |||
|             case ExtStoreType.COLLECTED_DOC_FOOTER: | |||
|             case ExtStoreType.COLLECTED_DOC_HEAD: | |||
|                 this._aggregation = ESAggregation.COLLECTED; | |||
| 
 | |||
|                 this._aggregation = ESAggregation.COLLECTED; | |||
|                 break; | |||
|             case ExtStoreType.CENTRALIZED_DOC_HEAD: | |||
|             case ExtStoreType.CENTRALIED_SEGMENT_BEGIN: | |||
|             case ExtStoreType.CENTRALIZED_DOC_FOOTER: | |||
|             default: | |||
|                 this._aggregation = ESAggregation.CENTRALIZED; | |||
|                 break | |||
|         } | |||
| 
 | |||
|         return this; | |||
|     } | |||
| 
 | |||
| 
 | |||
|     /** | |||
|      * Currently it works the same as the "updateForFunctions()" since the same rules won't work. | |||
|      * @returns {ExtStorage} this extStore (updated if rules were used, that won't work for StyleClasses) | |||
|      */ | |||
|     setupForStyleClass() { | |||
|         /* | |||
|         const positionedAfter = [ | |||
|             COLLECTED_DOC_FOOTER, | |||
|             INDIVIDUALLY_DOC_FOOTER, | |||
|             CENTRALIZED_DOC_FOOTER | |||
|         ]; | |||
| 
 | |||
|         if (positionedAfter.includes(this._type)) { | |||
|             this._type = ExtStoreType.INTERNALIZED_WITHIN; | |||
|         } | |||
|         */ | |||
| 
 | |||
|         return this.setupForGeneralStyling(); | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @returns {InsertPosition} | |||
|      */ | |||
|     getRelativePositioning() { | |||
|         switch (this._position) { | |||
|             case ExtStorePosition.BEFORE: | |||
|                 return "beforebegin" | |||
|             case ExtStorePosition.SEGMENT_BEGIN: | |||
|                 return "afterbegin"; | |||
|             case ExtStorePosition.DOC_HEAD: | |||
|             case ExtStorePosition.DOC_FOOTER: | |||
|                 return "beforeend" | |||
|             case ExtStorePosition.WITHIN: | |||
|             default: | |||
|                 return "afterbegin"; | |||
|         } | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Expects a reference element for the positions before and segment_begin. | |||
|      * Otherwise will return head, footer or element accordingly. | |||
|      * @param {HTMLLIElement|Component} element  | |||
|      * @returns {HTMLElement} | |||
|      */ | |||
|     getRefElement(element = null) { | |||
|         let ensuredElement = element; | |||
|         if (!element) { | |||
|             console.log("ExtStorePosition defines a relative position, but no reference Element is given - using head!") | |||
|             return document.querySelector('head'); | |||
|         } | |||
| 
 | |||
|         if (element instanceof Component) { | |||
|             ensuredElement = element.generate().compext; | |||
|         } | |||
| 
 | |||
|         switch (this._position) { | |||
|             case ExtStorePosition.BEFORE: | |||
|             case ExtStorePosition.SEGMENT_BEGIN: | |||
|                 return ensuredElement.closest('[data-compel-isHCompel="true"]'); | |||
|             case ExtStorePosition.DOC_HEAD: | |||
|                 return document.querySelector('head'); | |||
|             case ExtStorePosition.DOC_FOOTER: | |||
|                 return document.querySelector('footer'); | |||
|             case ExtStorePosition.WITHIN: | |||
|             default: | |||
|                 return ensuredElement; | |||
|         } | |||
|     } | |||
| 
 | |||
|     insertElementAccordingly(element) { | |||
|         this.getRefElement(element) | |||
|             .insertAdjacentElement( | |||
|                 this.getRelativePositioning(), | |||
|                 this.getRefElement(element) | |||
|             ) | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Returns a function that will setup the distribution of a given styling. | |||
|      * @returns {function(SStoreDefinition,HTMLElement,number): boolean} | |||
|      */ | |||
|     getStylingDistribution() { | |||
|         switch (this._aggregation) { | |||
|             case ESAggregation.INDIVIDUALLY: | |||
|                 return function (ssd, orgElement, counter) { | |||
|                     let container = generateAndFillStyleTag([ssd]); | |||
|                     container.setAttribute("data-compel-individually-nr", counter++); | |||
|                     Page.addElementToPage(container, this); | |||
|                     return false; | |||
|                 } | |||
|             case ESAggregation.COLLECTED: | |||
|                 return function (ssd, orgElement) { | |||
|                     return true; | |||
|                 } | |||
|             case ESAggregation.CENTRALIZED: | |||
|                 return function (ssd, orgElement) { | |||
|                     Page.registerStyling(ssd._identifier, ssd._definition); | |||
|                     return false; | |||
|                 } | |||
| 
 | |||
|             case ESAggregation.INTERNALIZED: | |||
|             default: | |||
|                 return function (ssd, orgElement) { | |||
|                     helperFun.fillAttrsInContainerByCb( | |||
|                         ssd._definition, | |||
|                         orgElement, | |||
|                         (key, val, el) => { el.style[key] = val; } | |||
|                     ); | |||
|                     return false; | |||
|                 } | |||
|         } | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @returns {function(SStoreDefinition, Map<ExtStorage, Array<SStoreDefinition>, number): boolean} | |||
|      */ | |||
|     getFunctionDistribution() { | |||
|         switch (this._aggregation) { | |||
|             case ESAggregation.INTERNALIZED: | |||
|             case ESAggregation.INDIVIDUALLY: | |||
|                 return function (ssd, counter) { | |||
|                     let container = document.createElement("script"); | |||
|                     container.setAttribute("data-compel-individually-nr", counter++); | |||
|                     container.innerText += getScriptTagInjectionText( | |||
|                         clearFunctionDeclarationText(ssd._definition), | |||
|                         ssd._identifier | |||
|                     ); | |||
|                     Page.addElementToPage(container, refESType); | |||
|                     return false; | |||
|                 } | |||
|             case ESAggregation.COLLECTED: | |||
|                 return function () { | |||
|                     return true; | |||
|                 } | |||
| 
 | |||
|             case ESAggregation.CENTRALIZED: | |||
|             default: | |||
|                 return function (ssd) { | |||
|                     Page.registerPageFunction(ssd._identifier, ssd._definition); | |||
|                     return false; | |||
|                 } | |||
|         } | |||
|     } | |||
| } | |||
| 
 | |||
| 
 | |||
| /** | |||
|  * ExtStorage := Extensions storage (type) | |||
|  * Extensions in this context are stylings and scripts (currently only javascript).  | |||
|  * internalized: the extensions are part of the element code/attributes - works obviously only with styling | |||
|  * individually: an individual tag is created/used | |||
|  * collected:    the extension can/will be collected with others in a higher position of the element hierarchy  | |||
|  *               (but not document - root) | |||
|  * centralized:  the extensions are send to the Page to be joined in a centralized tag/position of the document  | |||
|  *               (either head or footer tag) | |||
|  */ | |||
| const ExtStoreType = Object.freeze({ | |||
|     INTERNALIZED_WITHIN: new ExtStorage(ESAggregation.INDIVIDUALLY, ExtStorePosition.WITHIN), | |||
|     INDIVIDUALLY_WITHIN: new ExtStorage(ESAggregation.INDIVIDUALLY, ExtStorePosition.WITHIN), | |||
|     INDIVIDUALLY_BEFORE: new ExtStorage(ESAggregation.INDIVIDUALLY, ExtStorePosition.BEFORE), | |||
|     INDIVIDUALLY_SEGMENT_BEGIN: new ExtStorage(ESAggregation.INDIVIDUALLY, ExtStorePosition.SEGMENT_BEGIN), | |||
|     INDIVIDUALLY_DOC_HEAD: new ExtStorage(ESAggregation.INDIVIDUALLY, ExtStorePosition.DOC_HEAD), | |||
|     INDIVIDUALLY_DOC_FOOTER: new ExtStorage(ESAggregation.INDIVIDUALLY, ExtStorePosition.DOC_FOOTER), | |||
|     COLLECTED_BEFORE: new ExtStorage(ESAggregation.COLLECTED, ExtStorePosition.BEFORE), | |||
|     COLLECTED_SEGMENT_BEGIN: new ExtStorage(ESAggregation.COLLECTED, ExtStorePosition.SEGMENT_BEGIN), | |||
|     COLLECTED_DOC_HEAD: new ExtStorage(ESAggregation.COLLECTED, ExtStorePosition.DOC_HEAD), | |||
|     COLLECTED_DOC_FOOTER: new ExtStorage(ESAggregation.COLLECTED, ExtStorePosition.DOC_FOOTER), | |||
|     CENTRALIZED_DOC_HEAD: new ExtStorage(ESAggregation.CENTRALIZED, ExtStorePosition.DOC_HEAD), | |||
|     CENTRALIZED_SEGMENT_BEGIN: new ExtStorage(ESAggregation.CENTRALIZED, ExtStorePosition.SEGMENT_BEGIN), | |||
|     CENTRALIZED_DOC_FOOTER: new ExtStorage(ESAggregation.CENTRALIZED, ExtStorePosition.DOC_FOOTER) | |||
| }); | |||
| 
 | |||
| /** | |||
|  * Style or Script Store Definition | |||
|  * @property {string} _identifier; | |||
|  * @property {any} _definition; | |||
|  * @property {any} _additionaly; | |||
|  * @property {ExtStorage} _extStore; | |||
|  */ | |||
| class SStoreDefinition { | |||
|     constructor(identifier, definition, extStore = null, additions = null) { | |||
|         /** | |||
|          * Usually the name or the selector | |||
|          * @type {string} _identifier; | |||
|          */ | |||
|         this._identifier = identifier; | |||
|         /** | |||
|          * the values | |||
|          * @type {any} _definition; | |||
|          */ | |||
|         this._definition = definition; | |||
|         /** | |||
|          * additional values, if needed. E.g. funciton args | |||
|          * @type {any} _additionaly; | |||
|          */ | |||
|         this._additions = additions; | |||
|         /** | |||
|          * The corresponding extStore | |||
|          * @type {ExtStorage} _extStore; | |||
|          */ | |||
|         this._extStore = extStore; | |||
|     } | |||
| } | |||
| 
 | |||
| 
 | |||
| /** | |||
|  * Resolves an overwrite case for a map/object. | |||
|  * @param {string} key  | |||
|  * @param {Map|Object} container  | |||
|  * @param {OverwriteBehaviour} overwriteBehaviour  | |||
|  * @returns {string} the key to be used | |||
|  */ | |||
| function resolveOverwrite(key, container, overwriteBehaviour) { | |||
|     let dealAsMap = container instanceof Map; | |||
|     let occurances = [...( | |||
|         dealAsMap | |||
|             ? container.keys() | |||
|             : Object.keys(container) | |||
|     ) | |||
|         .filter(e => e.includes(key) | |||
|         )].length; | |||
| 
 | |||
|     switch (overwriteBehaviour) { | |||
|         case OverwriteBehaviour.REPLACE: { | |||
|             break; | |||
|          | |||
|         } | |||
|         case OverwriteBehaviour.RENAME_OLD: { | |||
|             let nameForOld = `${key}${occurances}`; | |||
|             if (dealAsMap) { | |||
|                 container.set(nameForOld, container.get(key)); | |||
|                 container.delete(key); | |||
|             } else { | |||
|                 container[nameForOld] = container[key]; | |||
|                 delete container[key]; | |||
|             } | |||
|             break; | |||
|          | |||
|         } | |||
|         case OverwriteBehaviour.RENAME:  | |||
|         default:  | |||
|             key = `${key}${occurances}`; | |||
|             break; | |||
|     } | |||
| 
 | |||
|     return key; | |||
| } | |||
| 
 | |||
| /** | |||
|  * Will resolve the compareKey according to the overwriteBehaviour  | |||
|  * and add the newValue to the targetContainer with it. | |||
|  * @param {Object} targetContainer  | |||
|  * @param {string} compareKey  | |||
|  * @param {Object} newValue  | |||
|  * @param {OverwriteBehaviour} overwriteBehaviour  | |||
|  * @returns {string} the "resolved" compareKey | |||
|  */ | |||
| function identifyAndResolveOverwrite(targetContainer, compareKey, newValue, overwriteBehaviour) { | |||
|     let keys = Object.keys(targetContainer); | |||
|     if (keys.includes(compareKey)) { | |||
|         if (overwriteBehaviour === OverwriteBehaviour.DROP_NEW) { | |||
|             console.log("Not Adding, because overwrite is set to DROP_NEW"); | |||
|             return compareKey; | |||
|         } | |||
| 
 | |||
|         compareKey = resolveOverwrite(compareKey, targetContainer, overwriteBehaviour); | |||
|     } | |||
| 
 | |||
|     targetContainer[compareKey] = newValue; | |||
|     return compareKey; | |||
| } | |||
| 
 | |||
| /** | |||
|  * Creates a new Script Tag  | |||
|  * and then fills the given css rules into it. | |||
|  * @param {Array<SStoreDefinition>} ssdArray  | |||
|  * @returns {HTMLScriptElement} | |||
|  */ | |||
| function generateAndFillScriptTag(ssdArray) { | |||
|     let tag = document.createElement("script"); | |||
|     tag.setAttribute("data-compel-gen", "true"); | |||
| 
 | |||
|     for (let i = 0; i < ssdArray.length; i++) { | |||
|         const ssd = ssdArray[i]; | |||
|         tag.innerText += getScriptTagInjectionText( | |||
|             clearFunctionDeclarationText(ssd._definition), | |||
|             ssd._identifier | |||
|         ); | |||
|     } | |||
| 
 | |||
|     return tag; | |||
| } | |||
| 
 | |||
| 
 | |||
| /** | |||
|  *  | |||
|  * @param {string} selector  | |||
|  * @param {Map<string,string>} stylingMap  | |||
|  * @returns {string} | |||
|  */ | |||
| function getStylingInjectionText(selector, stylingMap) { | |||
|     function keyValueToString(key) { | |||
|         return `${key}: ${stylingMap[key]}; `; | |||
|     } | |||
| 
 | |||
|     return `${selector | |||
|         } { ${Object.keys(stylingMap) | |||
|             .map(keyValueToString) | |||
|             .join(" ") | |||
|         } }; `;
 | |||
| } | |||
| 
 | |||
| /** | |||
|  *  | |||
|  * @param {Array<SStoreDefinition>} ssdArray  | |||
|  * @returns {HTMLStyleElement} | |||
|  */ | |||
| function generateAndFillStyleTag(ssdArray) { | |||
|     let tag = document.createElement("style"); | |||
|     tag.setAttribute("data-compel-gen", "true"); | |||
| 
 | |||
|     for (let i = 0; i < ssdArray.length; i++) { | |||
|         const ssd = ssdArray[i]; | |||
|         tag.innerText += getStylingInjectionText(ssd._identifier, ssd._definition); | |||
|     } | |||
| 
 | |||
|     return tag; | |||
| } | |||
| 
 | |||
| 
 | |||
| /** | |||
|  * Executes the given function upon the delegating ExtStoreTypes | |||
|  * @param {Function} func  | |||
|  * @returns {Map<ExtStoreType, *} | |||
|  */ | |||
| function executeOnExtStoreTypeCollectedTriple(func) { | |||
|     return new Map([ | |||
|         { [ExtStoreType.COLLECTED_SEGMENT_BEGIN]: func(ExtStoreType.COLLECTED_SEGMENT_BEGIN) }, | |||
|         { [ExtStoreType.COLLECTED_DOC_HEAD]: func(ExtStoreType.COLLECTED_DOC_HEAD) }, | |||
|         { [ExtStoreType.COLLECTED_DOC_FOOTER]: func(ExtStoreType.COLLECTED_DOC_FOOTER) } | |||
|     ]); | |||
| } | |||
| @ -0,0 +1,49 @@ | |||
| /** | |||
|  * Wenity := Web Trinity | |||
|  */ | |||
| class WebTrinity { | |||
|     /** | |||
|      * @deprecated renamed - use copext instead | |||
|      * @type {HTMLElement|Component|string} = compext, for a migration period | |||
|      */ | |||
|     html; | |||
| 
 | |||
|     /** | |||
|      * @deprecated renamed - use scripts instead | |||
|      * @type {HTMLStyleElement|Map<ExtStorageType, Array<SStoreDefinition>} = scripts, for a migration period | |||
|      */ | |||
|     js; | |||
| 
 | |||
|     /** | |||
|      * @deprecated renamed - use stylings instead | |||
|      * @type {HTMLScriptElement|Array<SStoreDefinition>} = stylings, for a migration period | |||
|      */ | |||
|     css; | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {HTMLElement|Component|string} compext := Component Text | |||
|      * @param {HTMLStyleElement|Map<ExtStorageType, Array<SStoreDefinition>} scripts  | |||
|      * @param {HTMLScriptElement|Array<SStoreDefinition>} stylings | |||
|      */ | |||
|     constructor(compext = null, scripts = null, stylings = null) { | |||
|         this.compext = compext; | |||
|         this.html = compext; | |||
|         this.scripts = scripts; | |||
|         this.js = scripts; | |||
|         this.stylings = stylings; | |||
|         this.css = stylings; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @returns {boolean} | |||
|      */ | |||
|     isSSEmpty() { | |||
|         return (this.scripts === null || this.scripts.length === 0) | |||
|             && (this.stylings === null || this.stylings.size === 0) | |||
|             /* LEGACY CHECK */ | |||
|             && (this.js === null || this.js.length === 0) | |||
|             && (this.css === null || this.css.size === 0); | |||
|     } | |||
| } | |||
| @ -1,106 +0,0 @@ | |||
| /** | |||
|  * This file is part of the jps-like-websites lib | |||
|  * URL: https://git.labos.goip.de/chris/jpc-like-websites
 | |||
|  * COPYRIGHT and LICENCE are owned by its creator Christian Martin  | |||
|  * Copy, altering or distribution without the allowance of the owner are prohibited | |||
|  */ | |||
| 
 | |||
| /** | |||
|  * Represents container Components. | |||
|  * Some predefined modifications are applied on the child components. | |||
|  */ | |||
| class FlexContainerComponent extends Component { | |||
|     constructor(attr = {}) { | |||
|         super(document.createElement("div"), attr) | |||
|             .addStyleClass("flex-container-component") | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {Component|Array<Component>} innerComponent  | |||
|      * @returns {FlexContainerComponent} this component object | |||
|      */ | |||
|     childContext(innerComponent) { | |||
|         if (innerComponent instanceof Array) { | |||
|             innerComponent | |||
|                 .map(cl => { | |||
|                     if (cl instanceof Component) { | |||
|                         return cl | |||
|                     } else { | |||
|                         return cl.ensureModifier().toComponent() | |||
|                     } | |||
|                 }) | |||
|                 .forEach(icomp => { | |||
|                     icomp._modifier = new Modifier() | |||
|                         .setStyleRule("flex", "none") | |||
|                         .join(icomp._modifier) | |||
| 
 | |||
|                 }) | |||
|         } | |||
|         return super.childContext(innerComponent); | |||
|     } | |||
| } | |||
| 
 | |||
| /** | |||
|  * A FlexContainerComponent, which organizes the children in a column like manner. | |||
|  */ | |||
| class Column extends FlexContainerComponent { | |||
|     constructor(attr = {}) { | |||
|         super(attr) | |||
|             .addStyleClass("column-component") | |||
|             .modifier( | |||
|                 new Modifier() | |||
|                     .setStyleRule("flex-direction", "column") | |||
|             ); | |||
|     } | |||
| } | |||
| 
 | |||
| /** | |||
|  * A FlexContainerComponent, which organizes the children in a row like manner. | |||
| */ | |||
| class Row extends FlexContainerComponent { | |||
|     constructor(attr = {}) { | |||
|         super(attr) | |||
|             .addStyleClass("row-component") | |||
|             .modifier( | |||
|                 new Modifier() | |||
|                     .fillMaxWidth() | |||
|                     .setStyleRule("flex-direction", "row") | |||
|             ) | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {*} innerComponent  | |||
|      * @returns {Row} | |||
|      */ | |||
|     childContext(innerComponent) { | |||
|         if (innerComponent instanceof Array) { | |||
|             innerComponent | |||
|                 .map(cl => (cl instanceof Component ? cl : cl.ensureModifier().toComponent())) | |||
|                 .forEach((icomp, i) => { | |||
|                     /* sets the width for all elements,  | |||
|                         to avoid overlapping or line break because of lacking width, | |||
|                         a percent is subtracted for every child element  */ | |||
|                     /* To enable "override" a new Modifier is generated and joined  | |||
|                     with the modifier of the component */ | |||
|                     icomp._modifier = new Modifier() | |||
|                         .setStyleRule("float", (i === 0 ? "left" : "right")) | |||
|                         .join(icomp._modifier) | |||
|                 }) | |||
|         } | |||
|         return super.childContext(innerComponent) | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @returns {Row} | |||
|      */ | |||
|     distibuteSpacingEvenly() { | |||
|         this._element.children.forEach(child => { | |||
|             child.style["width"] = (100 / innerComponent.length); | |||
|         }) | |||
|         return this; | |||
|     } | |||
| } | |||
| 
 | |||
| @ -1,15 +0,0 @@ | |||
| /** | |||
|  * This file is part of the jps-like-websites lib | |||
|  * URL: https://git.labos.goip.de/chris/jpc-like-websites
 | |||
|  * COPYRIGHT and LICENCE are owned by its creator Christian Martin  | |||
|  * Copy, altering or distribution without the allowance of the owner are prohibited | |||
|  */ | |||
| 
 | |||
| /** | |||
|  * Enum to access common events | |||
|  */ | |||
| const CommonEvents = Object.freeze({ | |||
|     ONCLICK: "onclick", | |||
|     ONCHANGE: "onchange" | |||
| }) | |||
| 
 | |||
| @ -1,246 +0,0 @@ | |||
| /** | |||
|  * This file is part of the jps-like-websites lib | |||
|  * URL: https://git.labos.goip.de/chris/jpc-like-websites
 | |||
|  * COPYRIGHT and LICENCE are owned by its creator Christian Martin  | |||
|  * Copy, altering or distribution without the allowance of the owner are prohibited | |||
|  */ | |||
| 
 | |||
| 
 | |||
| /** | |||
|  * A chainable HTMLElement builder.  | |||
|  */ | |||
| class Component { | |||
|     _element; | |||
|     _modifier | |||
|     _alignment; | |||
|     _arrangement; | |||
|     _toRegister; | |||
| 
 | |||
|     constructor(element, attr = {}) { | |||
|         this._modifier = new Modifier().margin(new Sides().all(0)); | |||
|         var akeys = Object.keys(attr); | |||
|         for (let i = 0; i < akeys.length; i++) { | |||
|             element.setAttribute(akeys[i], attr[akeys[i]]); | |||
|         } | |||
|         this._element = element; | |||
|         this._toRegister = []; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Sets the alignment (modifications) for this element or more specific for its children.  | |||
|      * @param {Alignment} alignment  | |||
|      * @returns {Component} this component object | |||
|      */ | |||
|     alignment(alignment) { | |||
|         this._alignment = alignment; | |||
| 
 | |||
|         this._modifier._modifications["display"] = "flex"; | |||
|         this._modifier._modifications["align-content"] = alignment; | |||
|         this._modifier._modifications["align-items"] = alignment; | |||
|         this._modifier._modifications["text-align"] = alignment; | |||
|         //this._modifier._modifications["justify-content"] = alignment;
 | |||
| 
 | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Sets the arrangement (modifications) for this element or more specific for its children.  | |||
|      * @param {Arrangement} arrangement  | |||
|      * @returns {Component} this component object | |||
|      */ | |||
|     arrangement(arrangement) { | |||
|         this._arrangement = arrangement; | |||
|         switch (arrangement) { | |||
|             case Arrangement.START: | |||
|                 this._modifier._modifications["justify-content"] = "start"; | |||
|                 break; | |||
|             case Arrangement.END: | |||
|                 this._modifier._modifications["justify-content"] = "end"; | |||
|                 break; | |||
|             case Arrangement.CENTER: | |||
|                 this._modifier._modifications["justify-content"] = "center"; | |||
|                 break; | |||
|             case Arrangement.SPACE_AROUND: | |||
|                 this._modifier._modifications["justify-content"] = "space-around"; | |||
|                 break; | |||
|             case Arrangement.SPACE_BETWEEN: | |||
|                 this._modifier._modifications["justify-content"] = "space-between"; | |||
|                 break; | |||
|             case Arrangement.SPACE_EVENLY: | |||
|                 this._modifier._modifications["justify-content"] = "space-evenly"; | |||
|                 break; | |||
|         } | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {Modifier} modifier  | |||
|      * @returns {Component} this component object | |||
|      */ | |||
|     modifier(modifier) { | |||
|         this._modifier = this._modifier.join(modifier.ensureModifier()) | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Sets the innerText of the element | |||
|      * @param {string} text  | |||
|      * @returns {Component} this component object | |||
|      */ | |||
|     text(text) { | |||
|         this._element.innerText = text; | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     title(text) { | |||
|         this._element.title = text; | |||
|         return this; | |||
|     } | |||
|     /** | |||
|      *  | |||
|      * @param {string} styleClass  | |||
|      * @param {Modifier} modifier  | |||
|      * @returns {Component} this component object | |||
|      */ | |||
|     addStyleClass(styleClass, modifier = null) { | |||
|         if (modifier) { | |||
|             Page.registerStyling(styleClass, modifier._modifications); | |||
|         } | |||
|         this._element.classList.add(styleClass); | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     registerStyleClass(styleClass, styleRuleMap) { | |||
|         Page.registerStyling('.' + styleClass, styleRuleMap); | |||
|         return this.addStyleClass(styleClass); | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {string} key  | |||
|      * @param {string} value  | |||
|      * @returns {Component} this component object | |||
|      */ | |||
|     setAttribute(key, value) { | |||
|         this._element.setAttribute(key, value); | |||
|         return this; | |||
|     } | |||
| 
 | |||
| 
 | |||
|     /** | |||
|      * Ends chain.  | |||
|      * Applies all modifications on the element. | |||
|      * @returns {HTMLElemment} the html element | |||
|      */ | |||
|     generate() { | |||
|         /* apply styling to element */ | |||
|         var mkeys = Object.keys(this._modifier._modifications); | |||
|         for (let i = 0; i < mkeys.length; i++) { | |||
|             this._element.style[mkeys[i]] = this._modifier._modifications[mkeys[i]]; | |||
|         } | |||
|         /* subscribe/register to lists */ | |||
|         for (let i = 0; i < this._toRegister.length; i++) { | |||
|             this._toRegister[i].push(this._element); | |||
|         } | |||
|         return this._element; | |||
|     } | |||
| 
 | |||
| 
 | |||
|     /** | |||
|      * Opens a context to create element children.  | |||
|      * Either as one component or a list/array of components. | |||
|      * @param {Component|Array<Component>} component  | |||
|      * @returns {Component} this component object | |||
|      */ | |||
|     childContext(component) { | |||
|         if (arguments.length > 1) { | |||
|             for (let i = 0; i < arguments.length; i++) { | |||
|                 this.childContext(arguments[i]); | |||
|             } | |||
|         } else if (component instanceof Array) { | |||
|             for (let i = 0; i < component.length; i++) { | |||
|                 this.childContext(component[i]); | |||
|             } | |||
|         } else { | |||
|             this._element.append( | |||
|                 (component instanceof Component | |||
|                     ? component | |||
|                     : component.toComponent() | |||
|                 ) | |||
|                     .generate() | |||
|             ); | |||
|         } | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * @deprecated | |||
|      * @param {Array<Component>} innerComponent  | |||
|      * @returns {Component} this component object | |||
|      */ | |||
|     componentChildren(innerComponent) { | |||
|         for (let i = 0; i < innerComponent.length; i++) { | |||
|             this.childContext(innerComponent[i]); | |||
|         } | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {CommonEvent} commonEvent  | |||
|      * @param {string} functionName  | |||
|      * @returns {Component} this component object | |||
|      */ | |||
|     setEvent(commonEvent, functionName) { | |||
|         return this.setAttribute(commonEvent, `${functionName}(this)`); | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @returns {ChainableModifier}  | |||
|      */ | |||
|     chainModifier() { | |||
|         return new ChainableModifier(this); | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Collects the given List in the _toRegister attribute. | |||
|      * When generate() is called,  | |||
|      * the created Element will be registered (added) in every list  | |||
|      * within the list.  | |||
|      * @param {Array} listName  | |||
|      */ | |||
|     subscribeOnGenerate(listName) { | |||
|         this._toRegister.push(listName); | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /* | |||
|     clickable(eventName) { | |||
|         let cssClass = "button-like" | |||
|         this.addStyleClass(cssClass) | |||
|         this._modifier._modifications["box-shadow"] = "4px 6px #999"; | |||
| 
 | |||
|         let bckk = "background-color"; | |||
|         if (this._modifier._modifications.hasOwnProperty(bckk)) { | |||
|             let bckc = this._modifier._modifications[bckk]; | |||
|             delete this._modifier._modifications[bckk]; | |||
|             Page.registerStyling(`.${cssClass}`, { | |||
|                 [bckk]: bckc | |||
|             }) | |||
|         } | |||
| 
 | |||
|         Page.registerStyling(`.${cssClass}:hover`, { | |||
|             "background-color": Colors.greenyellow.cssRGBString(), | |||
|         }) | |||
|         Page.registerStyling(`.${cssClass}:active`, { | |||
|             "background-color": Colors.greenyellow.cssRGBString(), | |||
|             "box-shadow": "1px 2px #666", | |||
|             "transform": "translateY(4px)" | |||
|         }) | |||
| 
 | |||
|         return this.setEvent(CommonEvents.ONCLICK, eventName); | |||
|     } | |||
|         */ | |||
| } | |||
| @ -0,0 +1,234 @@ | |||
| /** | |||
|  * Represents the most basic and simple form of a Component. | |||
|  * It is mainly a collection of wrapper methods  | |||
|  * around the HTMLElement methods to make them chainable. | |||
|  * It serves as base for further functionallity extensions. | |||
|  * @abstract | |||
|  */ | |||
| class ElementWrapper { | |||
|     /** | |||
|      * The basic HTMLElement the Component is wrapped around. | |||
|      * It will be modified in several ways and in the end returned. | |||
|      * @type {HTMLElement} | |||
|      */ | |||
|     _element; | |||
|     /** | |||
|      * The auto-generated name of the component.  | |||
|      * @type {string} | |||
|      */ | |||
|     _compName; | |||
|     /** | |||
|      * The auto-generated name of the component.  | |||
|      * @type {string} | |||
|      */ | |||
|     _givenName; | |||
|     /** | |||
|      * @type {Component} | |||
|      */ | |||
|     _parentComponent; | |||
| 
 | |||
|     /** | |||
|      * Initializes the component | |||
|      * @param {HTMLElement} element the base element | |||
|      * @param {map<string,string>} attr Specific already known attributes | |||
|      */ | |||
|     constructor(element, attr = {}) { | |||
|         helperFun.fillAttrsInContainerByCb( | |||
|             attr, | |||
|             element, | |||
|             function (k, v, con) { | |||
|                 con.setAttribute(k, v); | |||
|             } | |||
|         ); | |||
| 
 | |||
|         this._compName = Page.autoRegisterComponent(); | |||
|         element.setAttribute('data-autocompel', this._compName); | |||
|         this._element = element; | |||
|         this.addStyleClass(this._compName); | |||
|     } | |||
| 
 | |||
|     setComponentName(name) { | |||
|         this._givenName = name; | |||
|         this.setAttribute('data-compel', name); | |||
|         Page.registerComponent(name); | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * (Wrapper) Sets the innerText of the element | |||
|      * @todo add Alignment of text functionality | |||
|      * @param {string} text  | |||
|      * @returns {Component} this component object | |||
|      */ | |||
|     text(text) { | |||
|         this._element.innerText = text; | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Wrapper to set the HTMLElement.title attribute | |||
|      * @param {string} text  | |||
|      * @returns {Component} | |||
|      */ | |||
|     title(text) { | |||
|         this._element.title = text; | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Wrapper for HTMLElement.classList.add() | |||
|      * @param {string} styleClass  | |||
|      * @returns {Component} this component object | |||
|      */ | |||
|     addStyleClass(styleClass) { | |||
|         this._element.classList.add(styleClass); | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Wrapper for the HTMLElement.setAttribute() method. | |||
|      * @param {string} key  | |||
|      * @param {string} value  | |||
|      * @returns {Component} this component object | |||
|      */ | |||
|     setAttribute(key, value) { | |||
|         this._element.setAttribute(key, value); | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Wrapper for the HTMLElement.addEventListener() | |||
|      * @param {keyof WindowEventMap|CommonEvents} theEvent  | |||
|      * @param {Function} theListener  | |||
|      * @param {boolean|AddEventListenerOptions} options  | |||
|      * @returns {Component} this component object | |||
|      */ | |||
|     addEventListener(theEvent, theListener, options = null) { | |||
|         this._element.addEventListener(theEvent, theListener, options) | |||
|         return this; | |||
|     } | |||
| } | |||
| 
 | |||
| 
 | |||
| /** | |||
|  * @inheritdoc | |||
|  * @extends ElementWrapper | |||
|  * @abstract | |||
|  */ | |||
| class ChildbearerComponent extends ElementWrapper { | |||
|     /** | |||
|      * @type {Array<Component>} children | |||
|      */ | |||
|     _children; | |||
|     /** | |||
|      * @type {Alignment} alignment | |||
|      */ | |||
|     _alignment; | |||
| 
 | |||
|     /** | |||
|      * @type {Arrangement} arrangement | |||
|      */ | |||
|     _arrangement; | |||
| 
 | |||
|     constructor(element, attr = {}) { | |||
|         super(element, attr); | |||
|         this._children = []; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * @todo: Unify logic extract modifications into responsible construct | |||
|      * @todo: Make it work as expected, fix docu | |||
|      * @todo: Differentiate between directions (horizontal, vertiacl) | |||
|      *  | |||
|      * Sets the alignment (modifications) for this element or more specific for its children.  | |||
|      * @param {Alignment} alignment  | |||
|      * @returns {Component} this component object | |||
|      */ | |||
|     alignment(alignment) { | |||
|         /* | |||
|         this._modifier._modifications["display"] = "flex"; | |||
|         this._modifier._modifications["align-content"] = alignment; | |||
|         this._modifier._modifications["align-items"] = alignment; | |||
|         this._modifier._modifications["text-align"] = alignment; | |||
|         */ | |||
|         this._alignment = alignment; | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * @todo: Unify logic extract modifications into responsible construct | |||
|      * @todo: Differentiate between directions (horizontal, vertical) | |||
|      * @todo: Make it work as expected, fix docu | |||
|      *  | |||
|      * Sets the arrangement (modifications) for this element or more specific for its children.  | |||
|      * @param {Arrangement} arrangement  | |||
|      * @returns {Component} this component object | |||
|      */ | |||
|     arrangement(arrangement) { | |||
|         /* | |||
|         this._modifier._modifications["justify-content"] = arrangement; | |||
|         */ | |||
|         this._arrangement = arrangement; | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Opens a context to create children elements.  | |||
|      * Either as one component or a list/array of components. | |||
|      * @param {Component|Array<Component>} component  | |||
|      * @returns {Component} this component object | |||
|      */ | |||
|     childContext(component) { | |||
|         if (!component) return this; | |||
| 
 | |||
|         if (arguments.length > 1) { | |||
|             for (let i = 0; i < arguments.length; i++) { | |||
|                 this.childContext(arguments[i]); | |||
|             } | |||
|         } else if (component instanceof Array) { | |||
|             for (let i = 0; i < component.length; i++) { | |||
|                 this.childContext(component[i]); | |||
|             } | |||
|         } else { | |||
|             if (!(component instanceof Component)) { | |||
|                 this.childContext(component.toComponent()) | |||
|             } else { | |||
|                 this._children.push(component.end()); | |||
|             } | |||
|         } | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Ends chain for the current component. | |||
|      * Returns the builder object. | |||
|      * The Component that is selected there will be set as child to this one.  | |||
|      *  | |||
|      * This funciton is a convenience function.  | |||
|      * Mainly to offer the possibility to reduce the depth of method chains. | |||
|      * Especially in the case of components with only one child.  | |||
|      *  | |||
|      * @returns {builder} the given  | |||
|      */ | |||
|     chainChild() { | |||
|         return builder._nextComponent(this); | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Ends a chainChild - chain.  | |||
|      * If components are setup as chainChild  | |||
|      * they would be wrongfully taken through childContext(). | |||
|      * Therefore thoose chains are recursively resolved through this method.  | |||
|      * @returns {Component} | |||
|      */ | |||
|     end() { | |||
|         let parent = this._parentComponent; | |||
|         if (parent) { | |||
|             this._parentComponent = null; | |||
|             return parent | |||
|                 .childContext(this) | |||
|                 .end(); | |||
|         } | |||
|         return this; | |||
|     } | |||
| } | |||
| @ -0,0 +1,19 @@ | |||
| /** | |||
|  * A FlexContainerComponent, which organizes the children in a column like manner. | |||
|  * @extends FlexContainerComponent | |||
|  * @inheritdoc | |||
|  */ | |||
| class Column extends FlexContainerComponent { | |||
|     /** | |||
|      * | |||
|      * @param {Attr} attr | |||
|      * @param {Modifier} modifier | |||
|      */ | |||
|     constructor(attr = {}, modifier = null) { | |||
|         super(attr, modifier); | |||
|         this.addStyleClass("column-component"); | |||
|         this._flexDirection = "column"; | |||
|         this.setFlexDirection(); | |||
|     } | |||
| 
 | |||
| } | |||
| @ -0,0 +1,346 @@ | |||
| /** | |||
|  * Represents the most basic and simple form of a Component. | |||
|  * It is mainly a collection of wrapper methods  | |||
|  * around the HTMLElement methods to make them chainable. | |||
|  * It serves as base for further functionallity extensions. | |||
|  * @extends StyleAndScriptStoringComponent | |||
|  * @inheritdoc | |||
|  *  | |||
|  */ | |||
| class Component extends StyleAndScriptStoringComponent { | |||
|     /** | |||
|      * @type {boolean} | |||
|      */ | |||
|     _isCompel; | |||
|     /** | |||
|      * @type {Array<any>} | |||
|      */ | |||
|     _toRegister; | |||
|     /** | |||
|      * @type {boolean}  | |||
|      */ | |||
|     _isContextMenu; | |||
| 
 | |||
| 
 | |||
|     /** | |||
|      * Initializes the component | |||
|      * @param {HTMLElement} element the base element | |||
|      * @param {Map<string,string>} attr Specific already known attributes | |||
|      */ | |||
|     constructor(element, attr = {}) { | |||
|         super(element, attr); | |||
|         this._isCompel = false; | |||
|         this._isContextMenu = false; | |||
| 
 | |||
|         this._modifier = new Modifier() | |||
|             .margin(0); | |||
|         this._modifier._modifications['display'] = "flex"; | |||
|         this._modifier._modifications["box-sizing"] = "border-box"; | |||
| 
 | |||
|         this._toRegister = []; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Adds a class to classList via HTMLElement.classList.add() method. | |||
|      * Further collects rules in a property until generate is called. | |||
|      *  | |||
|      * @CAUGHTION implementation is not safe to use, ignoring extStore is recommended; | |||
|      *  | |||
|      * @todo difference between stylings and classes, extStore logic in combination with the Page.register... logic | |||
|      *  | |||
|      * @override | |||
|      *  | |||
|      * @param {string} styleClass (without the '.' in the front) | |||
|      * @param {string|Modifier|map<string,string>} styling | |||
|      * @param {ExtStorage|ExtStoreType|ExtStorePosition|OverwriteBehaviour|EXPosConfer|ESOverwriteConfer} extStore  | |||
|      * if a unique definition is desired, all constants or configurator objects are allowed - they will be processed accordingly  | |||
|      * @returns {Component} this component object | |||
|      */ | |||
|     addStyleClass(styleClass, styling = null, extStore = null) { | |||
|         if (!extStore) { | |||
|             extStore = this._styleClassesExtStore; | |||
|         } else if (extStore.isMissing()) { | |||
|             extStore = extStore.fillBy(this._styleClassesExtStore); | |||
|         } | |||
| 
 | |||
|         if (styling) { | |||
|             if (styling instanceof Modifier) { | |||
|                 styling = styling._modifications; | |||
|             } | |||
| 
 | |||
|             Page.registerStyling('.' + styleClass, styling); | |||
|         } | |||
| 
 | |||
|         this._element.classList.add(styleClass); | |||
| 
 | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {boolean} vertical Defines if the Component should overflow vertically (default: true) | |||
|      * @param {boolean} horizontal Defines if the Component should overflow horizontally (default: false) | |||
|      * @returns {Component} | |||
|      */ | |||
|     overflow(vertical = true, horizontal = false) { | |||
|         if (vertical || horizontal) { | |||
|             this._modifier.join( | |||
|                 new Modifier() | |||
|                     .removeStyleRule("flex") | |||
|                     .setStyleRule("overflow", "hidden auto") | |||
|             ); | |||
|             this.subscribeOnGenerate(CommonCompelGroups.OVERFLOWING); | |||
|         } | |||
| 
 | |||
|         if (vertical) { | |||
|             this._modifier._modifications["overflow-y"] = "hidden auto"; | |||
|         } | |||
|         if (horizontal) { | |||
|             this._modifier._modifications["overflow-x"] = "hidden auto"; | |||
|         } | |||
|         return this; | |||
|     } | |||
| 
 | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {boolean} untilFound | |||
|      * @returns {Component} | |||
|      */ | |||
|     hidden(untilFound = false) { | |||
|         let styleClass = "compel-mech-hidden"; | |||
|         let hid = "hidden"; | |||
|         Page.registerStyling("." + styleClass, { [hid]: hid }); | |||
|         this.addStyleClass(styleClass); | |||
| 
 | |||
|         this._modifier.removeStyleRule("display"); | |||
|         this.setAttribute( | |||
|             hid, | |||
|             (untilFound ? "until-found" : hid) | |||
|         ); | |||
| 
 | |||
|         this.subscribeOnGenerate(CommonCompelGroups.HIDDEN_ON_START); | |||
| 
 | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Since the "hidden" method call was quite often called as being conditional,  | |||
|      * and to resolve that it needed to be wrapped into an apply (and if condition within that) call. | |||
|      * This method is purely convinience as it reduces unnecessary code line.  | |||
|      * @experimental | |||
|      * @convenience | |||
|      * @param {boolean} [condition=false]  | |||
|      * @param {boolean} [untilFound=false] | |||
|      * @returns {Component} | |||
|      */ | |||
|     hiddenByCondition(condition = false, untilFound = false) { | |||
|         if (condition) { | |||
|             return this.hidden(untilFound); | |||
|         } | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Subscribes element under higher_compel group | |||
|      * sets corr. variable true | |||
|      * setAttribute("data-compel-isHCompel", "true") | |||
|      *  | |||
|      * @returns {Component} | |||
|      */ | |||
|     isHigherComponent() { | |||
|         this.subscribeOnGenerate(CommonCompelGroups.HIGHER_COMPEL); | |||
|         this._isCompel = true; | |||
|         this.addStyleClass("compel-higher"); | |||
|         return this.setAttribute("data-compel-isHCompel", "true") | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @returns {string} | |||
|      */ | |||
|     getHigherCompelSelector() { | |||
|         return this._element | |||
|             .closest('[data-compel-isHCompel="true"].compel-higher') | |||
|             .getAttribute("data-autocompel"); | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Collects the given List in the _toRegister attribute. | |||
|      * When generate() is called,  | |||
|      * the created Element will be registered (added) in every list  | |||
|      * within the list.  | |||
|      * @param {*|string|Array<*>} listName  | |||
|      */ | |||
|     subscribeOnGenerate(listName) { | |||
|         this._toRegister.push(listName); | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @returns {Component} | |||
|      */ | |||
|     registerAsContextMenu() { | |||
|         this.subscribeOnGenerate(CommonCompelGroups.IS_CONTEXT_MENU); | |||
|         this._isContextMenu = true; | |||
| 
 | |||
|         return this.addStyleClass('contextmenu') | |||
|             .hidden(); | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * @todo Positioning of the contextmenu element | |||
|      * @todo extract into an extra function(allity) provider | |||
|      *  | |||
|      * @param {Component} component  | |||
|      * @param {Sides|Function<HTMLElement|Event> => Sides} getRefPos  | |||
|      * @param {ExtStorage} [extStore=null]  | |||
|      * @returns {Component} | |||
|      */ | |||
|     contextMenu(component, getRefPos = null, extStore = null) { | |||
|         if (!component._isContextMenu) { | |||
|             component.registerAsContextMenu(); | |||
|         } | |||
|         if (!getRefPos) { | |||
|             getRefPos = function (cmEvent) { | |||
|                 return new Sides() | |||
|                     .left(cmEvent.pageX) | |||
|                     .top(cmEvent.pageY); | |||
|             } | |||
|         } | |||
| 
 | |||
|         this.subscribeOnGenerate(CommonCompelGroups.HAS_CONTEXT_MENU); | |||
| 
 | |||
|         let identifier = component._compName; | |||
| 
 | |||
|         this.addEventListener( | |||
|             "contextmenu", | |||
|             DefaultContextMenu.openContextMenuAction(identifier, getRefPos) | |||
|         ); | |||
| 
 | |||
|         return this.childContext(component); | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {DragAndDropImplementation} dadImpl  | |||
|      * @returns {Component} | |||
|      */ | |||
|     draggable(dadImpl = new DragAndDropImplementation()) { | |||
|         this.subscribeOnGenerate(CommonCompelGroups.DRAGGABLE); | |||
|         this.subscribeOnGenerate(CommonCompelGroups.HAS_DRAG_EVENT); | |||
| 
 | |||
|         let addedClass = "comp-el-mech-draggable"; | |||
|         let selector = this._element.getAttribute("data-autocompel"); | |||
| 
 | |||
|         selector = `.${addedClass}[data-autocompel="${selector}"]`; | |||
| 
 | |||
|         return this.addStyleClass(addedClass) | |||
|             .setAttribute("draggable", "true") | |||
|             .setAttribute("dropEffect", "none") | |||
|             .addEventListener( | |||
|                 CommonEvents.DRAG_START, | |||
|                 dadImpl.dragStartAction("text/plain", selector) | |||
|             ); | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {EventDrag} dragEvent  | |||
|      * @param {Function} action  | |||
|      */ | |||
|     onDrag(dragEvent, action = (e) => { e.preventDefault(); }) { | |||
|         this.subscribeOnGenerate(CommonCompelGroups.HAS_DRAG_EVENT); | |||
|         let selector = `comp-el-mech-drag${dragEvent}`; | |||
| 
 | |||
|         return this.addEventListener( | |||
|             'drag' + dragEvent, | |||
|             action | |||
|         ); | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {DragAndDropImplementation} dadImpl  | |||
|      * @returns {Component} | |||
|      */ | |||
|     dropTarget(dadImpl = new DragAndDropImplementation()) { | |||
|         this.subscribeOnGenerate(CommonCompelGroups.DROP_TARGET); | |||
|         let specialClass = "comp-el-mech-droptarget"; | |||
| 
 | |||
|         this.addStyleClass(specialClass) | |||
|             .onDrag(EventDrag.OVER); | |||
| 
 | |||
|         let selector = `.${specialClass}[data-autocompel="${this._compName}"]` | |||
| 
 | |||
|         this._element.addEventListener( | |||
|             "drop", | |||
|             dadImpl.dropEventAction("text/plain", selector) | |||
|         ); | |||
| 
 | |||
|         return this; | |||
|     } | |||
| 
 | |||
| 
 | |||
|     /** | |||
|      * An echo of  Scope-Functions from kotlin for convenience | |||
|      *  | |||
|      * Executes a given function injects this component into the function. | |||
|      * @param {Function} func  | |||
|      * @returns {Component} | |||
|      */ | |||
|     apply(func) { | |||
|         func(this); | |||
|         return this; | |||
|     } | |||
| 
 | |||
| 
 | |||
|     /** | |||
|      * An echo of  Scope-Functions from kotlin for convenience | |||
|      *  | |||
|      * Executes a given function injects the htmlelement of this component into the function. | |||
|      * @param {Function} func  | |||
|      * @returns {Component} | |||
|      */ | |||
|     applyToEl(func) { | |||
|         func(this._element) | |||
|         return this; | |||
|     } | |||
| 
 | |||
| 
 | |||
|     /** | |||
|      * Ends chain.  | |||
|      * Applies all modifications on the element. | |||
|      * Processes all stored additions. | |||
|      * Returns the constructed HTMLElement of this Component. | |||
|      * | |||
|      * @param {CompelGenerator} generator  | |||
|      * @param {Modifier | undefined} [modifier=null]  | |||
|      * @param {ExtStorage | undefined} [styleStore=null]  | |||
|      * @param {ExtStorage | undefined} [functionStore=null]  | |||
|      * @param {ExtStorage}  | |||
|      * @returns {WebTrinity} the constructed HTMLElement of this Component. | |||
|      */ | |||
|     generate(generator = singlepage, styleStore = null, functionStore = null) { | |||
|         /** | |||
|          * In the case that this component is a chainChild created one. | |||
|          * The generation chain needs to be setup in the proper order | |||
|          * so that the resulting element tree equals the expected/desired result.  | |||
|          *  | |||
|          * Therefore if this is a chainChild,  | |||
|          * it will be added to the parent via the regular childContext | |||
|          * and the generation of the parent will be returned.  | |||
|          *  | |||
|          * The parent will generate this component on its generate(). | |||
|          */ | |||
|         if (this._parentComponent) { | |||
|             let parent = this._parentComponent; | |||
|             this._parentComponent = null; | |||
|             return parent.childContext(this) | |||
|                 .generate(generator, styleStore, functionStore); | |||
|         } | |||
| 
 | |||
|         return generator.generate(this, styleStore, functionStore); | |||
|     } | |||
| } | |||
| @ -0,0 +1,173 @@ | |||
| /** | |||
|  * Represents container Components. | |||
|  * Some predefined modifications are applied on the child components. | |||
|  * @extends Component | |||
|  * @inheritdoc | |||
|  */ | |||
| class FlexContainerComponent extends Component { | |||
|     /** | |||
|      * @type {boolean} | |||
|      */ | |||
|     _distributeEvenglyAfterGenerate; | |||
|     /** | |||
|      * @type {number} the amount that should be left out of the total space before the children | |||
|      *                space is set.  | |||
|      */ | |||
|     #distributionRecess; | |||
|     /** | |||
|      * @type {number} the additional gap that should be left for children before their space is set | |||
|      */ | |||
|     #distributionGapPerChild; | |||
|     /** | |||
|      * @type {string} | |||
|      */ | |||
|     _flexDirection; | |||
|     /** | |||
|      * | |||
|      * @param {Attr} attr | |||
|      * @param {Modifier} modifier | |||
|      */ | |||
|     constructor(attr = {}, modifier = null, baseElement = "div") { | |||
|         super(document.createElement(baseElement), attr); | |||
|         this._flexDirection = ""; | |||
|         this._distributeEvenglyAfterGenerate = false; | |||
|         this.addStyleClass("flex-container-component"); | |||
|         if (modifier) { | |||
|             this.modifier(modifier); | |||
|         } | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * | |||
|      * @param {boolean} [vertical=true] | |||
|      * @returns {FlexContainerComponent} | |||
|      */ | |||
|     setFlexDirection(vertical = true) { | |||
|         return this.modifier( | |||
|             new Modifier() | |||
|                 .setStyleRule( | |||
|                     "flex-direction", | |||
|                     (vertical ? "column" : "row") | |||
|                 ) | |||
|         ); | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * | |||
|      * @param {Component|Array<Component>} innerComponent | |||
|      * @returns {FlexContainerComponent} this component object | |||
|      */ | |||
|     childContext(innerComponent) { | |||
|         if (innerComponent instanceof Array) { | |||
|             innerComponent | |||
|                 .map(cl => { | |||
|                     if (cl instanceof Component) { | |||
|                         return cl; | |||
|                     } else { | |||
|                         return cl.ensureModifier().toComponent(); | |||
|                     } | |||
|                 }) | |||
|                 .forEach(icomp => { | |||
|                     icomp._modifier = new Modifier() | |||
|                         .removeStyleRule("flex") | |||
|                         .join(icomp._modifier); | |||
|                 }); | |||
|         } | |||
|         return super.childContext(innerComponent); | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {number} [recessFraction=0.0] recessFraction a fraction/percentage (<1.0) of the recess  | |||
|      *                 that should be left out before distributing the remaining space. | |||
|      * @param {number} [gapPerChild=1] gapPerChild the gap per child, therefore the recess between children  | |||
|      */ | |||
|     _distributingSpacing(recessFraction = 0.0, gapPerChild = 1) { | |||
|         if (this._children && this._children.length > 1) { | |||
| 
 | |||
|             let distributableSpace = 100 - 100 * recessFraction - (this._children.length - 1) * gapPerChild; | |||
| 
 | |||
|             let childDistributionFraction = Math.floor( | |||
|                 (distributableSpace / this._children.length) * 100 | |||
|             ) / 100; | |||
| 
 | |||
|             let direction = (this._flexDirection === 'column' ? 'height' : "width"); | |||
| 
 | |||
|             for (const icomp of this._children) { | |||
|                 /* sets the width for all elements, | |||
|                 to avoid overlapping or line break because of lacking width, | |||
|                 a percent is subtracted for every child element  */ | |||
|                 /* To enable "override" a new Modifier is generated and joined | |||
|                 with the modifier of the component */ | |||
|                 icomp._modifier._modifications[direction] = childDistributionFraction + "%"; | |||
|                 icomp._modifier._modifications["max-" + direction] = childDistributionFraction + "%"; | |||
|             } | |||
|         } | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Distributes the spacing of the childrens evengly,  | |||
|      * according to the flexdirection of this component. | |||
|      * By default this will be executed imediately when called. | |||
|      * The spacing is distributed by:  | |||
|      *      100 - 100 * recessFraction - (children) * gapPerChild | |||
|      *  | |||
|      * @param {boolean} [rightNow=true] if set to false it will set properties accordingly | |||
|      *                  so that the distribution will be executed on generate | |||
|      * @param {number} [recessFraction=0.0] recessFraction a fraction/percentage (<1.0) of the recess  | |||
|      *                 that should be left out before distributing the remaining space. | |||
|      * @param {number} [gapPerChild=1] gapPerChild the gap per child, therefore the recess between children | |||
|      * @returns {FlexContainerComponent} | |||
|      */ | |||
|     distibuteSpacingEvenly(rightNow = true, recessFraction = 0.0, gapPerChild = 1) { | |||
|         if (rightNow) { | |||
|             this._distributingSpacing(recessFraction, gapPerChild); | |||
|         } else { | |||
|             this.#distributionRecess = recessFraction; | |||
|             this.#distributionGapPerChild = gapPerChild; | |||
|             this._distributeEvenglyAfterGenerate = true; | |||
|         } | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Adds, sets, updates or overwrites the Modification of/for the children of this component. | |||
|      * Just calls .modifier(modifier) on each child.  | |||
|      *  | |||
|      * @param {Modifier} modifier  | |||
|      * @param {boolean|ExtStorage|ExtStoreType|OverwriteBehaviour} [extStore=null]  | |||
|      * @returns {FlexContainerComponent} this component object | |||
|     */ | |||
|     modifyChildren(modifier, underTheName = "", extStore = false) { | |||
|         if (underTheName === "") { | |||
|             underTheName = `.${this._compName}-style-children`; | |||
|         } | |||
|         if (!extStore) { | |||
|             for (const child of this._children) { | |||
|                 child.modifier(modifier) | |||
|             } | |||
|         } else { | |||
|             this.addStyleClass(underTheName); | |||
|             this._styles.push( | |||
|                 new SStoreDefinition( | |||
|                     underTheName, | |||
|                     modifier.ensureModifier() | |||
|                 ) | |||
|             ); | |||
|         } | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * @override | |||
|      * @inheritdoc | |||
|      * @extends Component.generate() | |||
|      */ | |||
|     generate(generator = singlepage, styleStore = null, functionStore = null) { | |||
|         if (this._distributeEvenglyAfterGenerate) { | |||
|             this._distributingSpacing(this.#distributionRecess, this.#distributionGapPerChild); | |||
|         } | |||
| 
 | |||
|         return super.generate(generator, styleStore, functionStore); | |||
|     } | |||
| } | |||
| @ -0,0 +1,69 @@ | |||
| /** | |||
|  * Represents a Component (of an HTMLElement) that is capable of receiving input. | |||
|  * @extends Component | |||
|  * @inheritdoc | |||
|  */ | |||
| class InputComponent extends Component { | |||
|     /** | |||
|      * | |||
|      * @param {string} element | |||
|      * @param {Attr} attr | |||
|      * @param {Modifier} modifier | |||
|      */ | |||
|     constructor(element, attr = {}, modifier = null) { | |||
|         super(element, attr); | |||
|         this.addStyleClass("el-input-comp"); | |||
|         if (modifier) { | |||
|             this.modifier(modifier); | |||
|         } | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * This overrides the text() method in such a way, | |||
|      * that it sets the value attribute for input elements instead. | |||
|      *  | |||
|      * @override | |||
|      * @inheritdoc | |||
|      * @param {string} text  | |||
|      * @returns {InputComponent} | |||
|      */ | |||
|     text(text){ | |||
|         if (this._element instanceof HTMLInputElement) { | |||
|             this._element.value = text; | |||
|         } else { | |||
|             super.text(text); | |||
|         } | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * The parameter makes it optional to trigger the state by a variable | |||
|      * @param {boolean} readonly | |||
|      * @returns {Component} | |||
|      */ | |||
|     readonly(readonly = true) { | |||
|         if (readonly) { | |||
|             this._element.setAttribute("readonly", readonly); | |||
|         } | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Sets the value of the InputComponent | |||
|      * @param {string} Value | |||
|      * @returns {InputComponent|Component} | |||
|      */ | |||
|     value(value) { | |||
|         this._element.value = value; | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * | |||
|      * @param {string} name | |||
|      * @returns {InputComponent|Component} | |||
|      */ | |||
|     name(name) { | |||
|         return this.setAttribute("name", name); | |||
|     } | |||
| } | |||
| @ -0,0 +1,53 @@ | |||
| /** | |||
|  * @inheritdoc | |||
|  * @extends ChildbearerComponent | |||
|  * @abstract | |||
|  */ | |||
| class ModifiableComponent extends ChildbearerComponent { | |||
|     /** | |||
|      * @type {Modifier} modifier | |||
|      */ | |||
|     _modifier; | |||
| 
 | |||
|     constructor(element, attr = {}) { | |||
|         super(element, attr); | |||
|         this._modifier = new Modifier(); | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Sets, updates or overwrites the Modifier-Object for this component | |||
|      * @param {Modifier} modifier  | |||
|      * @param {boolean|ExtStorage|ExtStoreType|OverwriteBehaviour} [extStore=null]  | |||
|      * @returns {Component} this component object | |||
|      */ | |||
|     modifier(modifier, underTheName = "", extStore = false) { | |||
|         if (underTheName === "") { | |||
|             underTheName = `.${this._compName}-style-${this._styles.length}`; | |||
|         }        | |||
| 
 | |||
|         if (!extStore) { | |||
|             this._modifier = this._modifier | |||
|                 .join(modifier.ensureModifier()); | |||
|         } else { | |||
|             this.addStyleClass(underTheName); | |||
|             this._styles.push( | |||
|                 new SStoreDefinition( | |||
|                     underTheName, | |||
|                     modifier.ensureModifier() | |||
|                 ) | |||
|             ); | |||
|         } | |||
| 
 | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Returns a Modifier to chain modifications  | |||
|      * instead of setting them within an sepperate context. | |||
|      * @returns {ChainableModifier}  | |||
|      */ | |||
|     chainModifier() { | |||
|         return new ChainableModifier(this); | |||
|     } | |||
| 
 | |||
| } | |||
| @ -0,0 +1,46 @@ | |||
| /** | |||
|  * A FlexContainerComponent, which organizes the children in a row like manner. | |||
|  *  | |||
|  * @extends FlexContainerComponent | |||
|  * @inheritdoc | |||
|  */ | |||
| class Row extends FlexContainerComponent { | |||
|     /** | |||
|      *  | |||
|      * @param {Attr} attr  | |||
|      * @param {Modifier} modifier  | |||
|      */ | |||
|     constructor(attr = {}, modifier = null) { | |||
|         super(attr, modifier); | |||
|         this.addStyleClass("row-component") | |||
|         this._flexDirection = "row"; | |||
|         //this.modifier(new Modifier().fillMaxWidth());
 | |||
|         this.setFlexDirection(false); | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {Component|Array<Component>} innerComponent  | |||
|      * @returns {Row} | |||
|      */ | |||
|     childContext(innerComponent) { | |||
|         function setFloat(comp, side = "right") { | |||
|             comp._modifier = new Modifier() | |||
|                 .setStyleRule("float", side) | |||
|                 .join(comp._modifier); | |||
|         } | |||
| 
 | |||
|         super.childContext(innerComponent); | |||
| 
 | |||
|         for (const child of this._children) { | |||
|             onSingleOrArray( | |||
|                 child, | |||
|                 (e, i) => setFloat(e, (i === 0 ? "left" : "right")) | |||
|             ); | |||
|         } | |||
| 
 | |||
|         return this; | |||
|     } | |||
| 
 | |||
| } | |||
| 
 | |||
| @ -0,0 +1,194 @@ | |||
| /** | |||
|  * @inheritdoc | |||
|  * @abstract | |||
|  * @extends ModifiableComponent | |||
|  */ | |||
| class StyleAndScriptStoringComponent extends ModifiableComponent { | |||
|     /** | |||
|      * @type {ExtStorage} | |||
|      */ | |||
|     _styleClassesExtStore | |||
|     /** | |||
|      * @type {boolean} | |||
|      */ | |||
|     _isClassESDefault; | |||
|     /** | |||
|      * @type {ExtStorage} | |||
|      */ | |||
|     _stylesExtStore; | |||
|     /** | |||
|      * @type {boolean} | |||
|      */ | |||
|     _isStyleESDefault; | |||
|     /** | |||
|      * @type {Array<SStoreDefinition>} | |||
|      */ | |||
|     _styles; | |||
|     /** | |||
|      * @type {ExtStorage} | |||
|      */ | |||
|     _functionsExtStore; | |||
|     /** | |||
|      * @type {boolean} | |||
|      */ | |||
|     _isFunESDefault; | |||
|     /** | |||
|      * @type {Array<SStoreDefinition>} | |||
|      */ | |||
|     _functions; | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {HTMLElement} element  | |||
|      * @param {Map<string,string>} attr  | |||
|      */ | |||
|     constructor(element, attr = {}) { | |||
|         super(element, attr); | |||
| 
 | |||
|         this._isClassESDefault = true; | |||
|         this._isStyleESDefault = true; | |||
|         this._styles = []; | |||
|         this._isFunESDefault = true; | |||
|         this._functions = []; | |||
| 
 | |||
|         this._styleClassesExtStore = ExtStoreType.CENTRALIZED_DOC_HEAD | |||
|             .setOverwriteBehaviour(OverwriteBehaviour.REPLACE); | |||
|         this._stylesExtStore = ExtStoreType.INTERNALIZED_WITHIN | |||
|             .setOverwriteBehaviour(OverwriteBehaviour.REPLACE); | |||
|         this._functionsExtStore = ExtStoreType.CENTRALIZED_DOC_HEAD | |||
|             .setOverwriteBehaviour(OverwriteBehaviour.REPLACE); | |||
|     } | |||
| 
 | |||
| 
 | |||
|     /** | |||
|      * @todo: Unify logic extract modifications into responsible construct | |||
|      * @todo: Make it work as expected, fix docu | |||
|      * @todo: Differentiate between directions (horizontal, vertiacl) | |||
|      * @override | |||
|      * @inheritdoc | |||
|      * Sets the alignment (modifications) for this element.  | |||
|      * @param {Alignment} alignment  | |||
|      * @returns {Component} this component object | |||
|      */ | |||
|     alignment(alignment) { | |||
|         /* | |||
|         this._modifier._modifications["display"] = "flex"; | |||
|         this._modifier._modifications["align-content"] = alignment; | |||
|         this._modifier._modifications["align-items"] = alignment; | |||
|         this._modifier._modifications["text-align"] = alignment; | |||
|         */ | |||
|         this._alignment = alignment; | |||
|         this._modifier._modifications["align-content"] = this._alignment; | |||
|         this._modifier._modifications["align-items"] = this._alignment; | |||
|         this._modifier._modifications["text-align"] = this._alignment; | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * @todo: Unify logic extract modifications into responsible construct | |||
|      * @todo: Differentiate between directions (horizontal, vertical) | |||
|      * @todo: Make it work as expected, fix docu | |||
|      * @override | |||
|      * @inheritdoc | |||
|      * Sets the arrangement (modifications) for this element or more specific for its children.  | |||
|      * Therefore it defines the space distribution to the children. | |||
|      * @param {Arrangement} arrangement  | |||
|      * @returns {Component} this component object | |||
|      */ | |||
|     arrangement(arrangement) { | |||
|         /* | |||
|         this._modifier._modifications["justify-content"] = arrangement; | |||
|         */ | |||
|         this._arrangement = arrangement; | |||
|         this._modifier._modifications["justify-content"] = this._arrangement; | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Defines/Sets the general "storage-behaviour" for styling of this component. | |||
|      * Further for potential css definitions. | |||
|      * If a styling/modifier/class is passed via the corresponding methods/way,  | |||
|      * an alternative behaviour can be passed as a parameter - which will only be applied on/for that single one. | |||
|      *  | |||
|      * @param {ExtStorage|ExtStoreType|OverwriteBehaviour} extStore  | |||
|      * @returns {Component} | |||
|      */ | |||
|     setStylingsStorage(extStore) { | |||
|         if (extStore) { | |||
|             if (extStore instanceof ExtStorage) { | |||
|                 this._stylesExtStore = extStore; | |||
|             } else if (extStore instanceof ExtStoreType) { | |||
|                 this._stylesExtStore.setExtStoreType(extStore); | |||
|             } else { | |||
|                 this._stylesExtStore.OverwriteBehaviour(extStore); | |||
|             } | |||
|             this._isStyleESDefault = false; | |||
|         } else { | |||
|             console.log("(Style)ExtStore was empty, did nothing"); | |||
|         } | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Defines/Sets the general "storage-behaviour" for functions of this component. | |||
|      * If a function is passed via "registerFunction",  | |||
|      * an alternative behaviour can be passed as a parameter - which will only be applied on/for that single one. | |||
|      *  | |||
|      * @param {ExtStorage|ExtStoreType|OverwriteBehaviour} extStore  | |||
|      * @returns {Component} | |||
|      */ | |||
|     setFunctionsStorage(extStore) { | |||
|         if (extStore) { | |||
|             if (extStore instanceof ExtStorage) { | |||
|                 this._functionsExtStore = extStore; | |||
|             } else if (extStore instanceof ExtStoreType) { | |||
|                 this._functionsExtStore.setExtStoreType(extStore); | |||
|             } else { | |||
|                 this._functionsExtStore.OverwriteBehaviour(extStore); | |||
|             } | |||
|             this._isFunESDefault = false; | |||
|         } else { | |||
|             console.log("(Function)ExtStore was empty, did nothing"); | |||
|         } | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|     *  | |||
|     * @param {Function} func  | |||
|     * @param {string} underTheName  | |||
|     * @param {ExtStorage|ExtStoreType|ExtStorePosition|OverwriteBehaviour|EXPosConfer|ESOverwriteConfer} extStore  | |||
|     * if a unique definition is desired, all constants or configurator objects are allowed - they will be processed accordingly  | |||
|     * @returns  | |||
|     */ | |||
|     registerFunction(func, underTheName = "", extStore = null) { | |||
| 
 | |||
|         let registrationName = [ | |||
|             underTheName.trim(), | |||
|             func.name.trim(), | |||
|             `func${this._compName}${Object.keys(this._functions).length}` | |||
|         ] | |||
|             .find(e => e !== ''); | |||
| 
 | |||
|         /* | |||
|         if (!extStore) { | |||
|             extStore = this._functionsExtStore; | |||
|         } else if (extStore instanceof ExtStoreConfer) { | |||
|             extStore = extStore.ensureStore(); | |||
|         } else { | |||
|             extStore = new ExtStorage().setSingleValueToClone(extStore, this._functionsExtStore); | |||
|         } | |||
|             */ | |||
| 
 | |||
|         let entry = new SStoreDefinition(); | |||
|         entry._identifier = registrationName; | |||
|         entry._definition = func; | |||
|         entry._extStore = extStore; | |||
| 
 | |||
|         this._functions.push(entry); | |||
| 
 | |||
|         //identifyAndResolveOverwrite(this._functions, registrationName, entry, extStore._overwriteBehaviour);
 | |||
| 
 | |||
|         return this; | |||
|     } | |||
| } | |||
| @ -1,177 +0,0 @@ | |||
| /** | |||
|  * This file is part of the jps-like-websites lib | |||
|  * URL: https://git.labos.goip.de/chris/jpc-like-websites
 | |||
|  * COPYRIGHT and LICENCE are owned by its creator Christian Martin  | |||
|  * Copy, altering or distribution without the allowance of the owner are prohibited | |||
|  */ | |||
| 
 | |||
| /** | |||
|  * The class provides overreaching options for building the website. | |||
|  */ | |||
| class PageBuilder { | |||
|     #cssClasses; | |||
|     #functions; | |||
|     #delayedFunctions; | |||
|     #repeatingFunctions; | |||
|     #functionNames; | |||
|     #cssElementIdentifiers; | |||
| 
 | |||
|     constructor() { | |||
|         this.#cssClasses = document.createElement("style"); | |||
|         this.#functions = document.createElement("script"); | |||
|         this.#functionNames = []; | |||
|         this.#cssElementIdentifiers = []; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Registers a function to be added later in a script tag in the head of the document. | |||
|      * @ATTENTION Be careful with intended empty strings (e.g. in variable values),  | |||
|      *              empty strings within the function code will be shrunk. | |||
|      * @param {string} name  | |||
|      * @param {function} fun  | |||
|      */ | |||
|     registerFunction(name, fun) { | |||
|         /** | |||
|          * Is supposed to shrink all empty strings to length 1 | |||
|          * @param {string} text  | |||
|          * @returns {string} | |||
|          */ | |||
|         function shrinkEmptyStrings(text){ | |||
|             for (let i = 1; i < 10; i++) { | |||
|                 text = text.replaceAll("          ".slice(i), ' '); | |||
|             } | |||
|             return text; | |||
|         } | |||
|         if (!this.#functionNames.includes(name)) { | |||
|             let clearedFuncText = shrinkEmptyStrings( | |||
|                 fun.toString() | |||
|                     .replaceAll('\n', ' ') | |||
|                     .replaceAll('\r\n', ' ') | |||
|                     .replaceAll('\n\r', ' ') | |||
|             ); | |||
|             let isFuncWritten = clearedFuncText.startsWith('function');  | |||
|             let funcHasName = fun.name && fun.name.trim() !== ''; | |||
|             if(isFuncWritten){ | |||
|                 let isNameInFuncText = clearedFuncText.startsWith(`function ${name}`); | |||
|                 this.#functions.innerText += (funcHasName && isNameInFuncText | |||
|                     ? clearedFuncText | |||
|                     : clearedFuncText.replace('function ', 'function '+name) | |||
|                 )+'; '; | |||
|             }else{ | |||
|                 this.#functions.innerText += `const ${name} = ${clearedFuncText}; ` | |||
|             } | |||
|             this.#functionNames.push(name); | |||
|         } | |||
|         return this; | |||
|     } | |||
| 
 | |||
| 
 | |||
|     registerNamedFunction(namedFunction) { | |||
|         return this.registerFunction(namedFunction.name, namedFunction) | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * @experimental Attention is adviced, registration mechanism doesn't work yet | |||
|      * @param {string} name The name the interval will be tied to | |||
|      * @param {Function} fun the function that is supposed to be executed repeatedly | |||
|      * @param {number} interval the time in ms between executions | |||
|      */ | |||
|     registerRepeatingFunction(name, fun, interval) { | |||
|         if (!Object.keys(this.#repeatingFunctions).includes(name)) { | |||
|             this.#repeatingFunctions[name] = { | |||
|                 name: name, | |||
|                 fun: fun, | |||
|                 interval: interval | |||
|             }; | |||
|         } | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Adds the styling rules to the element identifiers into the style tag. | |||
|      * An elementDefinition can only be used once, repeated use will be ignored. | |||
|      * @param {string} elementDefinition The element identifier | |||
|      * @param {Map<string, string>} styleRuleMap The Styling rules/values | |||
|      */ | |||
|     registerStyling(elementDefinition, styleRuleMap) { | |||
|         if (!this.#cssElementIdentifiers.includes(elementDefinition)) { | |||
|             this.#cssClasses.innerText += `${elementDefinition | |||
|                 } {${Object.keys(styleRuleMap) | |||
|                     .map(e => e + ": " + styleRuleMap[e] + "; ") | |||
|                     .join(" ") | |||
|                 }} ` | |||
|             this.#cssElementIdentifiers.push(elementDefinition) | |||
|         } | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Adds into the (head) document.  | |||
|      * - script tag | |||
|      * - function tag | |||
|      * - sets and registers repeatedly executed functions | |||
|      * - sets (timeout and) functions that are supposed to be executed after load | |||
|      */ | |||
|     generate() { | |||
|         let head = document.querySelector("head"); | |||
|         head.appendChild(this.#functions) | |||
|         head.appendChild(this.#cssClasses) | |||
| 
 | |||
|         /* set repeating functions  */ | |||
|         if (this.#repeatingFunctions) { | |||
|             let repeatedFun = Object.values(this.#repeatingFunctions) | |||
|                 .reduce((a, c, i, arr) => Object.assign(a, { | |||
|                     [c.name]: setInterval(c.fun, c.interval) | |||
|                 }), {}); | |||
|         } | |||
| 
 | |||
|         /* set timeouts for funcitons executed after load */ | |||
|         if (this.#delayedFunctions) { | |||
|             for (let i = 0; i < this.#delayedFunctions.length; i++) { | |||
|                 let func = this.#delayedFunctions[i]; | |||
|                 if (func.repeat) { | |||
|                     setTimeout(setInterval(func.func, func.interval), func.dl, func.args); | |||
|                 } else { | |||
|                     setTimeout(func.func, func.dl, func.args); | |||
|                 } | |||
|             } | |||
|         } | |||
|         console.log(this.#functionNames); | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Registeres a function to be executed after page-load | |||
|      * @param {Function} func the function that will be executed | |||
|      * @param {number} delay the time in ms the execution is delayed after load | |||
|      * @param {string} name if provided the function will be registered as well | |||
|      * @param {Array<any>} args arguments for the function | |||
|      * @param {boolean} repeat defines if the function is supposed to be repeated as well | |||
|      * @param {number} interval if the function is supposed to repeat, this defines the interval of repetition | |||
|      */ | |||
|     executeAfterLoad(func, delay = 1000, name = '', args = [], repeat = false, interval = 5000) { | |||
|         if (name !== '') { | |||
|             this.registerFunction(name, func); | |||
|         } | |||
|         if (!this.#delayedFunctions) { | |||
|             this.#delayedFunctions = []; | |||
|         } | |||
|         this.#delayedFunctions.push({ dl: delay, func: func, args: args, repeat: repeat, interval: interval }); | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {number} relaunchSeconds timeinterval for page to reload (changes) | |||
|      */ | |||
|     inDev(relaunchSeconds = 20) { | |||
|         let head = document.querySelector("head"); | |||
|         let meta = document.createElement("meta"); | |||
|         meta.setAttribute("http-equiv", "refresh"); | |||
|         meta.setAttribute("content", `${relaunchSeconds}`); | |||
| 
 | |||
|         head.insertAdjacentElement("beforeend", meta); | |||
|         this.#functions.innerText = ` | |||
|             let ts = new Date(); | |||
|             console.log("Refreshed at: ", ts.getHours()+':'+ts.getMinutes()+':'+ts.getSeconds(), "Intervall ${relaunchSeconds}s"); | |||
|             `;
 | |||
|     } | |||
| } | |||
| 
 | |||
| const Page = new PageBuilder(); | |||
| @ -0,0 +1,196 @@ | |||
| /** | |||
|  * @todo Potentially class implementation could be removed entirely. | |||
|  *       Since it (at the time being) is only working as a singleton anyway.  | |||
|  *       Further maybe rename to 'AppBuilder' instead of 'PageBuilder',  | |||
|  *       page might later become part of a navigation setup.  | |||
|  *  | |||
|  *  | |||
|  * The class provides overreaching options for building the website. | |||
|  * @extends ScriptAndStyleContext | |||
| */ | |||
| class PageBuilder extends ScriptAndStyleContext { | |||
|     /** | |||
|      * @type {Array<string>} | |||
|      */ | |||
|     #autoRegisteredComponents; | |||
|     /** | |||
|      * @type {Array<string>} | |||
|      */ | |||
|     #registeredComponents; | |||
|     /** | |||
|      * @type {boolean} | |||
|      */ | |||
|     #showFrameworkConsole; | |||
|     /** | |||
|      * @type {Array<CompelExtension>} | |||
|      */ | |||
|     _extensions; | |||
|     /** | |||
|      * @type {Map<string,*>} | |||
|      */ | |||
|     _groups; | |||
| 
 | |||
|     constructor() { | |||
|         super(); | |||
|         this.#showFrameworkConsole = false; | |||
| 
 | |||
|         this.#autoRegisteredComponents = []; | |||
|         this.#registeredComponents = []; | |||
|         this._extensions = []; | |||
| 
 | |||
|         this._groups = new Map(); | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {*|Array<*>} groups  | |||
|      * @param {Component} component  | |||
|      */ | |||
|     subscribeComponentToGroup(groups, component) { | |||
|         if (groups instanceof Array && !(groups instanceof String)) { | |||
|             for (let i = 0; i < groups.length; i++) { | |||
|                 this.subscribeComponentToGroup(groups[i], component); | |||
|             } | |||
|         } else { | |||
|             if (!this._groups.has(groups)) { | |||
|                 this._groups.set(groups, []); | |||
|             } | |||
|             this._groups.get(groups).push(component); | |||
|         } | |||
|     } | |||
| 
 | |||
|     autoRegisterComponent() { | |||
|         let compName = 'comp-el-' + this.#autoRegisteredComponents.length; | |||
|         this.#autoRegisteredComponents.push(compName); | |||
|         return compName; | |||
|     } | |||
| 
 | |||
|     registerComponent(compName) { | |||
|         this.#registeredComponents.push(compName); | |||
|         return compName; | |||
|     } | |||
| 
 | |||
| 
 | |||
|     /** | |||
|      * Inserts the given element according to the extStore into the page/document. | |||
|      * The refElement is a reference element for the case  | |||
|      * that extStore._position defines "before" or "segment_begin",  | |||
|      * which will then look for the refElement as the corresponding insert reference. | |||
|      *  | |||
|      * @param {HTMLElement|Component} element  | |||
|      * @param {ExtStorage} extStore  | |||
|      * @param {HTMLElement|Component} refElement  | |||
|      */ | |||
|     addElementToPage(element, extStore = ExtStoreType.CENTRALIZED_DOC_HEAD) { | |||
|         let { insertCallEl, relativePositioning } = {}; | |||
| 
 | |||
|         relativePositioning = extStore.getRelativePositioning(); | |||
|         insertCallEl = extStore.getRefElement(element); | |||
| 
 | |||
|         insertCallEl.insertAdjacentElement( | |||
|             relativePositioning, | |||
|             element | |||
|         ); | |||
| 
 | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Determines that the jpc-like-websites libs shouldn't be part of the resulting page. | |||
|      * Therefore the generate() methods will package/generate finalized js, css and html elements | |||
|      * into the final html page in the end.  | |||
|      * Especially tricky are reusable elements and functions that use such.  | |||
|      *  | |||
|      * @todo This method/feature will have to have a logic implemented for state altering components | |||
|      *          that then can be "packaged" into a single page. | |||
|      * @todo This method/feature will work only, if an automatic reuse logic for elements/components is implemented within the jpc lib. | |||
|      * @ATTENTION DO NOT USE | |||
|      */ | |||
|     enableFrameworkConsole() { | |||
|         this.#showFrameworkConsole = true; | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Little helper function. | |||
|      * If a single page application is in development. | |||
|      * This method sets an autoreload interval for the page. | |||
|      * Default is 20 (sec). | |||
|      * @param {number} relaunchSeconds timeinterval for page to reload (changes) | |||
|      */ | |||
|     inDev(relaunchSeconds = 20) { | |||
|         let head = document.querySelector("head"); | |||
|         let meta = document.createElement("meta"); | |||
|         meta.setAttribute("http-equiv", "refresh"); | |||
|         meta.setAttribute("content", `${relaunchSeconds}`); | |||
| 
 | |||
|         let devScript = document.createElement('script'); | |||
|         devScript.setAttribute("data-label", "devScript"); | |||
|         devScript.innerText = ` | |||
|         let ts = new Date(); | |||
|         console.log("Page is in Dev-Mode (through 'inDev()' call of PageBuilder."); | |||
|         console.log("Refreshed at: ", ts.getHours()+':'+ts.getMinutes()+':'+ts.getSeconds(), "Intervall ${relaunchSeconds}s"); | |||
|         `;
 | |||
|         head.appendChild(devScript); | |||
|         head.insertAdjacentElement("beforeend", meta); | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {CompelExtension} extension  | |||
|      */ | |||
|     addExtension(extension) { | |||
|         if (extension instanceof CompelExtension) { | |||
|             this._extensions.push(extension); | |||
|             extension.install(); | |||
|         } | |||
|     } | |||
| 
 | |||
|     setPageTitle(title) { | |||
|         this._apptitle = title; | |||
|     } | |||
| 
 | |||
| 
 | |||
| 
 | |||
|     generate() { | |||
|         super.generate(); | |||
| 
 | |||
|         let docBody = document.querySelector('body'); | |||
| 
 | |||
|         if (this._apptitle) { | |||
|             document.querySelector("title") | |||
|                 .innerText = this._apptitle; | |||
|         } | |||
| 
 | |||
|         if (this.#showFrameworkConsole) { | |||
|             let pageContextControlPanel = frameworkControlPanel(this._extensions); | |||
|             pageContextControlPanel = pageContextControlPanel.generate(); | |||
| 
 | |||
|             docBody.insertAdjacentElement( | |||
|                 "afterbegin", | |||
|                 pageContextControlPanel.html | |||
|             ); | |||
|         } | |||
| 
 | |||
|         compelgroups = this._groups; | |||
|     } | |||
| 
 | |||
| } | |||
| 
 | |||
| const CommonCompelGroups = Object.freeze({ | |||
|     AUTO_REGISTRATED: "auto_registrated", | |||
|     REUSABLE_COMPEL: "reusable", | |||
|     HIGHER_COMPEL: "higher_compel", | |||
| 
 | |||
|     OVERFLOWING: "overflowing", | |||
|     HIDDEN_ON_START: "hidden_on_start", | |||
| 
 | |||
|     IS_CONTEXT_MENU: "is_contextmenu", | |||
|     HAS_CONTEXT_MENU: "has_contextmenu", | |||
| 
 | |||
|     DRAGGABLE: "draggable", | |||
|     HAS_DRAG_EVENT: "has_drag", | |||
|     DROP_TARGET: "droptarget", | |||
| 
 | |||
| }); | |||
| 
 | |||
| const Page = new PageBuilder(); | |||
| @ -0,0 +1,61 @@ | |||
| /** | |||
|  *  | |||
|  * @param {Array<CompelExtension>} extensions  | |||
|  * @returns {Component} | |||
|  */ | |||
| function frameworkControlPanel( | |||
|     extensions = [] | |||
| ) { | |||
|     return builder.row() | |||
|         .alignment(Alignment.CENTER) | |||
|         .arrangement(Arrangement.CENTER) | |||
|         .isHigherComponent() | |||
|         .setStylingsStorage(ExtStoreType.INTERNALIZED_WITHIN) | |||
|         .modifier( | |||
|             new Modifier() | |||
|                 .fillMaxWidth() | |||
|                 .background(MaterialFiveHundredlColors.ORANGE) | |||
|                 .padding(4) | |||
|                 .border( | |||
|                     new Border(3) | |||
|                     .color(Colors.goldenrod_3) | |||
|                 ) | |||
|                 .dimensions().height(200) | |||
|         ) | |||
|         .childContext([ | |||
|             builder.column() | |||
|                 .modifier(new Modifier().fillMaxHeight()) | |||
|                 .childContext([ | |||
|                     builder.label().text("Installed Extensions:") | |||
|                     , | |||
|                     builder.column() | |||
|                         .overflow() | |||
|                         .modifier( | |||
|                             new Modifier() | |||
|                                 .padding(4) | |||
|                                 .border(1) | |||
|                         ) | |||
|                         .childContext( | |||
|                             extensions.map( | |||
|                                 /** | |||
|                                  *  | |||
|                                  * @param {CompelExtension} ext  | |||
|                                  * @returns {Component} | |||
|                                  */ | |||
|                                 ext => builder.span().text(ext.diplayTitle) | |||
|                             ) | |||
|                         ) | |||
|                 ]) | |||
|             , | |||
|             builder.div() | |||
|                 .alignment(Alignment.CENTER) | |||
|                 .arrangement(Arrangement.CENTER) | |||
|                 .childContext([ | |||
|                     builder.label() | |||
|                         .text("to generate and download page displayed below click on 'generate'") | |||
|                     , | |||
|                     builder.button() | |||
|                         .text("generate") | |||
|                 ]) | |||
|         ]); | |||
| } | |||
| @ -0,0 +1,197 @@ | |||
| /** | |||
|  * @abstract | |||
|  * Class adds function and style storing properties to the context (PageBuilder). | |||
|  */ | |||
| class ScriptAndStyleContext { | |||
|     /** | |||
|      * @property {Map<string, Map<string, string>>} #css | |||
|      * @type {Map<string, Map<string, string>>} #css | |||
|      */ | |||
|     #css; | |||
|     /** | |||
|      * @property {Map<string, FunctionStoreBuffer>} #functions | |||
|      * @type {Map<string, FunctionStoreBuffer>} #functions | |||
|      */ | |||
|     #functions; | |||
| 
 | |||
|     constructor() { | |||
|         this.#functions = new Map(); | |||
|         this.#css = new Map(); | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {string} nameAddition text that will be added to the end of the generated name | |||
|      * @returns a name for a function (e.g. for storing) | |||
|      */ | |||
|     getFunctionName(nameAddition = "") { | |||
|         return `func${this.#functions.size}${nameAddition}`; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Registers a function to be added later in a script tag in the head of the document. | |||
|      * @ATTENTION Be careful with intended empty strings (e.g. in variable values),  | |||
|      *              empty strings within the function code will be shrunk. | |||
|      * | |||
|      * @param {Function} fun The function that will be registered | |||
|      * @param {string} underTheName (alternative) name for the registration of the function,  | |||
|      *        if none the name of the function will be used, if that is missing a name will be generated. | |||
|      * @param {OverwriteBehaviour} overwriteBehaviour defines what to do,  | |||
|      *        if the registration name already exists (default: OverwriteBehaviour.RENAME - adds a nr to the name) | |||
|      * @returns {string} the name under witch the function is registered (and therefore can be called from)  | |||
|      */ | |||
|     registerPageFunction(fun, underTheName = '', overwriteBehaviour = OverwriteBehaviour.RENAME) { | |||
| 
 | |||
|         /* Find name-root */ | |||
|         let registrationName = [ | |||
|             underTheName.trim(), | |||
|             fun.name.trim(), | |||
|             this.getFunctionName() | |||
|         ].find(e => e !== ''); | |||
| 
 | |||
|         /* deal with name already present */ | |||
|         if (this.#functions.has(registrationName)) { | |||
|             registrationName = resolveOverwrite(registrationName, this.#functions, overwriteBehaviour); | |||
|         } | |||
| 
 | |||
|         /* clear function text */ | |||
|         let clearedFuncText = clearFunctionDeclarationText(fun); | |||
| 
 | |||
|         this.#functions.set(registrationName, new FunctionStoreBuffer(clearedFuncText)); | |||
| 
 | |||
|         return registrationName; | |||
|     } | |||
| 
 | |||
| 
 | |||
|     /** | |||
|      *  | |||
|       * @experimental Attention is adviced, registration mechanism doesn't work yet | |||
|       * @param {Function} fun The function that is supposed to be executed repeatedly  | |||
|       * @param {number} interval the time in ms between executions | |||
|       * @param {string} underTheName the name the interval will be tied to | |||
|       * @param {OverwriteBehaviour} overwriteBehaviour defines what to do, | |||
|       *        if the registration name already exists (default: OverwriteBehaviour.RENAME - adds a nr to the name) | |||
|       */ | |||
|     registerRepeatingFunction(fun, interval, underTheName = '', overwriteBehaviour = OverwriteBehaviour.RENAME, args = []) { | |||
|         let registrationName = this.registerPageFunction(fun, underTheName, overwriteBehaviour); | |||
|         let fsb = this.#functions.get(registrationName); | |||
|         fsb.repeats = true; | |||
|         fsb.interval = interval; | |||
|         fsb.args = args; | |||
|         return registrationName; | |||
|     } | |||
| 
 | |||
| 
 | |||
|     /** | |||
|      * Registeres a function to be executed after page-load | |||
|      * @param {Function} func the function that will be executed | |||
|      * @param {number} delay the time in ms the execution is delayed after load | |||
|      * @param {string} name if provided the function will be registered as well | |||
|      * @param {Array<any>} args arguments for the function | |||
|      * @param {boolean} repeat defines if the function is supposed to be repeated as well | |||
|      * @param {number} interval if the function is supposed to repeat, this defines the interval of repetition | |||
|      */ | |||
|     executeAfterLoad(fun, delay = 1000, underTheName = '', overwriteBehaviour = OverwriteBehaviour.RENAME, interval = -1, args = []) { | |||
|         let registrationName = this.registerPageFunction(fun, underTheName, overwriteBehaviour); | |||
|         let fsb = this.#functions.get(registrationName); | |||
| 
 | |||
|         fsb.execAfterStart = true; | |||
|         fsb.delay = delay; | |||
|         fsb.args = args; | |||
| 
 | |||
|         if (interval > 0) { | |||
|             fsb.repeats = true; | |||
|             fsb.interval = interval; | |||
|         } | |||
| 
 | |||
|         return registrationName; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {string} nameAddition text that will be added to the end of the generated name | |||
|      * @returns a name for a styling (e.g. for storing) | |||
|      */ | |||
|     getStyleName(nameAddition = "") { | |||
|         return `styling${this.#css.size}${nameAddition}`; | |||
|     } | |||
| 
 | |||
| 
 | |||
|     /** | |||
|      * Adds the styling rules to the element identifiers into the style tag. | |||
|      * An element definition can only be used once, repeated use will be ignored. | |||
|      *  | |||
|      * @todo implement extStore logic | |||
|      *  | |||
|      * @param {string} elementIdentifier The element identifier | |||
|      * @param {map<string, string>|Modifier} styleRuleMap The Styling rules/values | |||
|      */ | |||
|     registerStyling(elementIdentifier, styleRuleMap) { | |||
|         if (styleRuleMap instanceof Modifier) { | |||
|             styleRuleMap = styleRuleMap._modifications; | |||
|         } | |||
| 
 | |||
|         if (!this.#css.has(elementIdentifier)) { | |||
|             this.#css.set(elementIdentifier, styleRuleMap); | |||
|         } | |||
|         return elementIdentifier; | |||
|     } | |||
| 
 | |||
|     /** | |||
|       * Adds into the (head) document.  | |||
|       * - script tag | |||
|       * - function tag(s) | |||
|       * - sets and registers repeatedly executed functions | |||
|       * - sets (timeout and) functions that are supposed to be executed after load | |||
|       * @class ScriptAndStyleContext | |||
|       */ | |||
|     generate() { | |||
|         let head = document.querySelector('head'); | |||
| 
 | |||
|         /* generate style tag and fill it with stored stylings */ | |||
|         let styleTag = document.createElement('style'); | |||
| 
 | |||
|         for (const tuple of this.#css.entries()) { | |||
|             styleTag.innerText += `${tuple[0]} {${Object.entries(tuple[1]) | |||
|                 .map(style => style[0] + ": " + style[1] + "; ") | |||
|                 .join(" ") | |||
|                 }} `;
 | |||
|         } | |||
| 
 | |||
|         head.appendChild(styleTag); | |||
| 
 | |||
|         /*  | |||
|         generate script tag(s) and fill it with stored functions | |||
|         for now there will be 3 script tags, so interval, execAfterStart and normal functions are sepperated. | |||
|          */ | |||
| 
 | |||
|         let containersTag = document.createElement('script'); | |||
|         containersTag.setAttribute("data-compel-mech-script", "main"); | |||
|         containersTag.innerText = 'const delayed = {}; '; | |||
|         containersTag.innerText += 'const repeated = {}; '; | |||
|         head.appendChild(containersTag); | |||
| 
 | |||
|         if (this.#functions.size > 0) { | |||
|             let funcTag = document.createElement('script'); | |||
|             for (const tuple of this.#functions.entries()) { | |||
|                 let regName = tuple[0]; | |||
|                 let fsb = tuple[1]; | |||
|                 funcTag.innerText += getScriptTagInjectionText(fsb.func, regName); | |||
| 
 | |||
|                 if (fsb.repeats && !fsb.execAfterStart) { | |||
|                     repeated[regName] = setInterval(regName, fsb.interval, fsb.args); | |||
|                 } | |||
| 
 | |||
|                 if (!fsb.repeats && fsb.execAfterStart) { | |||
|                     delayed[regName] = setTimeout(regName, fsb.interval, fsb.args); | |||
|                 } | |||
| 
 | |||
|                 if (fsb.repeats && fsb.execAfterStart) { | |||
|                     repeated[regName] = setInterval(regName, fsb.interval, fsb.args); | |||
|                     delayed[regName] = setTimeout(repeated[regName], fsb.delay, fsb.args); | |||
|                 } | |||
|             } | |||
|             head.appendChild(funcTag); | |||
|         } | |||
|     } | |||
| } | |||
| @ -1,10 +1,3 @@ | |||
| /** | |||
|  * This file is part of the jps-like-websites lib | |||
|  * URL: https://git.labos.goip.de/chris/jpc-like-websites
 | |||
|  * COPYRIGHT and LICENCE are owned by its creator Christian Martin  | |||
|  * Copy, altering or distribution without the allowance of the owner are prohibited | |||
|  */ | |||
| 
 | |||
| /** | |||
|  * Enum providing common alignment rules | |||
|  */ | |||
| @ -0,0 +1,11 @@ | |||
| /** | |||
|  * Enum providing common alignment rules | |||
|  */ | |||
| const Arrangement = Object.freeze({ | |||
|     START: "start", | |||
|     END: "end", | |||
|     CENTER: "center", | |||
|     SPACE_BETWEEN: "space-between", | |||
|     SPACE_EVENLY: "space-evenly", | |||
|     SPACE_AROUND: "space-around", | |||
| }); | |||
| @ -0,0 +1,53 @@ | |||
| /** | |||
|  * @inheritdoc | |||
|  * @extends Sides | |||
|  */ | |||
| class Margin extends Sides { | |||
|     /** | |||
|      *  | |||
|      * @param {number|string} defaultValue  | |||
|      * @param {SizeUnits} defaultUnit  | |||
|      */ | |||
|     constructor(defaultValue = 0, defaultUnit = SizeUnits.PIXEL) { | |||
|         super("margin", defaultValue, defaultUnit); | |||
|     } | |||
| } | |||
| 
 | |||
| 
 | |||
| /** | |||
|  * @ATTENTION since it just increases complexity to constantly use padding and margin | |||
|  *            it is recommended to use padding and to stick to that as often as possible.  | |||
|  *            Padding values take affect inside/within the element. | |||
|  * @inheritdoc | |||
|  * @extends Margin | |||
|  * @mixes  | |||
|  */ | |||
| class MarginChain extends mixinModSubChainEndings(Margin) { | |||
|     /** | |||
|      *  | |||
|      * @param {Modifier} modifier  | |||
|      */ | |||
|     constructor(modifier) { | |||
|         super(modifier); | |||
|     } | |||
| } | |||
| 
 | |||
| 
 | |||
| /** | |||
|  *  | |||
|  * @ATTENTION since it just increases complexity to constantly use padding and margin | |||
|  *            it is recommended to use padding and to stick to that as often as possible.  | |||
|  *            Padding values take affect inside/within the element. | |||
|  * @inheritdoc | |||
|  * @extends MarginChain | |||
|  * @mixin ModificationDefinition | |||
|  */ | |||
| class MarginChainedModifier extends mixinModSubChainComponentMethods(Margin) { | |||
|     /** | |||
|      *  | |||
|      * @param {ChainableModifier} modifier  | |||
|      */ | |||
|     constructor(modifier) { | |||
|         super(modifier); | |||
|     } | |||
| } | |||
| @ -0,0 +1,44 @@ | |||
| /** | |||
|  * @inheritdoc | |||
|  * @extends Sides | |||
|  */ | |||
| class Padding extends Sides { | |||
|     /** | |||
|      *  | |||
|      * @param {number|string} defaultValue  | |||
|      * @param {SizeUnits} defaultUnit  | |||
|      */ | |||
|     constructor(defaultValue = 0, defaultUnit = SizeUnits.PIXEL) { | |||
|         super("padding", defaultValue, defaultUnit); | |||
|     } | |||
| } | |||
| 
 | |||
| 
 | |||
| /** | |||
|  * @inheritdoc | |||
|  * @extends Padding | |||
| */ | |||
| class PaddingChain extends mixinModSubChainEndings(Padding){ | |||
|     /** | |||
|      *  | |||
|      * @param {Modifier} modifier  | |||
|      */ | |||
|     constructor(modifier) { | |||
|         super(modifier); | |||
|     } | |||
| } | |||
| 
 | |||
| 
 | |||
| /** | |||
|  * @inheritdoc | |||
|  * @extends PaddingChain | |||
|  */ | |||
| class PaddingChainedModifier extends mixinModSubChainComponentMethods(Padding){ | |||
|     /** | |||
|      *  | |||
|      * @param {Modifier} modifier  | |||
|      */ | |||
|     constructor(modifier) { | |||
|         super(modifier); | |||
|     } | |||
| } | |||
| @ -0,0 +1 @@ | |||
| const singlepage = new CompelGenerator(); | |||
| @ -0,0 +1,313 @@ | |||
| /** | |||
|  * This class defines the Component generator.  | |||
|  * It externalizes all decision making about script or style storage from the component.  | |||
|  * The component stores the generator (if set, if not =default or passed down from parent component).  | |||
|  * The CompelGenerator class enables the setup/definition of storage, generation and distribution.  | |||
|  *  | |||
|  * Further if other frameworks are targeted:  | |||
|  *      if the components should be generated in a manner  | |||
|  *      that they fullfill the setup/look of other framework-components. | |||
|  * Only the generator has to be modified, implemented, extended ...  | |||
|  * not the component, modifier or any other of the classes.  | |||
|  *  | |||
|  * Therefore the usages of CompelGenerator-feature-logic resets all style and script storages to local.  | |||
|  * Only towards the end (when "generate()" is called) any of that will be resolved.  | |||
|  */ | |||
| class CompelGenerator { | |||
|     /** | |||
|      * @param {ExtStorage} styleStore default ExtStoreType.INTERNALIZED_WITHIN | |||
|      * @param {ExtStorage} functionStore default ExtStoreType.CENTRALIZED_DOC_HEAD | |||
|      */ | |||
|     constructor( | |||
|         styleStore = ExtStoreType.INTERNALIZED_WITHIN | |||
|             .setOverwriteBehaviour(OverwriteBehaviour.REPLACE), | |||
|         functionStore = ExtStoreType.CENTRALIZED_DOC_HEAD | |||
|             .setOverwriteBehaviour(OverwriteBehaviour.REPLACE) | |||
|     ) { | |||
|         this._styleStore = styleStore; | |||
|         this._functionStore = functionStore; | |||
|     } | |||
| 
 | |||
| 
 | |||
|     /** | |||
|      * Deals with the direct component stylings  | |||
|      * @param {Component} component  | |||
|      * @param {Array<WebTrinity>} childrenWenity  | |||
|      * @param {ExtStorage} extStore  | |||
|      * @returns {Array<SStoreDefinition>} | |||
|      */ | |||
|     processStyles(component, extStore = null) { | |||
|         extStore = (extStore | |||
|             ? extStore | |||
|             : component._stylesExtStore | |||
|         ) | |||
|             .setupForGeneralStyling(); | |||
| 
 | |||
|         let forCollection = []; | |||
| 
 | |||
|         let counter = 0; | |||
|         for (const ssd of component._styles) { | |||
|             /* Make sure that the type is unified for later processing */ | |||
|             if (ssd._definition instanceof Modifier) { | |||
|                 ssd._definition = ssd._definition._modifications; | |||
|             } | |||
| 
 | |||
|             /* Check/Ensure proper ExtStorageType for following comparison */ | |||
|             /** | |||
|              * @type {ExtStorage} | |||
|              */ | |||
|             let curExtStore = extStore; | |||
| 
 | |||
|             if (ssd.hasOwnProperty("_extStore") && ssd._extStore) { | |||
|                 curExtStore = ssd._extStore.setupForGeneralStyling(); | |||
|             } | |||
| 
 | |||
|             if (curExtStore.getStylingDistribution()(ssd, component._element, counter)) { | |||
|                 forCollection.push(ssd); | |||
|             } | |||
|         } | |||
| 
 | |||
|         return forCollection; | |||
|     } | |||
| 
 | |||
| 
 | |||
|     /** | |||
|      * First deals with the scripts/functions. | |||
|      *  | |||
|      * @param {Component} component  | |||
|      * @param {Array<WebTrinity>} childrenWenity  | |||
|      * @param {ExtStorage} extStore  | |||
|      * @returns {Array<SStoreDefinition>} | |||
|      */ | |||
|     processFunctions(component, extStore = null) { | |||
|         extStore = (extStore | |||
|             ? extStore | |||
|             : component._functionsExtStore | |||
|         ) | |||
|             .setupForFunctions(); | |||
| 
 | |||
|         const forCollection = new Map(); | |||
|         const collectForBefore = []; | |||
| 
 | |||
|         let counter = 0; | |||
|         for (const ssd of component._functions) { | |||
|             /* Make sure that the type is unified for later processing */ | |||
| 
 | |||
|             let curExtStore = extStore; | |||
|             if (Object.hasOwn(ssd, "_extStore") && ssd._extStore) { | |||
|                 curExtStore = ssd._extStore.setupForFunctions(); | |||
|             } | |||
| 
 | |||
|             if (curExtStore.getFunctionDistribution()(ssd, counter)) { | |||
|                 if (curExtStore._position.BEFORE) { | |||
|                     collectForBefore.push(ssd); | |||
|                 } else { | |||
|                     if (!forCollection.has(curExtStore)) { | |||
|                         forCollection.set(curExtStore, []); | |||
|                     } | |||
|                     forCollection.get(curExtStore).push(ssd); | |||
|                 } | |||
|             } | |||
|         } | |||
| 
 | |||
|         return forCollection; | |||
|     } | |||
| 
 | |||
| 
 | |||
|     /** | |||
|      * checks if the source has the extStoreType  | |||
|      * fills the target extStoreType-array with the corr. elements of source. | |||
|      * @param {Map<ExtStoreType, Array<SStoreDefinition>>} source  | |||
|      * @param {Map<ExtStoreType, Array<SStoreDefinition>>} target  | |||
|      * @param {ExtStoreType} extStoreType  | |||
|      * @returns  | |||
|      */ | |||
|     transferCollectedFunctions(source, target, extStoreType) { | |||
|         if (source) { | |||
|             if (source.has(extStoreType)) { | |||
|                 if (funcCollections.has(extStoreType)) { | |||
|                     target.get(extStoreType) | |||
|                         .push(source.get(extStoreType)) | |||
|                 } else { | |||
|                     target.set( | |||
|                         extStoreType, | |||
|                         source.get(extStoreType) | |||
|                     ); | |||
|                 } | |||
|             } | |||
|         } | |||
| 
 | |||
|         return target; | |||
|     } | |||
| 
 | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      *  | |||
|      * @param {Map<ExtStoreType, *>} extFuncMap  | |||
|      * @param {ExtStorageType} extStoreType  | |||
|      */ | |||
|     dealCollectedFuncs(extFuncMap, extStoreType) { | |||
|         if (extFuncMap.has(extStoreType)) { | |||
|             let collectionScriptTag = generateAndFillScriptTag(extFuncMap.get(extStoreType)); | |||
|             if (extStoreType === ExtStoreType.COLLECTED_SEGMENT_BEGIN) { | |||
|                 this._element.insertAdjacentElement( | |||
|                     "afterbegin", | |||
|                     generateAndFillScriptTag(segment) | |||
|                 ); | |||
|             } else { | |||
|                 Page.addElementToPage( | |||
|                     collectionScriptTag, | |||
|                     extStoreType | |||
|                 ); | |||
|             } | |||
|         } | |||
|     } | |||
| 
 | |||
| 
 | |||
|     /** | |||
|      * Generates and appends a child Component. | |||
|      * @param {Component} parent component the child component to add it. | |||
|      * @param {Component|WebTrinity|string} child  | |||
|      * @returns {WebTrinity} | |||
|      */ | |||
|     appendChildComponent(parent, child) { | |||
|         let childWT = new WebTrinity(); | |||
|         if (child instanceof Component) { | |||
|             childWT = child.generate(this); | |||
|         } | |||
| 
 | |||
|         if (child instanceof WebTrinity) { | |||
|             childWT = child; | |||
|         } | |||
| 
 | |||
|         if (child instanceof HTMLElement) { | |||
|             console.log("No wenity set - htmlEl was given"); | |||
|             childWT.compext = child; | |||
|         } | |||
| 
 | |||
|         parent._element.append(childWT.compext); | |||
| 
 | |||
|         return childWT; | |||
|     } | |||
| 
 | |||
| 
 | |||
|     /** | |||
|      * Iterates over the children of the component  | |||
|      * and calls generate on each child. | |||
|      * The resulting compext (the text of the component) is added/appended accordingly. | |||
|      * If the generation returns with delegatable scripts or styles | |||
|      * the WebTrinity object is collected in an array,  | |||
|      * which will be returned. | |||
|      *  | |||
|      * @param {Component} component  | |||
|      * @returns {Array<WebTrinity>} | |||
|      */ | |||
|     resolveChildren(component, styleStore, functionStore) { | |||
|         /** | |||
|          * @type {Array<WebTrinity>} | |||
|          */ | |||
|         let wenities = []; | |||
| 
 | |||
|         for (let child of component._children) { | |||
|             child = child.generate(this, styleStore, functionStore); | |||
| 
 | |||
|             let wenity = this.appendChildComponent(component, child); | |||
| 
 | |||
|             if (!wenity.isSSEmpty()) { | |||
|                 wenities.push(wenity); | |||
|             } | |||
|         } | |||
| 
 | |||
|         return wenities; | |||
|     } | |||
| 
 | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {Component} component  | |||
|      * @param {ExtStorage} [styleStore=null]  | |||
|      * @param {ExtStorage} [functionStore=null]  | |||
|      * @returns {WebTrinity} | |||
|      */ | |||
|     generate(component, styleStore = null, functionStore = null) { | |||
| 
 | |||
|         if (!styleStore) { | |||
|             styleStore = component._stylesExtStore; | |||
|         } | |||
|         if (!functionStore) { | |||
|             functionStore = component._functionsExtStore; | |||
|         } | |||
| 
 | |||
|         /** | |||
|          * DEAL WITH COMPONENT MODIFICATION FIRST  | |||
|          *  | |||
|          * @todo pay attention to the "overwrite" behaviour  | |||
|          *          the local modifier styles are the "closest" | |||
|          *          it might be appropriate to use this._styles.unshift(...) instead.  | |||
|          */ | |||
|         component._styles.push(new SStoreDefinition( | |||
|             (styleStore._aggregation !== ESAggregation.INTERNALIZED | |||
|                 ? "." | |||
|                 : "") | |||
|             + component._compName, | |||
|             component._modifier, | |||
|             component._stylesExtStore | |||
|         )); | |||
| 
 | |||
|         /** | |||
|          * DEAL WITH CHILDREN | |||
|          *  | |||
|          * Depending on the setup of the generator/-tion  | |||
|          * The children might return scripts or styles  | |||
|          * that are supposed to be collected  | |||
|          * and dealt with as a collection. | |||
|          */ | |||
|         let childrenWenities = this.resolveChildren(component, styleStore, functionStore); | |||
| 
 | |||
|         /** | |||
|          * DEAL WITH STYLES AND SCRIPTS  | |||
|          */ | |||
| 
 | |||
|         /** | |||
|          * @type {Array<SStoreDefinition>} | |||
|          */ | |||
|         let styleCollection = this.processStyles(component, styleStore); | |||
|         /** | |||
|          * @type {Map<ExtStoreType, Array<SStoreDefinition>>} | |||
|          */ | |||
|         let funcCollections = this.processFunctions(component, functionStore); | |||
| 
 | |||
|         /** | |||
|          * DEAL WITH CHILDREN WENITY SCRIPTS | |||
|          */ | |||
|         for (const child of childrenWenities) { | |||
|             if (child.scripts) { | |||
|                 executeOnExtStoreTypeCollectedTriple( | |||
|                     (extstoretype) => this.transferCollectedFunctions(child.scripts, funcCollections, extstoretype) | |||
|                 ); | |||
|             } | |||
|         } | |||
| 
 | |||
| 
 | |||
|         let wenity = new WebTrinity(); | |||
|         /** | |||
|          * DEAL WITH CHILDREN WENITY STYLE | |||
|          */ | |||
|         if (component._isCompel) { | |||
|             executeOnExtStoreTypeCollectedTriple((est) => this.dealCollectedFuncs(funcCollections, est)); | |||
|         } else { | |||
|             wenity.scripts = funcCollections; | |||
|             wenity.stylings = styleCollection; | |||
|         } | |||
| 
 | |||
|         wenity.compext = component._element | |||
| 
 | |||
|         for (const group of component._toRegister) { | |||
|             Page.subscribeComponentToGroup(group, component._compName); | |||
|         } | |||
| 
 | |||
|         return wenity; | |||
|     } | |||
| 
 | |||
| } | |||
| @ -0,0 +1,14 @@ | |||
| /** | |||
|  * Enum to access common events | |||
|  */ | |||
| const CommonEvents = Object.freeze({ | |||
|     ONCLICK: "onclick", | |||
|     ONCHANGE: "onchange", | |||
|     SCROLL: "scroll", | |||
|     DRAG_START: "dragstart", | |||
|     DRAG_END: "dragend", | |||
|     DRAG_ENTER: "dragenter", | |||
|     DRAG_LEAVE: "dragleave", | |||
|     DRAG_OVER: "dragover", | |||
|     DROP: "drop", | |||
| }); | |||
| @ -0,0 +1,63 @@ | |||
| /** | |||
|  * Default implementation of a contextmenu-behaviour | |||
|  */ | |||
| const DefaultContextMenu = { | |||
|     openContextMenuAction: function (identifier, refPos) { | |||
|         if (!refPos) { | |||
|             refPos = function (event) { | |||
|                 return new Sides() | |||
|                     .left(event.pageX) | |||
|                     .top(event.pageY); | |||
|             } | |||
|         } | |||
| 
 | |||
|         return function (event) { | |||
|             event.preventDefault(); | |||
| 
 | |||
|             if (refPos instanceof Function) { | |||
|                 refPos = refPos(event); | |||
|             } | |||
| 
 | |||
|             let menu = document.querySelector(`[data-autocompel="${identifier}"`); | |||
| 
 | |||
|             menu.style.left = `${refPos.getByIndex(SideDirections.LEFT)}px`; | |||
|             menu.style.top = `${refPos.getByIndex(SideDirections.TOP)}px`; | |||
| 
 | |||
|             helperFun.toggleElementVisibility(menu, true); | |||
|             helperFun.toggleElementVisibility(menu); | |||
| 
 | |||
|             document.addEventListener( | |||
|                 "click", | |||
|                 DefaultContextMenu.hideOnClickOutsideBoundsAction(identifier) | |||
|             ); | |||
|             document.addEventListener( | |||
|                 "keyup", | |||
|                 DefaultContextMenu.hideOnEscapeAction(identifier) | |||
|             ); | |||
|         } | |||
| 
 | |||
|     } | |||
|     , | |||
|     hideOnEscapeAction: function (identifier) { | |||
|         return function (event) { | |||
|             if (event.key === "Escape") { | |||
|                 let menu = document.querySelector(`[data-autocompel="${identifier}"`); | |||
|                 helperFun.toggleElementVisibility(menu, true); | |||
|                 document.removeEventListener("keyup"); | |||
|             } | |||
|         } | |||
|     } | |||
|     , | |||
|     hideOnClickOutsideBoundsAction: function (identifier) { | |||
|         return function (event) { | |||
|             let menu = document.querySelector(`[data-autocompel="${identifier}"`); | |||
| 
 | |||
|             let area = getEnclosingBounds(menu); | |||
| 
 | |||
|             if (!areXYInArea(area, event.clientX, event.clientY)) { | |||
|                 helperFun.toggleElementVisibility(menu, true); | |||
|                 document.removeEventListener("click") | |||
|             } | |||
|         } | |||
|     } | |||
| }; | |||
| @ -0,0 +1,123 @@ | |||
| const EventDrag = Object.freeze({ | |||
|     OVER: "over", | |||
|     START: "start", | |||
|     END: "end", | |||
|     ENTER: "enter", | |||
|     LEAVE: "leave" | |||
| }); | |||
| 
 | |||
| /** | |||
|  * Defines default functions to setup a drag/drop behaviour | |||
|  */ | |||
| class DragAndDropImplementation { | |||
|     _isMngmtElNeeded; | |||
| 
 | |||
|     ensureManagementElementExists() { | |||
| 
 | |||
|     } | |||
| 
 | |||
|     getManagementElement() { | |||
| 
 | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {string} datakey  | |||
|      * @param {string} selector  | |||
|      * @returns {Function} | |||
|      */ | |||
|     dragStartAction(datakey, selector) { | |||
|         return function (event) { | |||
|             console.log("DragEvent", event, "on", selector); | |||
|             event.dataTransfer | |||
|                 .setData( | |||
|                     datakey, | |||
|                     JSON.stringify({ | |||
|                         ["selector"]: selector, | |||
|                         ["relDims"]: new TwoDimPoint( | |||
|                             event.clientX - event.target.getBoundingClientRect().left, | |||
|                             event.clientY - event.target.getBoundingClientRect().top | |||
|                         ) | |||
|                     }) | |||
|                 ); | |||
|         } | |||
|     } | |||
| 
 | |||
|     dragEventAction(selector) { | |||
|         return function (event) { | |||
| 
 | |||
|         } | |||
|     } | |||
| 
 | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {string} datakey  | |||
|      * @param {string} selector  | |||
|      * @returns {Function} | |||
|      */ | |||
|     dropEventAction(datakey, selector) { | |||
|         return function (event) { | |||
|             event.preventDefault(); | |||
| 
 | |||
|             let draggedData = JSON.parse(event.dataTransfer.getData(datakey)); | |||
|             let draggedElement = document.querySelector(draggedData.selector); | |||
| 
 | |||
|             let target = event.target | |||
|                 .closest(selector); | |||
| 
 | |||
|             console.log("Default drop event"); | |||
| 
 | |||
|             //if (![...target.childNodes].includes(draggedElement)) {
 | |||
|             target.appendChild(draggedElement); | |||
|             //}
 | |||
|         } | |||
|     } | |||
| }; | |||
| 
 | |||
| 
 | |||
| const DADInPlace = new DragAndDropImplementation(); | |||
| DADInPlace.dropEventAction = function (datakey, selector) { | |||
|     /** | |||
|      * @param {Event} event  | |||
|      */ | |||
|     return function (event) { | |||
|         event.preventDefault(); | |||
| 
 | |||
|         let draggedData = JSON.parse(event.dataTransfer.getData(datakey)); | |||
|         let draggedElement = document.querySelector(draggedData.selector); | |||
| 
 | |||
|         let target = event.target | |||
|             .closest(selector); | |||
| 
 | |||
|         let dimsDraggedEl = new Dimensions() | |||
|             .width(draggedElement.offsetWidth) | |||
|             .height(draggedElement.offsetHeight); | |||
| 
 | |||
|         let pagePoint = new TwoDimPoint( | |||
|             event.clientX, | |||
|             event.clientY | |||
|         ); | |||
| 
 | |||
|         let parentPagePoint = new TwoDimPoint( | |||
|             target.getBoundingClientRect().left, | |||
|             target.getBoundingClientRect().top | |||
|         ); | |||
| 
 | |||
|         draggedElement.style["width"] = dimsDraggedEl._fFirst + "px"; | |||
|         draggedElement.style["height"] = dimsDraggedEl._fSecond + "px"; | |||
| 
 | |||
|         // x page to relative is (parent-page), then -rel
 | |||
| 
 | |||
|         let dropPosition = pagePoint | |||
|             .minusTDP(parentPagePoint) | |||
|             .minusTDP(draggedData.relDims); | |||
| 
 | |||
|         draggedElement.style.position = "relative"; | |||
|         draggedElement.style.left = dropPosition.x + "px"; | |||
|         draggedElement.style.top = dropPosition.y + "px"; | |||
|         //if (![...target.childNodes].includes(draggedElement)) {
 | |||
|         target.appendChild(draggedElement); | |||
|         //}
 | |||
|     } | |||
| } | |||
| @ -0,0 +1,55 @@ | |||
| /** | |||
|  * | |||
|  */ | |||
| class ObjectAccessObject { | |||
|     /** | |||
|      * | |||
|      * @param {Object} object | |||
|      */ | |||
|     constructor(object = null) { | |||
|         /** | |||
|          * @type {Array<string>} | |||
|          */ | |||
|         this.keys = (object ? Object.keys(object) : []); | |||
|         /** | |||
|          * @type {Object} | |||
|          */ | |||
|         this.objects = (object ? object : {}); | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * | |||
|      * @param {Array<string>} keyArr | |||
|      * @param {Object} refObject | |||
|      * @returns | |||
|      */ | |||
|     fillByArrayReference(keyArr, refObject) { | |||
|         this.keys = keyArr; | |||
|         refObject = keyArr.reduce((a, c) => Object.assign(a, { [c]: refObject[c] }), {}); | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * | |||
|      * @param {string} key | |||
|      * @param {*} value | |||
|      * @returns {ObjectAccessObject} | |||
|      */ | |||
|     add(key, value) { | |||
|         this.objects[key, value]; | |||
|         this.keys.push(key); | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * | |||
|      * @param {string} key | |||
|      * @returns {*} | |||
|      */ | |||
|     remove(key) { | |||
|         let tmp = this.objects[key]; | |||
|         delete this.objects[key]; | |||
|         return tmp; | |||
|     } | |||
| 
 | |||
| } | |||
| @ -0,0 +1,348 @@ | |||
| /** | |||
|  * Class containing two numbers.  | |||
|  * Usually they represent coordinates,  | |||
|  * but they also might serve as length and width  | |||
|  * as this class provides several convenience methods. | |||
|  */ | |||
| class TwoDimPoint { | |||
|     /** | |||
|      *  | |||
|      * @param {number} x  | |||
|      * @param {number} y  | |||
|      */ | |||
|     constructor(x = -1, y = -1) { | |||
|         this.x = x; | |||
|         this.y = y; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @returns {string} | |||
|      */ | |||
|     toString() { | |||
|         return `P(${this.x},${this.y})`; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Loads the width and height of an element into the x and y of this TDP. | |||
|      * @param {HTMLElement} element  | |||
|      * @returns {TwoDimPoint} | |||
|      */ | |||
|     loadFromElementDimensions(element) { | |||
|         let bcr = element.getBoundingClientRect(); | |||
|         this.x = bcr.width; | |||
|         this.y = bcr.height; | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Loads the coordinates of the top-left corner of an element,  | |||
|      * into the x and y of this TDP. | |||
|      * @param {HTMLElement} element  | |||
|      * @returns {TwoDimPoint} | |||
|      */ | |||
|     loadFromElementZero(element) { | |||
|         let bcr = element.getBoundingClientRect(); | |||
|         this.x = bcr.left; | |||
|         this.y = bcr.top; | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Loads the coordinates of the bottom-right corner of an element,  | |||
|      * into the x and y of this TDP. | |||
|      * @param {HTMLElement} element  | |||
|      * @returns {TwoDimPoint} | |||
|      */ | |||
|     loadFromElementMax(element) { | |||
|         let bcr = element.getBoundingClientRect(); | |||
|         this.x = bcr.left; | |||
|         this.y = bcr.top; | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * If onConditionMet is true (default) and the value of x is positive  | |||
|      * the x of this TDP will be multiplied with -1,  | |||
|      * otherwise nothing will be done. | |||
|      * @param {boolean} [onConditionMet = true] onConditionMet  | |||
|      * @returns {TwoDimPoint} this TDP Object | |||
|      */ | |||
|     setXAsNegative(onConditionMet = true) { | |||
|         if (onConditionMet && this.x > 0) { | |||
|             this.x = this.x * -1; | |||
|         } | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * If onConditionMet is true (default) and the value of y is positive  | |||
|      * the y of this TDP will be multiplied with -1,  | |||
|      * otherwise nothing will be done. | |||
|      * @param {boolean} [onConditionMet = true] onConditionMet  | |||
|      * @returns {TwoDimPoint} | |||
|      */ | |||
|     setYAsNegative(onConditionMet = true) { | |||
|         if (onConditionMet && this.y > 0) { | |||
|             this.y = this.y * -1; | |||
|         } | |||
|         return this; | |||
|     } | |||
| 
 | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {boolean} onConditionMetX  | |||
|      * @param {boolean} onConditionMetY  | |||
|      * @returns {TwoDimPoint} | |||
|      */ | |||
|     setAsNegative(onConditionMetX, onConditionMetY) { | |||
|         this.setXAsNegative(onConditionMetX); | |||
|         this.setYAsNegative(onConditionMetY); | |||
|         return this; | |||
|     } | |||
| 
 | |||
| 
 | |||
|     /** | |||
|      * Subtracts the given Delta from the x-value | |||
|      * @param {number} delta  | |||
|      * @returns {TwoDimPoint} | |||
|      */ | |||
|     setXMinus(delta) { | |||
|         this.x = this.x - delta; | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Adds the given Delta from the x-value | |||
|      * @param {number} delta  | |||
|      * @returns {TwoDimPoint} | |||
|      */ | |||
|     setXPlus(delta) { | |||
|         this.x = this.x + delta; | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Subtracts the given Delta from the y-value | |||
|      * @param {number} delta  | |||
|      * @returns {TwoDimPoint} | |||
|      */ | |||
|     setYMinus(delta) { | |||
|         this.y = this.y - delta; | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Adds the given Delta from the y-value | |||
|      * @param {number} delta  | |||
|      * @returns {TwoDimPoint} | |||
|      */ | |||
|     setYPlus(delta) { | |||
|         this.y = this.y + delta; | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Returns the absolute delta between this x and given ref | |||
|      * @param {number} refnumber  | |||
|      * @returns {number} | |||
|      */ | |||
|     getXDeltaTo(refnumber) { | |||
|         return Math.abs(this.x - refnumber); | |||
|     } | |||
| 
 | |||
| 
 | |||
|     /** | |||
|      * Returns the absolute delta between this y and given ref | |||
|      * @param {number} refnumber  | |||
|      * @returns {number} | |||
|      */ | |||
|     getYDeltaTo(refnumber) { | |||
|         return Math.abs(this.y - refnumber); | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {number} times  | |||
|      * @returns {TwoDimPoint} | |||
|      */ | |||
|     setXMultiplied(times) { | |||
|         this.x = this.x * times; | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {number} times  | |||
|      * @returns {TwoDimPoint} | |||
|      */ | |||
|     setYMultiplied(times) { | |||
|         this.y = this.y * times; | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {number} times  | |||
|      * @returns {TwoDimPoint} | |||
|      */ | |||
|     setXDivided(times) { | |||
|         if (times === 0) { | |||
|             throw new Error("Dividing by 0 is not defined"); | |||
|         } | |||
|         this.x = this.x / times; | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {number} times  | |||
|      * @returns {TwoDimPoint} | |||
|      */ | |||
|     setYDivided(times) { | |||
|         if (times === 0) { | |||
|             throw new Error("Dividing by 0 is not defined"); | |||
|         } | |||
|         this.y = this.y / times; | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Subtracts the given Delta {TwoDimPoint} TDP from this TDP | |||
|      * @param {TwoDimPoint} delta  | |||
|      * @returns {TwoDimPoint} | |||
|      */ | |||
|     setThisByMinusTDP(delta) { | |||
|         this.x = this.x - delta.x; | |||
|         this.y = this.y - delta.y; | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Adds the given Delta {TwoDimPoint} TDP to this TDP | |||
|      * @param {TwoDimPoint} delta  | |||
|      * @returns {TwoDimPoint} | |||
|      */ | |||
|     setThisByPlusTDP(delta) { | |||
|         this.x = this.x + delta.x; | |||
|         this.y = this.y + delta.y; | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {nr} tdp  | |||
|      * @returns {TwoDimPoint} | |||
|      */ | |||
|     setByMultipliedByNumber(nr) { | |||
|         this.x = this.x * nr; | |||
|         this.y = this.y * nr; | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {TwoDimPoint} tdp  | |||
|      * @returns {TwoDimPoint} | |||
|      */ | |||
|     setByMultipliedByTDP(tdp) { | |||
|         this.x = this.x * tdp.x; | |||
|         this.y = this.y * tdp.y; | |||
|         return this; | |||
|     } | |||
| 
 | |||
| 
 | |||
|     /** | |||
|      * @param {nr} tdp  | |||
|      * @returns {TwoDimPoint} | |||
|      */ | |||
|     setByDividedByNumber(nr) { | |||
|         if (nr === 0) { | |||
|             throw new Error("Dividing by 0 is not defined"); | |||
|         } | |||
|         this.x = this.x / nr; | |||
|         this.y = this.y / nr; | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {TwoDimPoint} tdp  | |||
|      * @returns {TwoDimPoint} | |||
|      */ | |||
|     setByDividedByTDP(tdp) { | |||
|         if (tdp.x === 0 | tdp.y === 0) { | |||
|             throw new Error("Dividing by 0 is not defined"); | |||
|         } | |||
|         this.x = this.x / tdp.x; | |||
|         this.y = this.y / tdp.y; | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Creates and returns a new TDP  | |||
|      * consisting of the absolute deltas of the x/y coordinates of this and the reference TDP. | |||
|      * @param {TwoDimPoint} refTDP  | |||
|      * @returns {TwoDimPoint} | |||
|      */ | |||
|     getNewDeltaTDP(refTDP) { | |||
|         return new TwoDimPoint( | |||
|             this.getXDeltaTo(refTDP.x), | |||
|             this.getYDeltaTo(refTDP.y) | |||
|         ); | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Returns the absolute Distance between two points. | |||
|      * @param {TwoDimPoint} refTDP  | |||
|      * @returns {number} | |||
|      */ | |||
|     getDistanceBetween(refTDP) { | |||
|         return Math.hypot( | |||
|             this.getXDeltaTo(refTDP.x), | |||
|             this.getYDeltaTo(refTDP.y) | |||
|         ); | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Returns a new TDP where x and y are calculated,  | |||
|      * based on the given distance and angle (degree).  | |||
|      * @param {number} distance  | |||
|      * @param {number} degrees  | |||
|      * @returns {TwoDimPoint} | |||
|      */ | |||
|     getNewTDPByDistance( | |||
|         distance, | |||
|         degrees | |||
|     ) { | |||
|         /** angle in radians */ | |||
|         let angle = ((parseFloat(degrees) * Math.PI) / 180); | |||
|         return new TwoDimPoint( | |||
|             this.x + distance * Math.cos(angle), | |||
|             this.y + distance * Math.sin(angle), | |||
|         ); | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Multiplies both (x,y) with -1 to inverse them. | |||
|      * @returns {TwoDimPoint} | |||
|      */ | |||
|     inverse() { | |||
|         this.x = this.x * -1 | |||
|         this.y = this.y * -1 | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Flips/changes x and y with each other. | |||
|      * @returns {TwoDimPoint}  | |||
|      */ | |||
|     flip() { | |||
|         let oldX = this.x; | |||
|         this.x = this.y; | |||
|         this.y = oldX; | |||
|         return this; | |||
|     } | |||
| 
 | |||
| } | |||
| @ -0,0 +1,101 @@ | |||
| /** | |||
|  *  | |||
|  * @param {Object | Array<Object>} singleOrArray  | |||
|  * @param {Function} fun | |||
|  * @returns {Object | Array<Object>} | |||
|  */ | |||
| function onSingleOrArray(singleOrArray, fun) { | |||
|     if (singleOrArray instanceof Array) { | |||
|         return singleOrArray.map(fun); | |||
|     } | |||
|     return fun(singleOrArray); | |||
| } | |||
| 
 | |||
| 
 | |||
| /** | |||
|  *  | |||
|  */ | |||
| const helperFun = { | |||
|     /** | |||
|      * Iterates over the keys of attrs,  | |||
|      * extracts the corresponding value  | |||
|      * and applies the callback (cb) on it in the order (key, value, targetContainer) | |||
|      * @extends StyleAndScriptStoringComponent | |||
|      * @param {map<string,any>} attrs  | |||
|      * @param {Object} intoContainer  | |||
|      * @param {Function<string, any, Object>} cb  | |||
|      * @returns {Object} the filled container | |||
|      */ | |||
|     fillAttrsInContainerByCb: function (attrs, intoContainer, cb) { | |||
|         let keys = Object.keys(attrs); | |||
|         for (let i = 0; i < keys.length; i++) { | |||
|             cb(keys[i], attrs[keys[i]], intoContainer); | |||
|         } | |||
|         return intoContainer; | |||
|     }, | |||
| 
 | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {HTMLElement} element | |||
|      * @returns {string} | |||
|      */ | |||
|     getHigherCompSelector: function (element) { | |||
|         let dac = "data-autocompel"; | |||
|         let hcompel = element.closest('[data-compel-ishcompel="true"]'); | |||
|         return `[${dac}="${hcompel.getAttribute(dac)}"]`; | |||
|     }, | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {string} selector  | |||
|      * @returns {boolean} [ensureHidden=false] for true element is now hidden and false it is not hidden. | |||
|      */ | |||
|     toggleSelectorElementVisibility: function (selector, ensureHidden = false) { | |||
|         /** | |||
|          * @type {HTMLElement} | |||
|          */ | |||
|         let el = document.querySelector(selector); | |||
|         let name = el.getAttribute("data-autocompel"); | |||
| 
 | |||
|         console.log("De-/hiding", name, selector); | |||
| 
 | |||
|         return helperFun.toggleElementVisibility(el, ensureHidden); | |||
|     }, | |||
| 
 | |||
|     /** | |||
|      * (De-) hides the given element.  | |||
|      * On ensureHidden=true it will be hidden regardless of the current state. | |||
|      *  | |||
|      * @param {HTMLElement} element  | |||
|      * @param {boolean} ensureHidden  | |||
|      * @returns {boolean} true if element is now hidden | |||
|      */ | |||
|     toggleElementVisibility: function (element, ensureHidden = false) { | |||
|         element.classList.toggle("compel-mech-hidden"); | |||
| 
 | |||
|         let isNowHidden = false; | |||
| 
 | |||
|         if (element.hasAttribute("hidden")) { | |||
|             element.removeAttribute("hidden"); | |||
|             element.style["display"] = "flex"; | |||
|             isNowHidden = false; | |||
|         } else { | |||
|             element.setAttribute("hidden", "hidden"); | |||
|             element.style.removeProperty("display"); | |||
|             isNowHidden = true; | |||
|         } | |||
| 
 | |||
|         if (ensureHidden && !isNowHidden) { | |||
|             return helperFun.toggleElementVisibility(element); | |||
|         } else { | |||
|             return isNowHidden; | |||
|         } | |||
|     }, | |||
| 
 | |||
|     /** | |||
|      * Contains helperFun functions from the installed/loaded extensions  | |||
|      * (jpclwe-<extension-name>) | |||
|      */ | |||
|     extensions: {} | |||
| } | |||
| @ -0,0 +1,84 @@ | |||
| /** | |||
|  *  | |||
|  * @param {number} start  | |||
|  * @param {number} end  | |||
|  * @param {number} value  | |||
|  * @param {number} tolerance  | |||
|  * @param {boolean} usePercentage  | |||
|  * @returns {boolean} | |||
|  */ | |||
| function isValueInBounds(start, end, value, tolerance = 0, usePercentage = false) { | |||
|     if (tolerance !== 0) { | |||
|         if (usePercentage) { | |||
|             start = start * (1 - tolerance / 100); | |||
|             end = end * (1 + tolerance / 100); | |||
|         } else { | |||
|             start = start - tolerance; | |||
|             end = end + tolerance; | |||
|         } | |||
|     } | |||
| 
 | |||
|     return value >= start && value <= end; | |||
| } | |||
| 
 | |||
| 
 | |||
| /** | |||
|  * @param {number} x  | |||
|  * @param {number} y  | |||
|  * @param {Map<SideDirections,number>} area  | |||
|  * @param {number} tolerance  | |||
|  * @param {boolean} usePercentage if tolerance is given and this is set to true,  | |||
|  *                  the tolerance will be calculated by percentage,  | |||
|  *                  otherwise it will be subtracted/added | |||
|  * @returns {boolean} | |||
|  */ | |||
| function areXYInArea(x, y, area, tolerance = 0, usePercentage = false) { | |||
|     return isValueInBounds( | |||
|         area.get(SideDirections.LEFT), | |||
|         area.get(SideDirections.RIGHT), | |||
|         x, | |||
|         tolerance, | |||
|         usePercentage | |||
|     ) && isValueInBounds( | |||
|         area.get(SideDirections.TOP), | |||
|         area.get(SideDirections.BOTTOM), | |||
|         y, | |||
|         tolerance, | |||
|         usePercentage | |||
|     ); | |||
| } | |||
| 
 | |||
| /** | |||
|  *  | |||
|  * @param {TwoDimPoint} point  | |||
|  * @param {Map<SideDirections,number>} area  | |||
|  * @param {number} tolerance  | |||
|  * @param {boolean} usePercentage if tolerance is given and this is set to true,  | |||
|  *                  the tolerance will be calculated by percentage,  | |||
|  *                  otherwise it will be subtracted/added | |||
|  */ | |||
| function isPointInArea(point, area, tolerance = 0, usePercentage = false) { | |||
|     return areXYInArea( | |||
|         point.x, | |||
|         point.y, | |||
|         area, | |||
|         tolerance, | |||
|         usePercentage | |||
|     ); | |||
| } | |||
| 
 | |||
| /** | |||
|  *  | |||
|  * @param {HTMLElement} element  | |||
|  * @returns {Object<SideDirections, number} | |||
|  */ | |||
| function getEnclosingBounds(element) { | |||
|     let area = element.getBoundingClientRect(); | |||
|     let parentArea = element.parentElement.getBoundingClientRect(); | |||
|     return { | |||
|         [SideDirections.LEFT]: Math.min(area.left, parentArea.left), | |||
|         [SideDirections.RIGHT]: Math.max(area.right, parentArea.right), | |||
|         [SideDirections.TOP]: Math.min(area.top, parentArea.top), | |||
|         [SideDirections.BOTTOM]: Math.max(area.bottom, parentArea.bottom) | |||
|     } | |||
| } | |||
| @ -1,276 +0,0 @@ | |||
| /** | |||
|  * This file is part of the jps-like-websites lib | |||
|  * URL: https://git.labos.goip.de/chris/jpc-like-websites
 | |||
|  * COPYRIGHT and LICENCE are owned by its creator Christian Martin  | |||
|  * Copy, altering or distribution without the allowance of the owner are prohibited | |||
|  */ | |||
| 
 | |||
| /** | |||
|  * A chained class that sets most of the stylings of an element | |||
|  * Attributes: | |||
|  *  _modifications: {Object} | |||
|  */ | |||
| class Modifier { | |||
|     /** | |||
|      * @property {Map<string,string>} _modifications | |||
|      */ | |||
|     _modifications; | |||
|     _shape; | |||
| 
 | |||
|     constructor() { | |||
|         this._modifications = new Object(); | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Sets the modifications for widht and height to 100%.  | |||
|      * @returns {Modifier} this modifier object | |||
|      */ | |||
|     fillMaxSize(widthFraction = 1, heightFraction = 1) { | |||
|         return this.fillMaxWidth(widthFraction) | |||
|             .fillMaxHeight(heightFraction); | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Sets the modification for width to the given fraction of 1 (default 1 := 100%). | |||
|      * @param {number} fraction  | |||
|      * @returns {Modifier} this modifier object | |||
|     */ | |||
|     fillMaxWidth(fraction = 1) { | |||
|         this._modifications["width"] = (100 * fraction) + "%"; | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Sets the modification for height to the given fraction of 1 (default 1 := 100%). | |||
|      * @param {number} fraction  | |||
|      * @returns {Modifier} this modifier object | |||
|      */ | |||
|     fillMaxHeight(fraction = 1) { | |||
|         this._modifications["height"] = (100 * fraction) + "%"; | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Sets modifications according to the dimensions object. | |||
|      * @param {Dimensions} dimensions  | |||
|      * @returns {Modifier} this modifier object | |||
|      */ | |||
|     dimensions(dimensions) { | |||
|         dimensions.toModifications() | |||
|             .forEach(kvpair => { | |||
|                 this._modifications[kvpair.key] = kvpair.value; | |||
|             }) | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Sets the padding on all sides according to the given padding object.  | |||
|      * Currently the padding will always be set  | |||
|      * to the most recent padding/padding.  | |||
|      * @param {Sides} siding | |||
|      * @returns {Modifier} this modifier object | |||
|      */ | |||
|     padding(siding) { | |||
|         let keyToAdd = ""; | |||
|         if (siding instanceof PaddingChain) { | |||
| 
 | |||
|         } else if (siding instanceof Sides) { | |||
|             keyToAdd = "padding-" | |||
|         } | |||
|         siding.toModifications() | |||
|             .forEach(kvpair => { | |||
|                 this._modifications[keyToAdd + kvpair.key] = kvpair.value; | |||
|             }) | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Sets the margin on all sides according to the given siding object.  | |||
|      * Currently the margin will always be set  | |||
|      * to the most recent margin/siding.  | |||
|      * @param {Sides} siding  | |||
|      * @returns {Modifier} this modifier object | |||
|      */ | |||
|     margin(siding) { | |||
|         let keyToAdd = ""; | |||
|         if (siding instanceof Sides) { | |||
|             keyToAdd = "margin-" | |||
|         } | |||
| 
 | |||
|         siding.toModifications() | |||
|             .forEach(kvpair => { | |||
|                 this._modifications[keyToAdd + kvpair.key] = kvpair.value; | |||
|             }); | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Sets the background-color as a rgb color. | |||
|      * @param {Color} color  | |||
|      * @returns {Modifier} this modifier object | |||
|      */ | |||
|     background(color) { | |||
|         this._modifications["background-color"] = color.cssRGBString(); | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Sets the color as a rgb color. | |||
|      * @param {Color} color  | |||
|      * @returns {Modifier} this modifier object | |||
|      */ | |||
|     color(color) { | |||
|         this._modifications["color"] = color.cssRGBString(); | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Adds the modifications of the given Modifier to current Modifier. | |||
|      * This is especailly used in the cases of extending existing/pre defined  | |||
|      * Components. | |||
|      * CAUTION matching existing modifications will be overwritten.  | |||
|      * @param modifier The "new" Modifier | |||
|      * @returns {Modifier} The "old/current" Modifier,  | |||
|      * extended with the modifications of the given Modifier. | |||
|      */ | |||
|     join(modifier, modifications = {}) { | |||
|         var keys = Object.keys(modifier.ensureModifier()._modifications); | |||
|         for (let i = 0; i < keys.length; i++) { | |||
|             /* if (!this._modifications.hasOwnProperty(keys[i])) */ | |||
|             this._modifications[keys[i]] = modifier.ensureModifier()._modifications[keys[i]]; | |||
|         } | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {string} key a css style rule | |||
|      * @param {string} value the corresponding value to the css style rule | |||
|      * @returns {Modifier} this modifier object | |||
|      */ | |||
|     setStyleRule(key, value) { | |||
|         this._modifications[key] = value; | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Sets a border line (with given linestyle) to all sides.  | |||
|      * If lineStyle is an array, the containing LineStyles,  | |||
|      * are applied in the order: [top, right, bottom, left]. | |||
|      * If the border has a shape defined,  | |||
|      * this shape will override earlier shape definitions. | |||
|      * Otherwise existing shape definitions will be applied. | |||
|      * @param {Border} border the style of the border line | |||
|      * @returns {Modifier} this modifier object | |||
|      */ | |||
|     border(border) { | |||
|         if (border._shape){ | |||
|             this.clip(border._shape); | |||
|         }else if(this._shape){ | |||
|             border._shape = this._shape; | |||
|         } | |||
|         border.toModifications() | |||
|             .forEach(e => this._modifications[e.key] = e.value); | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {Shape} shape  | |||
|      * @returns {Modifier}  | |||
|      */ | |||
|     clip(shape) { | |||
|         this._shape = shape; | |||
|         this._modifications["border-radius"] = shape.getOrderedValues().join(' '); | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {number} size of width and height in pixels | |||
|      * @returns {DimensionsChain}  | |||
|      */ | |||
|     linkDimensions(size = -1) { | |||
|         if (size === -1) { | |||
|             return new DimensionsChain(this); | |||
|         } else { | |||
|             return new DimensionsChain(this).all(size).ensureModifier() | |||
|         } | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {number} amount the padding for all four sides | |||
|      * @returns {PaddingChain}  | |||
|      */ | |||
|     linkPadding(amount = -1) { | |||
|         if (amount === -1) { | |||
|             return new PaddingChain(this); | |||
|         } else { | |||
|             return new PaddingChain(this).all(amount); | |||
|         } | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {number} cornerRadius will create a rounded rectangle with the given cornerRadius | |||
|      * @returns {ShapeChain} | |||
|      */ | |||
|     linkClip(cornerRadius = -1) { | |||
|         if (cornerRadius === -1) { | |||
|             return new ShapeChain(this); | |||
|         } else { | |||
|             return new ShapeChain(this).all(cornerRadius); | |||
|         } | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {number} borderWidth sets the width of all four border sides | |||
|      * @returns {BorderChain} | |||
|      */ | |||
|     linkBorder(borderWidth = -1) { | |||
|         if (borderWidth === -1) { | |||
|             return new BorderChain(this); | |||
|         } else { | |||
|             return new BorderChain(this).width(borderWidth); | |||
|         } | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @returns {Modifier} | |||
|      */ | |||
|     ensureModifier() { | |||
|         return this; | |||
|     } | |||
| 
 | |||
| } | |||
| 
 | |||
| class ChainableModifier extends Modifier { | |||
|     _component; | |||
| 
 | |||
|     constructor(component) { | |||
|         super(); | |||
|         this._component = component; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @returns {Component} | |||
|      */ | |||
|     toComponent() { | |||
|         return this._component.modifier(this); | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {Component|Array<Component>} innerComponent  | |||
|      * @returns {Component} the parent Component | |||
|      */ | |||
|     childContext(innerComponent) { | |||
|         return this._component | |||
|             .modifier(this) | |||
|             .childContext(innerComponent); | |||
|     } | |||
| } | |||
| @ -0,0 +1,187 @@ | |||
| /** | |||
|  * @extends Modifier | |||
|  * @inheritdoc | |||
|  */ | |||
| class ChainableModifier extends Modifier { | |||
|     /** | |||
|      * @type {Component} | |||
|      */ | |||
|     _component; | |||
| 
 | |||
|     /** | |||
|      * | |||
|      * @param {Component} component | |||
|      */ | |||
|     constructor(component) { | |||
|         super(); | |||
|         this._component = component; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * @inheritdoc | |||
|      * @override | |||
|      * @returns {ChainableModifier} | |||
|      */ | |||
|     fillMaxSize(widthFraction = 1, heightFraction = 1) { | |||
|         return super.fillMaxSize(widthFraction, heightFraction); | |||
|     } | |||
|     /** | |||
|      * @inheritdoc | |||
|      * @override | |||
|      * @returns {ChainableModifier} | |||
|      */ | |||
|     fillMaxWidth(fraction = 1) { | |||
|         return super.fillMaxWidth(fraction); | |||
|     } | |||
|     /** | |||
|      * @inheritdoc | |||
|      * @override | |||
|      * @returns {ChainableModifier} | |||
|      */ | |||
|     fillMaxHeight(fraction = 1) { | |||
|         return super.fillMaxHeight(fraction); | |||
|     } | |||
|     /** | |||
|      * @inheritdoc | |||
|      * @override | |||
|      * @returns {ChainableModifier} | |||
|      */ | |||
|     background(color) { | |||
|         return super.background(color); | |||
|     } | |||
|     /** | |||
|      * @inheritdoc | |||
|      * @override | |||
|      * @returns {ChainableModifier} | |||
|      */ | |||
|     color(color) { | |||
|         return super.color(color); | |||
|     } | |||
|     /** | |||
|      * @inheritdoc | |||
|      * @override | |||
|      * @returns {ChainableModifier} | |||
|      */ | |||
|     setStyleRule(key, value) { | |||
|         return super.setStyleRule(key, value); | |||
|     } | |||
|     /** | |||
|      * @inheritdoc | |||
|      * @override | |||
|      * @returns {ChainableModifier} | |||
|      */ | |||
|     addStyleRuleMap(rulemap) { | |||
|         return super.addStyleRuleMap(rulemap); | |||
|     } | |||
|     /** | |||
|      * @inheritdoc | |||
|      * @override | |||
|      * @returns {ChainableModifier} | |||
|      */ | |||
|     removeStyleRule(key) { | |||
|         return super.removeStyleRule(key); | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * @inheritdoc | |||
|      * | |||
|      * In Case it is called from a ChainableModifier chain, | |||
|      * the Return type would be DimensionsChainedModifier instead of DimensionsChain. | |||
|      * @override | |||
|      * @param {Dimensions | number | undefined} [modify=null] dimensions | |||
|      * @returns {ChainableModifier | DimensionsChainedModifier} | |||
|      */ | |||
|     dimensions(modify = null) { | |||
|         if (modify instanceof Dimensions || Number.isInteger(modify)) { | |||
|             return super.dimensions(modify); | |||
|         } | |||
|         return new DimensionsChainedModifier(this); | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * @inheritdoc | |||
|      * @override | |||
|      * @param {Padding | number | undefined} [modify=null] modify as in modifiers | |||
|      * @returns {ChainableModifier | PaddingChainedModifier} | |||
|      */ | |||
|     padding(modify = null) { | |||
|         if (modify instanceof Padding || Number.isInteger(modify)) { | |||
|             return super.padding(modify); | |||
|         } | |||
|         return new PaddingChainedModifier(this); | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * @inheritdoc | |||
|      * @override | |||
|      * @param {Margin | number | undefined} [modify=null] modify as in modifiers | |||
|      * @returns {ChainableModifier | MarginChainedModifier} | |||
|      */ | |||
|     margin(modify = null) { | |||
|         if (modify instanceof Margin || Number.isInteger(modify)) { | |||
|             return super.margin(modify); | |||
|         } | |||
|         return new MarginChainedModifier(this); | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * @inheritdoc | |||
|      * | |||
|      * @override | |||
|      * @param {Shape | number | undefined} [modify=null] modify | |||
|      * @returns {ChainableModifier | ShapeChainedModifier} | |||
|      */ | |||
|     clip(modify = null) { | |||
|         if (modify instanceof Shape || Number.isInteger(modify)) { | |||
|             return super.clip(modify); | |||
|         } | |||
|         return new ShapeChainedModifier(this); | |||
|     } | |||
| 
 | |||
| 
 | |||
|     /** | |||
|      * | |||
|      * @inheritdoc | |||
|      * | |||
|      * @override | |||
|      * @param {Border | number | undefined} [modify=null] modify | |||
|      * @returns {ChainableModifier | BorderChainedModifier} | |||
|      */ | |||
|     border(modify = null) { | |||
|         if (modify instanceof Border || Number.isInteger(modify)) { | |||
|             return super.border(modify); | |||
|         } | |||
|         return new BorderChainedModifier(this); | |||
|     } | |||
| 
 | |||
| 
 | |||
|     /** | |||
|      * | |||
|      * @returns {Component} | |||
|      */ | |||
|     toComponent() { | |||
|         return this._component.modifier(this); | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * | |||
|      * @param {Component|Array<Component>} innerComponent | |||
|      * @returns {Component} the parent Component | |||
|      */ | |||
|     childContext(innerComponent) { | |||
|         return this._component | |||
|             .modifier(this) | |||
|             .childContext(innerComponent); | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Calls chainChild() from Component (ChildbearerComponent) | |||
|      * @see {ChildbearerComponent.chainChild} | |||
|      * @returns {builder} | |||
|      */ | |||
|     chainChild() { | |||
|         return this._component | |||
|             .modifier(this) | |||
|             .chainChild(); | |||
|     } | |||
| } | |||
| @ -0,0 +1,87 @@ | |||
| /** | |||
|  * @abstract | |||
|  */ | |||
| class ModificationSubChain { | |||
|     /** | |||
|      * @type {Modifier} | |||
|      */ | |||
|     _modifier; | |||
|     /** | |||
|      * @type {string} | |||
|      */ | |||
|     _modMethod; | |||
| 
 | |||
|     /** | |||
|      * | |||
|      * @param {Modifier} modifier | |||
|      * @param {string} modMethod | |||
|      */ | |||
|     constructor(modifier, modMethod) { | |||
|         this._modifier = modifier; | |||
|         this._modMethod = modMethod; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Returns the Modifier SubChain to the Modifier | |||
|      * @returns {Modifier|ChainableModifier} | |||
|      */ | |||
|     toModifier() { | |||
|         return this._modifier[this._modMethod](this); | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Returns chain to the Modifier | |||
|      * @returns {Modifier|ChainableModifier} | |||
|      */ | |||
|     ensureModifier() { | |||
|         return this.toModifier(); | |||
|     } | |||
| } | |||
| /** | |||
|  * @inheritdoc | |||
|  * @extends ModificationSubChain | |||
|  * @abstract | |||
|  */ | |||
| class ModificationSubChainReComp extends ModificationSubChain { | |||
|     /** | |||
|      * | |||
|      * @param {ChainableModifier} modifier | |||
|      * @param {string} modMethod | |||
|      */ | |||
|     constructor(modifier) { | |||
|         super(); | |||
|         this._modifier = modifier; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Returns chain to the component that is under modification | |||
|      * @returns {Component} the Component that was (supposed to be) modified by this obj. | |||
|      */ | |||
|     toComponent() { | |||
|         return this._modifier[this._modMethod](this) | |||
|             .toComponent(); | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Returns chain to the component that is under modification | |||
|      * and adds the given innerComponent as children to the component. | |||
|      * @param {Component|Array<Component>} innerComponent | |||
|      * @returns {Component} | |||
|      */ | |||
|     childContext(innerComponent) { | |||
|         return this._modifier[this._modMethod](this) | |||
|             .toComponent() | |||
|             .childContext(innerComponent); | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Closes modifier chain and returns builder to define the next child of the component | |||
|      * @returns {Component} | |||
|      */ | |||
|     chainChild() { | |||
|         return this._modifier[this._modMethod](this) | |||
|             .toComponent() | |||
|             .chainChild(); | |||
|     } | |||
| } | |||
| 
 | |||
| @ -0,0 +1,466 @@ | |||
| /** | |||
|  * A chained class that sets most of the stylings of an element | |||
|  * Attributes: | |||
|  *  _modifications: {Object} | |||
|  */ | |||
| class Modifier { | |||
|     /** | |||
|      * @type {Map<string,string>} _modifications | |||
|      */ | |||
|     _modifications; | |||
|     /** | |||
|      * @type {Array<string>} | |||
|      */ | |||
|     _removeMods; | |||
|     /** | |||
|      * @type {Shape} | |||
|      */ | |||
|     _shape; | |||
|     /** | |||
|      * @type {Sides} paddingValues | |||
|      */ | |||
|     _paddingValues; | |||
| 
 | |||
|     constructor() { | |||
|         this._modifications = new Object(); | |||
|         this._removeMods = []; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Sets the modifications for widht and height to 100%.  | |||
|      * @returns {Modifier} this modifier object | |||
|      */ | |||
|     fillMaxSize(widthFraction = 1, heightFraction = 1) { | |||
|         return this.fillMaxWidth(widthFraction) | |||
|             .fillMaxHeight(heightFraction); | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Sets the modification for width to the given fraction of 1 (default 1 := 100%). | |||
|      * @param {number} fraction  | |||
|      * @returns {Modifier} this modifier object | |||
|     */ | |||
|     fillMaxWidth(fraction = 1) { | |||
|         this._modifications["width"] = (100 * fraction) + "%"; | |||
|         this._modifications["max-width"] = (100 * fraction) + "%"; | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Sets the modification for height to the given fraction of 1 (default 1 := 100%). | |||
|      * @param {number} fraction  | |||
|      * @returns {Modifier} this modifier object | |||
|      */ | |||
|     fillMaxHeight(fraction = 1) { | |||
|         this._modifications["height"] = (100 * fraction) + "%"; | |||
|         this._modifications["max-height"] = (100 * fraction) + "%"; | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {string} keyWord weither 'height' or 'width' that will be adjusted and looked for | |||
|      * @param {Sides} parentalPadding  | |||
|      */ | |||
|     _updateDimensionsBy(parentalPadding) { | |||
|         function updateDirection(keyWord, modifications, parentalAdjustment) { | |||
|             let refKeys = Object.keys(modifications) | |||
|                 .filter(k => k.includes(keyWord)); | |||
| 
 | |||
|             if (refKeys.length > 0) { | |||
|                 for (let i = 0; i < refKeys.length; i++) { | |||
|                     let key = refKeys[i]; | |||
|                     let value = modifications[key]; | |||
| 
 | |||
|                     if (key.includes("calc")) { | |||
|                         console.log( | |||
|                             `Modifier._updateByParent... ${keyWord | |||
|                             } - unexpected value '${value | |||
|                             }' for '${key | |||
|                             }', skipping...` | |||
|                         ); | |||
|                     } else { | |||
|                         let newValue = `calc(${value} - ${parentalAdjustment});`; | |||
|                         modifications[key] = newValue.trim(); | |||
|                     } | |||
|                 } | |||
|             } | |||
|             return modifications; | |||
|         } | |||
| 
 | |||
|         if (parentalPadding) { | |||
|             let pval = parentalPadding.getValues(); | |||
|             if (pval["horizontal"] > 0) { | |||
|                 this._modifications = updateDirection("width", this._modifications, pval["horizontal"] + parentalPadding._unit); | |||
|             } | |||
| 
 | |||
|             if (pval["vertical"] > 0) { | |||
|                 this._modifications = updateDirection("height", this._modifications, pval["vertical"] + parentalPadding._unit); | |||
|             } | |||
|         } | |||
| 
 | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Takes dimensions as param which can be either of three: | |||
|      *  | |||
|      * param == null (not given): | |||
|      * Returns new DimensionsChain (to chain Dimensions-Modifications) | |||
|      *  | |||
|      * param == number (>0): | |||
|      * Applies Dimensions.all(dimension); | |||
|      * Returns this modifier | |||
|      *  | |||
|      * param == Dimensions: | |||
|      * Sets modifications according to the dimensions object; | |||
|      * Returns this modifier | |||
|      * @param {Dimensions | number | undefined} [modify=null] modify as in modifiers  | |||
|      * @returns { Modifier | DimensionsChain } this modifier object or a DimensionsChain | |||
|      */ | |||
|     dimensions(modify = null) { | |||
|         if (modify instanceof Dimensions) { | |||
|             modify.toModifications() | |||
|                 .forEach(kvpair => { | |||
|                     this._modifications[kvpair.key] = kvpair.value; | |||
|                 }); | |||
|             return this; | |||
|         } else { | |||
|             let modSub = new DimensionsChain(this); | |||
|             if (Number.isInteger(modify) && modify > 0) { | |||
|                 return modSub.all(modify).ensureModifier(); | |||
|             } | |||
|             // case dimension is number but < 0 or dimensions == null or anything else
 | |||
|             return modSub; | |||
|         } | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Sets the padding on all sides according to the given padding object.  | |||
|      * Currently the padding will always be set  | |||
|      * to the most recent padding/padding.  | |||
|      * @param {Padding | number | undefined} modify as in modifiers | |||
|      * @returns {Modifier | PaddingChain} this modifier object | |||
|      */ | |||
|     padding(modify = null) { | |||
|         if (modify instanceof Sides && !(modify instanceof Padding)) { | |||
|             modify._modMethod = "padding"; | |||
|             modify = Object.assign(new Padding(), modify); | |||
|         } | |||
| 
 | |||
|         if (modify instanceof Padding) { | |||
|             modify.toModifications() | |||
|                 .forEach(kvpair => { | |||
|                     this._modifications[kvpair.key] = kvpair.value; | |||
|                 }); | |||
|         } else { | |||
|             let modSub = new PaddingChain(this); | |||
|             if (Number.isInteger(modify) && modify > -1) { | |||
|                 return modSub.all(modify).ensureModifier(); | |||
|             } | |||
|             return modSub | |||
|         } | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Sets the margin on all sides according to the given siding object.  | |||
|      * Currently the margin will always be set  | |||
|      * to the most recent margin/siding.  | |||
|      * @ATTENTION since it just increases complexity to constantly use padding and margin | |||
|      *            it is recommended to use padding and to stick to that as often as possible.  | |||
|      *            Padding values take affect inside/within the element. | |||
|      * @param {Margin | number | undefined} modify  | |||
|      * @returns {Modifier | MarginChain} this modifier object | |||
|      */ | |||
|     margin(modify = null) { | |||
|         if (modify instanceof Sides && !(modify instanceof Margin)) { | |||
|             modify._modMethod = "margin"; | |||
|             modify = Object.assign(new Margin(), modify); | |||
|         } | |||
| 
 | |||
|         if (modify instanceof Margin) { | |||
|             modify.toModifications() | |||
|                 .forEach(kvpair => { | |||
|                     this._modifications[kvpair.key] = kvpair.value; | |||
|                 }); | |||
|         } else { | |||
|             let modSub = new MarginChain(this); | |||
|             if (Number.isInteger(modify) && modify > -1) { | |||
|                 return modSub.all(modify).ensureModifier(); | |||
|             } | |||
|             return modSub | |||
|         } | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Sets the background-color as a rgb color. | |||
|      * If no color is given/specified the styling will be set to "inherit"  | |||
|      * and use the color setting from (one of) the parent. | |||
|      * @param {Color} color  | |||
|      * @returns {Modifier} this modifier object | |||
|      */ | |||
|     background(color) { | |||
|         if (color) { | |||
|             if (color._hex) { | |||
|                 this._modifications["background-color"] = color._hex; | |||
|             } else { | |||
|                 this._modifications["background-color"] = color.cssRGBString(); | |||
|             } | |||
|         } else { | |||
|             this._modifications["background-color"] = "inherit"; | |||
|         } | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Sets the color as a rgb color.  | |||
|      * If no color is given/specified the styling will be set to "inherit"  | |||
|      * and use the color setting from (one of) the parent. | |||
|      * @param {Color} color  | |||
|      * @returns {Modifier} this modifier object | |||
|      */ | |||
|     color(color) { | |||
|         this._modifications["color"] = ( | |||
|             color | |||
|                 ? color.cssRGBString() | |||
|                 : "inherit" | |||
|         ); | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Adds the modifications of the given Modifier to current Modifier. | |||
|      * This is especailly used in the cases of extending existing/pre defined  | |||
|      * Components. | |||
|      *  | |||
|      * CAUTION matching existing modifications will be overwritten.  | |||
|      *  | |||
|      * @todo finish second parameter "modifications" - logic | |||
|      *  | |||
|      * @param modifier The "new" Modifier | |||
|      * @returns {Modifier} The "old/current" Modifier,  | |||
|      * extended with the modifications of the given Modifier. | |||
|      */ | |||
|     join(modifier, modifications = {}) { | |||
|         let keys = Object.keys(modifier.ensureModifier()._modifications); | |||
|         for (let i = 0; i < keys.length; i++) { | |||
|             /* if (!this._modifications.hasOwnProperty(keys[i])) */ | |||
|             this._modifications[keys[i]] = modifier.ensureModifier()._modifications[keys[i]]; | |||
|         } | |||
|         let removeMods = modifier.ensureModifier()._removeMods; | |||
|         if (removeMods.length > 0) { | |||
|             for (let i = 0; i < removeMods.length; i++) { | |||
|                 delete this._modifications[removeMods[i]]; | |||
|             } | |||
|         } | |||
|         if (modifier._paddingValues) { | |||
|             this._paddingValues = modifier._paddingValues; | |||
|         } | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {string} key a css style rule | |||
|      * @param {string} value the corresponding value to the css style rule | |||
|      * @returns {Modifier} this modifier object | |||
|      */ | |||
|     setStyleRule(key, value) { | |||
|         this._modifications[key] = value; | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Sets the style rules to deactivate text-selection in the component. | |||
|      * @returns {Modifier} | |||
|      */ | |||
|     noneSelect() { | |||
|         return this | |||
|             .setStyleRule("user-select", "none") | |||
|             .setStyleRule("-ms-user-select", "none") | |||
|             .setStyleRule("-webkit-user-select", "none") | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {StylePropertyMap} rulemap  | |||
|      * @returns {Modifier} | |||
|      */ | |||
|     addStyleRuleMap(rulemap) { | |||
|         for (const ruleKey of Object.keys(rulemap)) { | |||
|             this._modifications[ruleKey] = rulemap[ruleKey]; | |||
|         } | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @param {string} key  | |||
|      * @returns {Modifier} this modifier object  | |||
|      */ | |||
|     removeStyleRule(key) { | |||
|         this._removeMods.push(key); | |||
|         if (Object.keys(this._modifications).includes(key)) { | |||
|             delete this._modifications[key]; | |||
|         } | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Takes border as param which can be either of three: | |||
|      *  | |||
|      * param == null (not given): | |||
|      * Returns new BorderChain (to chain Border-Modifications) | |||
|      *  | |||
|      * param == number (>0): | |||
|      * Applies Border.width(border); | |||
|      * Rerturns this modifier | |||
|      *  | |||
|      * param == Border: | |||
|      *  | |||
|      * Sets a border line (with given linestyle) to all sides.  | |||
|      * If lineStyle is an array, the containing LineStyles,  | |||
|      * are applied in the order: [top, right, bottom, left]. | |||
|      * If the border has a shape defined,  | |||
|      * this shape will override earlier shape definitions. | |||
|      * Otherwise existing shape definitions will be applied; | |||
|      *  | |||
|      * Rerturns this modifier | |||
|      *  | |||
|      * @param {Border | number | undefined} [modify=null] modify as in modifiers  | |||
|      * @returns {Modifier | BorderChain} this modifier or BorderChain | |||
|      */ | |||
|     border(modify = null) { | |||
|         if (modify instanceof Border) { | |||
|             if (modify._shape) { | |||
|                 this.clip(modify._shape); | |||
|             } else if (this._shape) { | |||
|                 modify._shape = this._shape; | |||
|             } | |||
| 
 | |||
|             modify.toModifications() | |||
|                 .forEach(e => this._modifications[e.key] = e.value); | |||
| 
 | |||
|         } else { | |||
|             let modSub = new BorderChain(this); | |||
|             if (Number.isInteger(modify) && modify > 0) { | |||
|                 return modSub.width(modify).ensureModifier(); | |||
|             } | |||
|             return modSub; | |||
|         } | |||
|         return this; | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * Takes shape as param which can be either of three: | |||
|      *  | |||
|      * param == null (not given): | |||
|      * Returns new ShapeChain (to chain Shape-Modifications) | |||
|      *  | |||
|      * param == number (>0): | |||
|      * Applies Shape.all(dimension); | |||
|      * Returns this modifier | |||
|      *  | |||
|      * param == Shape: | |||
|      * Sets modifications according to the shape object; | |||
|      * Returns this modifier | |||
|      *  | |||
|      *  | |||
|      * @param {Shape | number | undefined} [modify=null] modify as in modifiers  | |||
|      * @returns { Modifier | ShapeChain }  | |||
|      */ | |||
|     clip(modify = null) { | |||
|         if (modify instanceof Shape) { | |||
|             this._shape = modify; | |||
|             this._modifications["border-radius"] = modify.getOrderedValues().join(' '); | |||
|             return this; | |||
|         } else { | |||
|             let modSub = new ShapeChain(this); | |||
|             if (Number.isInteger(modify) && modify > 0) { | |||
|                 return modSub.all(modify).ensureModifier(); | |||
|             } | |||
|             return modSub; | |||
|         } | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * @deprecated use Modifier.dimensions() instead | |||
|      * @param {number} size of width and height in pixels | |||
|      * @returns {DimensionsChain}  | |||
|      */ | |||
|     linkDimensions(size = -1) { | |||
|         if (size === -1) { | |||
|             return new DimensionsChainedModifier(this); | |||
|         } else { | |||
|             return new DimensionsChain(this).all(size).ensureModifier() | |||
|         } | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * @deprecated use Modifier.padding() instead | |||
|      * @param {number} amount the padding for all four sides | |||
|      * @returns {PaddingChain} | |||
|      */ | |||
|     linkPadding(amount = -1) { | |||
|         if (amount === -1) { | |||
|             return new PaddingChainedModifier(this); | |||
|         } else { | |||
|             return new PaddingChain(this).all(amount); | |||
|         } | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * @deprecated use Modifier.margin() instead | |||
|      * @ATTENTION since it just increases complexity to constantly use padding and margin | |||
|      *            it is recommended to use padding and to stick to that as often as possible.  | |||
|      *            Padding values take affect inside/within the element. | |||
|      * @param {number} amount the padding for all four sides | |||
|      * @returns {PaddingChain}  | |||
|      */ | |||
|     linkMargin(amount = -1) { | |||
|         if (amount === -1) { | |||
|             return new MarginChainedModifier(this); | |||
|         } else { | |||
|             return new MarginChain(this).all(amount); | |||
|         } | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * @deprecated use Modifier.clip() instead | |||
|      * @param {number} cornerRadius will create a rounded rectangle with the given cornerRadius | |||
|      * @returns {ShapeChain} | |||
|      */ | |||
|     linkClip(cornerRadius = -1) { | |||
|         if (cornerRadius === -1) { | |||
|             return new ShapeChainedModifier(this); | |||
|         } else { | |||
|             return new ShapeChain(this).all(cornerRadius); | |||
|         } | |||
|     } | |||
| 
 | |||
|     /** | |||
|      * @deprecated use Modifier.border() instead | |||
|      * @param {number} borderWidth sets the width of all four border sides | |||
|      * @returns {BorderChain} | |||
|      */ | |||
|     linkBorder(borderWidth = -1) { | |||
|         if (borderWidth === -1) { | |||
|             return new BorderChainedModifier(this); | |||
|         } else { | |||
|             return new BorderChain(this).width(borderWidth); | |||
|         } | |||
|     } | |||
| 
 | |||
|     /** | |||
|      *  | |||
|      * @returns {Modifier} | |||
|      */ | |||
|     ensureModifier() { | |||
|         return this; | |||
|     } | |||
| 
 | |||
| } | |||
| 
 | |||
| 
 | |||
| @ -0,0 +1,87 @@ | |||
| /** | |||
|  * Function is a mixin to add endings to a Modifier SubChain. | |||
|  * Explicitly used for "link..." methods called from a Modifier, | |||
|  * that is unaware of its component.  | |||
|  *  | |||
|  * @param {typeof DirectionUnitDependentAttribute} classToExtend  | |||
|  * @returns {typeof ModificationSubChain} | |||
|  */ | |||
| function mixinModSubChainEndings(classToExtend) { | |||
|     return class extends classToExtend { | |||
|         /** | |||
|          * @type {Modifier} | |||
|          */ | |||
|         _modifier; | |||
| 
 | |||
|         /** | |||
|          *  | |||
|          * @param {Modifier} modifier  | |||
|          * @param {string} modMethod  | |||
|          */ | |||
|         constructor(modifier) { | |||
|             super(); | |||
|             this._modifier = modifier; | |||
|         } | |||
| 
 | |||
|         /** | |||
|          * Returns the Modifier SubChain to the Modifier | |||
|          * @returns {Modifier|ChainableModifier} | |||
|          */ | |||
|         toModifier() { | |||
|             let tmp = this._modifier[this._modMethod]; | |||
|             return this._modifier[this._modMethod](this); | |||
|         } | |||
| 
 | |||
|         /** | |||
|          * Returns chain to the Modifier | |||
|          * @returns {Modifier|ChainableModifier} | |||
|          */ | |||
|         ensureModifier() { | |||
|             return this.toModifier() | |||
|         } | |||
|     } | |||
| } | |||
| 
 | |||
| 
 | |||
| /** | |||
|  *  | |||
|  * Function is a mixin to add "return" methods to a Modifier SubChain. | |||
|  * Explicitly used for "link..." methods called from a ChainableModifier. | |||
|  *  | |||
|  * @param {typeof DirectionUnitDependentAttribute} classToExtend  | |||
|  * @returns {typeof ModificationSubChainReComp} | |||
|  */ | |||
| function mixinModSubChainComponentMethods(classToExtend) { | |||
|     return class extends mixinModSubChainEndings(classToExtend) { | |||
|         /** | |||
|          * Returns chain to the component that is under modification | |||
|          * @returns {Component} the Component that was (supposed to be) modified by this obj. | |||
|          */ | |||
|         toComponent() { | |||
|             return this._modifier[this._modMethod](this) | |||
|                 .toComponent(); | |||
|         } | |||
| 
 | |||
|         /** | |||
|          * Returns chain to the component that is under modification | |||
|          * and adds the given innerComponent as children to the component. | |||
|          * @param {Component|Array<Component>} innerComponent  | |||
|          * @returns {Component} | |||
|          */ | |||
|         childContext(innerComponent) { | |||
|             return this._modifier[this._modMethod](this) | |||
|                 .toComponent() | |||
|                 .childContext(innerComponent); | |||
|         } | |||
| 
 | |||
|         /** | |||
|          * Closes modifier chain and returns builder to define the next child of the component | |||
|          * @returns {Component} | |||
|          */ | |||
|         chainChild() { | |||
|             return this._modifier[this._modMethod](this) | |||
|                 .toComponent() | |||
|                 .chainChild(); | |||
|         } | |||
|     } | |||
| } | |||
| @ -0,0 +1,32 @@ | |||
| # Upcoming Features/Changes | |||
| 
 | |||
| - [ ] extend generator logic/possibilities (consider extensions logic) | |||
|   - [ ] [browserify](https://browserify.org/)? | |||
|   - [ ] potential frameworks | |||
|     - [ ] [alpinejs](https://alpinejs.dev/) | |||
|     - [ ] [lit](https://lit.dev/) | |||
|     - [ ] [htmx](https://htmx.org/) | |||
|     - [ ] [www](https://www.solidjs.com/) | |||
|     - [ ] [nuxt](https://nuxt.com/) | |||
|     - [ ] [vuejs](https://vuejs.org/) | |||
|   - [ ] externalize generator and corresponding functions | |||
|     - [ ] make the generator modular | |||
|     - [ ] include several generator-options into frameworkConsole | |||
|     - [ ] (re-) enable "packageWithoutFramework" | |||
|     - [x] check/refactor/-implement the extStorage logic | |||
| - [ ] check naming and setup of PageBuilder-class | |||
| - [ ] move builder.page to PageBuilderdd | |||
| - [ ] Re-Implementation Alignment and Arrangement | |||
|   - [ ] Introducing differentiation between element and text | |||
|   - [ ] Externalize Alignment and Arrangement from component | |||
|   - [ ] Check for inheritence of Arrange-, allignment for children | |||
| - [ ] unify data-compel attribute logic | |||
|   - [ ] naming(s) of attributes and values | |||
|   - [ ] functionallity | |||
|   - [ ] the autonaming and setting compel-name  | |||
|     - [ ] usage of the name in functions (selector)s | |||
| - [ ] Check Sides, Padding, Margin etc. logic for consistency | |||
| - [ ] Check builder.asComponent and similar functions for logical consistency | |||
| - [ ] reevaluate compelgroups logic | |||
|   - [ ] Common compel groups in context.js | |||
| - [ ] combine changelog and upcoming | |||
					Loading…
					
					
				
		Reference in new issue