Compare commits

...

131 Commits

Author SHA1 Message Date
chris c0171aa8d8 IMPRO,FEAT: added modifyChildren 2 months ago
chris 6933a89111 IMPRO,REFA,FEAT: extended distributeSpacingEvengly 2 months ago
chris 7a40c27712 REFA,IMPRO: select elements are InputComponents as well 2 months ago
chris 6de0aca195 REFA,FIX,IMPRO: changed default behaviour of text() 2 months ago
chris 02203a5dd0 CHORE: updated package version corresponding with git 2 months ago
chris a36190bde9 CHORE: updated package-lock.json 2 months ago
chris 41d71f1523 CHORE: fix dupplication of devdependencies introduced by git 2 months ago
chris 1a51451e82 DOC,REFA,LINT: implemented first couple of lint issues 2 months ago
chris 9fb347421d CHORE,REFA: introduced linter 2 months ago
chris 362d9f870c CHORE,REFA: introduced linter 2 months ago
chris cf6438d623 CHORE: updated version reference in package.json 2 months ago
chris c0b43e38d1 CHORE: adjusted licence to include exclusion of waranty 2 months ago
chris d361a1bbee FEAT,REFA: externalized extension base class 2 months ago
chris c25f9bb93e FEAT: added noselect modifier 2 months ago
chris aa65dc1e54 FIX,IMPRO,REFA: overriding all function for border 2 months ago
chris 5dab23dc14 FEAT,FIX,IMPRO,REFA: reworked overflow behaviour 2 months ago
chris 46f12f517d FIX,REFA: handling parameter given as string or function 2 months ago
chris ab4d39d55d IMPRO,MINOR,FIX: changed replacement order of empty string characters 2 months ago
chris 0496bb0295 FIX,IMPRO,REFA: removed shape invokation on border constructor 2 months ago
chris d578a56269 MINOR,FIX: lookup for existing key in object 2 months ago
chris 61a32a7bfe MINOR,IMPRO,FIX,REFA: changed to map approach 2 months ago
chris 27cc2d930d FIX: entries is a function of a map 2 months ago
chris 73c85f4d2e FIX: wrongfully created a dependend call loop 2 months ago
chris 025573a16c FIX: default values for parameters were wrongfully provided 2 months ago
chris 467fca6adf CHORE: extend npm module with exports attemt to include module variant 3 months ago
chris 819ad6bee0 CHORE: update npm run generate call 3 months ago
chris e65c2f96cd REFA,IMPRO: local custom bundler generates vainlla and module 3 months ago
chris 3608ae5cf0 FIX,FEAT-BUNDLING: ChainableModifier was missing 3 months ago
chris 2b50ed88d1 FIX: generator used this (legacy from component) 3 months ago
chris 60776d6fc3 DOC,REFA: unified used "imports", updated usage of deprecated methods 3 months ago
chris b9a92a4df2 MAJOR,REFA,IMPL: updated files for current bundling process 3 months ago
chris 74974f6c80 REFA: preparations for jpclw-bundler 3 months ago
chris 92a86aa880 CHORE: Removing name form disclaimer 3 months ago
chris fc1dfd7e21 REFA: renamed files to represent classes directly 3 months ago
chris 9f374eeaa4 IMPRO: default generator 3 months ago
chris 20710fc461 MAJOR,IMPL,FEAT,REFA: generator 3 months ago
chris db5750ca16 REFA: changed extStore setup 3 months ago
chris 0110253876 REFA: moved executeStoreTypeCollectedTriple function 3 months ago
chris e495aa72ad FIX: setFunctionsExtStorage setting property 3 months ago
chris 439a98eb33 REFA,DOC: Added Doc removed empty lines 3 months ago
chris 70e5d0d66f REFA: renamed properties of WebTrinity 3 months ago
chris 60faa1a062 MAJOR,REFA: extracted TwoDimPoint class in own file 3 months ago
chris e539ace5a0 DRAFT,IMPL,DOC: changed loop approaches 3 months ago
chris 23e30b8ffb MINOR,FEAT,REFA,DOC: first implementation of generator class 3 months ago
chris 8d6eb00ae4 MAJOR,REFA: moved context.js into corresponding module 3 months ago
chris c2a34dd540 MAJOR,REFA: moved base types into sepperate module 3 months ago
chris 12f52feca8 MAJOR,REFA: moved event/behaviour modifiers to handlers module 3 months ago
chris 89bc8106c9 MAJOR,REFA: seperated and moved baseCompontent 3 months ago
chris c34114c176 MAJOR,REFA: Moved style-modifiers into decorators dir 3 months ago
chris 6ff45892cb REFA: removing empty lines at begin of files (and doc if redundant) 3 months ago
chris 6f186b64d8 MAJOR,REFA: moved component and ancestry into module 3 months ago
chris dd3d986485 MAJOR,REFA: moved modifiers to decorators 3 months ago
chris d9cef5210d MAJOR,REFA: moved helpers into module out of context 3 months ago
chris 26becc9431 MAJOR,REFA: moved twoDimPoint to helpers instead 3 months ago
chris 3afeb8a5e6 MAJOR,REFA: Moved/Renamed modifier extensions into "module" dir 3 months ago
chris 0209c1012f MAJOR,REFA: externalized modifier extension classes 3 months ago
chris 1b81c4df0e REFA,CHORE: add module type generated bundle to gitignore 3 months ago
chris e4e4c60f27 REFA,CHORE: introducing module bundling 3 months ago
chris b53ffc3571 REFA, IMPRO: removing the property "_wenity" 3 months ago
chris eca487c2fe CHORE,REFA: removed copyright disclaimer from 'every' file 3 months ago
chris e0fbe8bd96 CHORE,DOC: some doc added, removed unused dependencies 3 months ago
chris c228b251b2 MINOR,FEAT: return parent if child is undefined to allow empty method stubs 3 months ago
chris 208481a867 FIX,DOC: adjusted return types and fixed integer identification 3 months ago
chris aada47b8e0 FEAT,MAJOR,REFA,FIX: unified logic of DirectionUnitDependentAttributes 3 months ago
chris 512551c4f4 FEAT: chainChild - chain of single elements 3 months ago
chris 578d26c2e5 MINOR,FEAT,REFA: introduced modifier passing to generator 3 months ago
chris 9f46eaba1a REFA: moved twoDimPoint and helpers into own file 3 months ago
chris 40d2546b36 MINOR,FEAT: to execute funciton on an array or a single value 3 months ago
chris 6ccee1914c CHORE: renamed module name in package.json 3 months ago
chris 8b81393131 CHORE: updating package.json 3 months ago
chris e88623a3f5 CHORE,GIT,FIX 3 months ago
chris 614ce95a2b MINOR,REFA: changed setup of framework control pane 3 months ago
chris 65b3debb9e MINOR,REFA: setting page title postponed to generate 3 months ago
chris 653e82e6b6 REFA: added _groups variable 3 months ago
chris 1dd90bd242 CHORE: updated upcoming 3 months ago
chris a30941888f REFA: extended usage of _hex variable 3 months ago
chris ac7bb1e1ff MINOR,FEAT: extended extension logic 3 months ago
chris 752ca8435b REFA: changed from classical object to map approach 3 months ago
chris df8ceb0bd8 MINOR,REFA,FEAT: added children and store variables 3 months ago
chris 90d446adb2 MINOR,FEAT: changed isCompel variable to make it accessible 3 months ago
chris 767893d9f9 MINOR,FEAT: refactored and reintroduced distributeEvenly function 3 months ago
chris 9557335694 MINOR,FEAT: created convenience functions in builder for dev 3 months ago
chris 430411ff7f REFA,MINOR: movement of helper functions into helperFun object 3 months ago
chris 42ad432bdb REFA,MINOR,FEAT: refactored Sides inheritance, introduced MarginChain 3 months ago
chris d8170fad15 REFA,MINOR,FEAT: Added more logic depending on higher-component. 3 months ago
chris d5c4865ecf MINOR,FEAT: Extended and unified methods -naming of TDP class 3 months ago
chris 626f7ac1ac MINOR,FEAT: minor improvements on FlexContainerComponent-logic 3 months ago
chris 51a4da6638 DOC,FEAT: extended "extensions" with helper functions 3 months ago
chris 7ff88c0e66 DOC: InputComponent class extending Component 5 months ago
chris 76fac29dd6 DOC: Introduced changelog and upcoming changes 5 months ago
chris 646afba4d3 FIX,REFA: extStore handling during generate and appendToPage 5 months ago
chris ea9261446d DOC: Added copyright, minor functional, README 5 months ago
chris e0a60593d2 CHORE: adjusted "build" script 5 months ago
chris 882303a5fa FEAT,REFA: extensions logic and management console 5 months ago
chris eae24e4bd5 DOC: Added or modified some doc 5 months ago
chris a0c6f68e23 REFA,FEAT: Contextmenu behaviour 5 months ago
chris 27d0635119 REFA,FEAT: Introduced DragAndDropImplementation as behaviour parameterr for components 5 months ago
chris 0ba13cca94 REFA,FEAT: Reintroduced and reintroduced ExtStorage and corresponding enums, functions and so on 5 months ago
chris e0046c6aa8 MINOR,HELPER: added default compel groups 5 months ago
chris 12bf468e22 FIX: Border Color value and component modifier setup during generate 5 months ago
chris 89207c163f HELPER,DOC,REFA: Added several helper methods 5 months ago
chris 170d00d852 REFA: Moved functions to their logical position 5 months ago
chris 4479c92bb4 MINOR,FEAT: Adjusted the component container logic 5 months ago
chris b7fff8604e REFA: Moved alignment and arrangement setting 5 months ago
chris a7fcb5a3ca REFA,RENAME,DOC: Renamed several functions for readability 5 months ago
chris 4b6f4006d9 MINOR,FEAT: Introduced default parameter "modifier" 5 months ago
chris 9447216e53 REFA,IMRO: Extended auto-subscriptions of components in compelgroups 5 months ago
chris f41e7f1384 REFA: provided an HTMLElement ToggleVisibility 5 months ago
chris c4b0fc9162 FEAT,MINOR: (Re- ?) Introduced component subscription 5 months ago
chris 5e782912f7 FIX,FEAT: Drag and Drop basic implementation 5 months ago
chris 1632528322 MINOR,FEAT,HELPERS,CONVINENCE: Couple functions/changes 5 months ago
chris b60b89cbea FEAT,MODIFY,FIX: ExtStore functionallity and parent-dependent-sizing 5 months ago
chris 0cc60ad07c MINOR,DOC: Added convenience functions to the Shape class and fixed/changed some doc 5 months ago
chris 771a05dba8 MINOR,FIX: In established functionallities 5 months ago
chris f9e7e188cb FIX: changed from !(Object) to ===null comparison 5 months ago
chris 02f72d4648 FEAT: DragAndDrop in a basic implementation 5 months ago
chris bb0ccb8904 FEAT: extended Siding added some constants 5 months ago
chris 6f78fa1cfb FEAT: ContextMenu 5 months ago
chris 3782344e1f FEAT: added hidden() and overflow() functions 5 months ago
chris 5b74731cf1 REFA,STEP,FEAT: added style remove functionallity and refactored inputTag-organisation in builder 5 months ago
chris 94ef6186cf REFA,STEP,DOC,FIX: some docs and obvious errors 5 months ago
chris 3b8efc9a6d REFA,STEP,UNSTABLE: reestablished generate 5 months ago
chris 0c51679e04 REFA,UNSTABLE,STEP: Removing old artifacts/fragments of earlier implementations 5 months ago
chris ec298757c2 STEP,UNSTABLE,REFA: Restructured organization of Component and its ancestry 5 months ago
chris 8121c9ec6a TYPO: added ; and (empty) dependency block 7 months ago
chris 9d073a8367 MINOR,FEAT: Added inputtags with predefined types to builder 7 months ago
chris cdc1188061 FEAT: Introduced first version of Scrollable and DragAndDrop 7 months ago
chris ea1786f379 FIX,IMPRO,REFA: Fixed framework after refactoring and -structuring 7 months ago
chris 4a0bd90799 MAJOR,REFA,IMPRO,CHANGE: Introducing extensive usage of Inheritance 7 months ago
chris 95d0649169 DOC,LICENCE,COPYRIGHT: Updated copyright comment in files-doc 7 months ago
chris fc2c6b15c0 MINOR,REFA,DOC,INDENT: Autoformatted code, added some doc 7 months ago
  1. 2
      .gitignore
  2. 0
      CHANGELOG.md
  3. 26
      LICENCE.md
  4. 24
      README.md
  5. 15
      eslint.config.mjs
  6. 193
      generate_single_file.js
  7. 25
      join_js_files.sh
  8. 695
      jpclw-bundle.json
  9. 2156
      package-lock.json
  10. 31
      package.json
  11. 19
      src/arrangement.js
  12. 660
      src/base/extStore.js
  13. 49
      src/base/webTrinity.js
  14. 106
      src/baseComponents.js
  15. 408
      src/builder.js
  16. 15
      src/commonEvents.js
  17. 246
      src/component.js
  18. 234
      src/component/ChildbearerComponent.js
  19. 19
      src/component/Column.js
  20. 329
      src/component/Component.js
  21. 173
      src/component/FlexContainerComponent.js
  22. 69
      src/component/InputComponent.js
  23. 46
      src/component/Row.js
  24. 194
      src/component/StyleAndScriptStoringComponent.js
  25. 53
      src/component/modifiableComponent.js
  26. 177
      src/context.js
  27. 196
      src/context/context.js
  28. 61
      src/context/framework-controls.js
  29. 197
      src/context/scriptAndStyleContext.js
  30. 7
      src/decorators/alignment.js
  31. 11
      src/decorators/arrangement.js
  32. 147
      src/decorators/border.js
  33. 18
      src/decorators/color.js
  34. 95
      src/decorators/dimensions.js
  35. 53
      src/decorators/margin.js
  36. 44
      src/decorators/padding.js
  37. 127
      src/decorators/shapes.js
  38. 167
      src/decorators/siding.js
  39. 1
      src/generators/defaultGenerators.js
  40. 313
      src/generators/generator.js
  41. 14
      src/handlers/commonEvents.js
  42. 63
      src/handlers/contextMenu.js
  43. 123
      src/handlers/dragAndDrop.js
  44. 55
      src/helper/ObjectAccessObject.js
  45. 101
      src/helper/general.js
  46. 84
      src/helper/indices.js
  47. 348
      src/helper/twoDimPoint.js
  48. 276
      src/modifier.js
  49. 187
      src/modifier/ChainableModifier.js
  50. 87
      src/modifier/ModificationSubChain.js
  51. 466
      src/modifier/Modifier.js
  52. 87
      src/modifier/mixinModSubChain.js
  53. 32
      upcoming.md

2
.gitignore

@ -1,3 +1,5 @@
node_modules/
samples/*/
jpc-like-websites.js
jpc-like-websites.mjs
extensions/

0
CHANGELOG.md

26
LICENCE.md

@ -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.

24
README.md

@ -1,7 +1,7 @@
# jpc-like-websites
> Jetpack Compose like websites
The building method of Jetpack Compose (for android),
The building method of Jetpack Compose (for e.g. android),
feels really straight forward
and intuitive for me.
Furthermore I personally found it more readable
@ -13,20 +13,28 @@ to build those websites more or less fast (easy)
and reliable.
While they do look the way I intend them to.
After developing some minor apps with Jetpack Compose I liked a lot about the way
the components are build/structured.
After developing some minor apps with Jetpack Compose,
I liked a lot about the way the components are build/structured.
The reusability and so on...
Further I love to write code in a "method-chaning" kind of way:
Every line is a chain-link concerning one particular task,
the IDE offers an overview of possibilities.
Method-chaining is more or less native in Kotlin.
Logical support for git-line-wise change detection.
It makes even more sence concidering the git-line-wise change detection.
After some (very little) searching,
I choose to give it a couple of hours
and develop a small "lib" to enable myself to do exactly that.
> Develop jetpack compose like components in html/javascript.
> Develop html/javascript components in a "jetpack compose like" manner.
BUT it is not inteded to be or do more than that.
The lib/framework is supposed to help to build/define/construct components
or furhter single page applications.
It is not meant to be a framework like react, vue, angular ... the list goes on.
The closest comparison is actually typescript,
it is supposed to build html, js, and css.
If desired, it can generate the entire page (html-file)
without the framework itself.
Javascript is by far not my strongest field,
typescript even less.
@ -36,4 +44,6 @@ you have been warned.
```sh
sh join_js_files.sh
```
```
https://eslint.org/docs/latest/use/getting-started

15
eslint.config.mjs

@ -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"] },
]);

193
generate_single_file.js

@ -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}`);
}
}
/**
*
* @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]);

25
join_js_files.sh

@ -7,28 +7,5 @@ SRC="src"
# Third "HIGHER_LIST" come several of the commons, the context as well as component
# and thoose that use component.
SUB_LIST="siding.js shapes.js border.js dimensions.js"
MODIFIERS_LIST="alignment.js arrangement.js modifier.js"
HIGHER_LIST="commonEvents.js context.js component.js baseComponents.js builder.js"
echo "" > $TARGET
echo "/* ## color.js ## */" >> $TARGET
cat $SRC/color.js >> $TARGET
echo "/* # SUB_LIST # */" >> $TARGET
for i in $SUB_LIST; do
echo "/* ## $i ## */" >> $TARGET
cat $SRC/sizeSide/$i >> $TARGET
done
echo "/* # MODIFIERS_LIST # */" >> $TARGET
for i in $MODIFIERS_LIST; do
echo "/* ## $i ## */" >> $TARGET
cat $SRC/$i >> $TARGET
done
echo "/* # HIGHER_LIST # */" >> $TARGET
for i in $HIGHER_LIST; do
echo "/* ## $i ## */" >> $TARGET
cat $SRC/$i >> $TARGET
done
node generate_single_file.js

695
jpclw-bundle.json

@ -0,0 +1,695 @@
{
"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"
],
"extensions": [
"extension.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",
"extension.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"
]
},
"extension.js": {
"folder": "src/extensions",
"name": "extension.js"
},
"simplePagingAndNavigation.js": {
"folder": "src/extensions",
"name": "simplePagingAndNavigation.js"
},
"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"
]
}
}
}
}

2156
package-lock.json

File diff suppressed because it is too large

31
package.json

@ -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",
"generate": "node generate_single_file.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"
}
}
}

19
src/arrangement.js

@ -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",
})

660
src/base/extStore.js

@ -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) }
]);
}

49
src/base/webTrinity.js

@ -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);
}
}

106
src/baseComponents.js

@ -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;
}
}

408
src/builder.js

@ -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
*/
/**
* Method Collection with predefined HTMLElements
*/
@ -12,215 +5,506 @@ const builder = {
components: {
parent: {},
current: {},
previous: {},
previous: null,
next: {},
openedChain: {}
},
/**
* Convenience function for the ChildbearerComponent.chainChild() method.
* @param {Component} callComponent
* @returns {builder}
*/
_nextComponent(callComponent) {
this.components.previous = callComponent;
return this;
},
/**
* @property {Function(Component|*): Component}
*/
extensions: {
/**
*
* @param {Component|*} extensionComponent
* @returns {Component}
*/
reComp: function (extensionComponent) {
return extensionComponent;
},
/**
*
* @param {Component|FlexContainerComponent|*} extensionComponent
* @returns {FlexContainerComponent}
*/
reFlexComp: function (extensionComponent) {
return extensionComponent;
}
},
/**
* Pure convenience, mostly to enable jdocs for builder.extensions
* @param {*} unrecognizedComponent
* @returns {Component}
*/
asComponent(unrecognizedComponent) {
if (unrecognizedComponent instanceof Component) {
console.log("Recognized as Component");
return unrecognizedComponent;
} else if (unrecognizedComponent instanceof Modifier) {
return unrecognizedComponent.toComponent()
}
console.log("NOT Recognized");
return unrecognizedComponent;
},
/**
* Pure convenience, mostly to enable jdocs for builder.extensions
* in this case if there is an actual difference as the element being
* a FlexContainerComponent
* @param {*} unrecognizedComponent
* @returns {FlexContainerComponent}
*/
asFlexContainerComponent(unrecognizedComponent) {
if (unrecognizedComponent instanceof FlexContainerComponent) {
console.log("Recognized as FlexContainerComponent");
return unrecognizedComponent;
} else if (unrecognizedComponent instanceof Modifier) {
return unrecognizedComponent.toComponent()
}
console.log("NOT Recognized");
return unrecognizedComponent;
},
/**
* Pure convenience, mostly to enable jdocs for builder.extensions
* in this case if there is an actual difference as the element being
* an InputComponent
* @param {*} unrecognizedComponent
* @returns {InputComponent}
*/
asInputComponent(unrecognizedComponent) {
if (unrecognizedComponent instanceof InputComponent) {
console.log("Recognized as InputComponent");
return unrecognizedComponent;
} else if (unrecognizedComponent instanceof Modifier) {
return unrecognizedComponent.toComponent()
}
console.log("NOT Recognized");
return unrecognizedComponent;
},
/**
*
* @param {string} htmlText
* @param {Modifier} modifier
* @returns {Component}
*/
componentFromHTML: function (htmlText, modifier = null) {
/**
* @type {Component}
*/
let compel = new Component(new DOMParser().parseFromString(htmlText, "text/html"));
if (modifier) {
return compel.modifier(modifier);
}
return compel;
},
/**
*
* @param {HTMLElement} element
* @param {Modifier} modifier
* @returns {Component}
*/
componentFromHTMLElement: function (element, modifier = null) {
let newCompel = new Component(element);
if (modifier) {
return newCompel.modifier(modifier);
}
return newCompel;
},
/**
*
* @param {string} tag
* @param {Map<string,string>} attr
* @param {Modifier} modifier
* @returns {Component}
*/
genTag: function (tag, attr = {}) { return new Component(document.createElement(tag), attr).addStyleClass(`el-${tag}`); },
genTag: function (tag, attr = {}, modifier = null) {
let compel = new Component(document.createElement(tag), attr).addStyleClass(`el-${tag}`);
if (modifier) {
return compel.modifier(modifier);
}
if (this.components.previous) {
compel._parentComponent = this.components.previous;
this.components.previous = null;
}
return compel;
},
/**
*
* @param {Map<string,string>} attr
* @param {Modifier} modifier
* @returns {Component}
*/
anchor: function (attr = {}) { return builder.genTag("a", attr); },
anchor: function (attr = {}, modifier = null) { return builder.genTag("a", attr, modifier); },
/**
*
* @param {Map<string,string>} attr
* @param {Modifier} modifier
* @returns {Component}
*/
label: function (attr = {}) { return builder.genTag("label", attr); },
label: function (attr = {}, modifier = null) { return builder.genTag("label", attr, modifier); },
/**
*
* @param {Map<string,string>} attr
* @param {Modifier} modifier
* @returns {Component}
*/
button: function (attr = {}) { return builder.genTag("button", attr); },
button: function (attr = {}, modifier = null) { return builder.genTag("button", attr, modifier); },
/**
*
* @param {InputTypes|string} type
* @param {Map<string,string>} attr
* @returns {Component}
* @param {Modifier} modifier
* @returns {InputComponent}
*/
input: function (attr = {}) { return builder.genTag("input", attr); },
input: function (type, attr = {}, modifier = null) {
let comp = new InputComponent(
document.createElement("input"),
Object.assign({ "type": (type || "text") }, attr),
modifier
)
.addStyleClass(`el-input`);
if (this.components.previous) {
comp._parentComponent = this.components.previous;
this.components.previous = null;
}
return comp;
},
/**
*
* @param {Map<string,string>} attr
* @returns {Component}
* @param {Modifier} modifier
* @returns {function(Object): InputComponent}
*/
div: function (attr = {}) { return builder.genTag("div", attr); },
inputTags: Object.freeze({
button: function (attr = {}, modifier = null) { return builder.input("button", attr, modifier) },
checkbox: function (attr = {}, modifier = null) { return builder.input("checkbox", attr, modifier) },
color: function (attr = {}, modifier = null) { return builder.input("color", attr, modifier) },
date: function (attr = {}, modifier = null) { return builder.input("date", attr, modifier) },
datetime: function (attr = {}, modifier = null) { return builder.input("datetime", attr, modifier) },
datetime_local: function (attr = {}, modifier = null) { return builder.input("datetime-local", attr, modifier) },
email: function (attr = {}, modifier = null) { return builder.input("email", attr, modifier) },
file: function (attr = {}, modifier = null) { return builder.input("file", attr, modifier) },
image: function (attr = {}, modifier = null) { return builder.input("image", attr, modifier) },
month: function (attr = {}, modifier = null) { return builder.input("month", attr, modifier) },
number: function (attr = {}, modifier = null) { return builder.input("number", attr, modifier) },
password: function (attr = {}, modifier = null) { return builder.input("password", attr, modifier) },
radio: function (attr = {}, modifier = null) { return builder.input("radio", attr, modifier) },
range: function (attr = {}, modifier = null) { return builder.input("range", attr, modifier) },
reset: function (attr = {}, modifier = null) { return builder.input("reset", attr, modifier) },
search: function (attr = {}, modifier = null) { return builder.input("search", attr, modifier) },
submit: function (attr = {}, modifier = null) { return builder.input("submit", attr, modifier) },
tel: function (attr = {}, modifier = null) { return builder.input("tel", attr, modifier) },
text: function (attr = {}, modifier = null) { return builder.input("text", attr, modifier) },
time: function (attr = {}, modifier = null) { return builder.input("time", attr, modifier) },
url: function (attr = {}, modifier = null) { return builder.input("url", attr, modifier) },
week: function (attr = {}, modifier = null) { return builder.input("week", attr, modifier) }
}),
/**
*
* @param {Map<string,string>} attr
* @param {Modifier} modifier
* @returns {Component}
*/
span: function (attr = {}) { return builder.genTag("span", attr); },
div: function (attr = {}, modifier = null) { return builder.genTag("div", attr, modifier); },
/**
*
* @param {Map<string,string>} attr
* @param {Modifier} modifier
* @returns {Component}
*/
paragraph: function (attr = {}) { return builder.genTag("p", attr); },
span: function (attr = {}, modifier = null) { return builder.genTag("span", attr, modifier); },
/**
*
* @param {Map<string,string>} attr
* @param {Modifier} modifier
* @returns {Component}
*/
header: function (sizeGroup, attr = {}) { return builder.genTag(`h${sizeGroup}`, attr); },
paragraph: function (attr = {}, modifier = null) { return builder.genTag("p", attr, modifier); },
/**
*
* @param {Map<string,string>} attr
* @param {Modifier} modifier
* @returns {Component}
*/
checkbox: function (attr = {}) { return builder.input({ "type": "checkbox" }) },
header: function (sizeGroup, attr = {}, modifier = null) { return builder.genTag(`h${sizeGroup}`, attr, modifier); },
/**
*
* @param {Map<string,string>} attr
* @returns {Component}
* @param {Modifier} modifier
* @returns {InputComponent}
*/
select: function (attr = {}) { return builder.genTag("select", attr); },
checkbox: function (attr = {}, modifier = null) { return builder.input({ "type": "checkbox" }, modifier) },
/**
*
* @param {Map<string,string>} attr
* @param {Modifier} modifier
* @returns {Component}
*/
option: function (attr = {}) { return builder.genTag("option", attr); },
select: function (attr = {}, modifier = null) {
let comp = new InputComponent(
document.createElement("select"),
attr,
modifier
)
.addStyleClass(`el-input`);
if (this.components.previous) {
comp._parentComponent = this.components.previous;
this.components.previous = null;
}
return comp;
},
/**
*
* @param {Map<string,string>} attr
* @param {Modifier} modifier
* @returns {Component}
*/
select: function (attr = {}) { return builder.genTag("select", attr); },
option: function (attr = {}, modifier = null) { return builder.genTag("option", attr, modifier); },
/**
*
* @param {Map<string,string>} attr
* @param {Modifier} modifier
* @returns {Component}
*/
radioBtn: function (attr = {}) { return builder.genTag("radioBtn", attr); },
radioBtn: function (attr = {}, modifier = null) { return builder.genTag("radioBtn", attr, modifier); },
/**
*
* @param {Map<string,string>} attr
* @param {Modifier} modifier
* @returns {Component}
*/
icon: function (attr = {}) { return builder.genTag("icon", attr); },
icon: function (attr = {}, modifier = null) { return builder.genTag("icon", attr, modifier); },
/**
*
* @param {Map<string,string>} attr
* @param {Modifier} modifier
* @returns {Component}
*/
img: function (attr = {}) { return builder.genTag("img", attr); },
img: function (attr = {}, modifier = null) { return builder.genTag("img", attr, modifier); },
/**
*
* @param {Map<string,string>} attr
* @param {Modifier} modifier
* @returns {InputComponent}
*/
textarea: function (attr = {}, modifier = null) {
let comp = new InputComponent(
document.createElement("textarea"),
attr,
modifier
)
.addStyleClass(`el-textarea`);
if (this.components.previous) {
comp._parentComponent = this.components.previous;
this.components.previous = null;
}
return comp;
},
/**
*
* @param {Map<string,string>} attr
* @param {Modifier} modifier
* @returns {Component}
*/
textarea: function (attr = {}) { return builder.genTag("textarea", attr); },
table: function (attr = {}, modifier = null) {
return builder.genTag("table", attr, modifier)
.modifier(new Modifier().removeStyleRule("display"))
},
/**
*
* @param {Map<string,string>} attr
* @param {Modifier} modifier
* @returns {Component}
*/
table: function (attr = {}) { return builder.genTag("table", attr) },
tableRow: function (attr = {}, modifier = null) { return builder.genTag("tr", attr, modifier); },
/**
*
* @param {Map<string,string>} attr
* @param {Modifier} modifier
* @returns {Component}
*/
tableRow: function (attr = {}) { return builder.genTag("tr", attr) },
tableCell: function (attr = {}, modifier = null) { return builder.genTag("td", attr, modifier); },
/**
*
* @param {Map<string,string>} attr
* @param {Modifier} modifier
* @returns {Component}
*/
tableCell: function (attr = {}) { return builder.genTag("td", attr) },
tableCaption: function (attr = {}, modifier = null) { return builder.genTag("caption", attr, modifier); },
/**
*
* @param {Map<string,string>} attr
* @param {Modifier} modifier
* @returns {Component}
*/
tableCaption: function (attr = {}) { return builder.genTag("caption", attr) },
tableHeadCell: function (attr = {}, modifier = null) { return builder.genTag("th", attr, modifier); },
/**
*
* @param {Map<string,string>} attr
* @param {Modifier} modifier
* @returns {Component}
*/
tableHeadCell: function (attr = {}) { return builder.genTag("th", attr) },
tableBody: function (attr = {}, modifier = null) {
return builder.genTag("tbody", attr, modifier)
.modifier(new Modifier().removeStyleRule("display"))
},
/**
*
* @param {Map<string,string>} attr
* @param {Modifier} modifier
* @returns {Component}
*/
tableBody: function (attr = {}) { return builder.genTag("tbody", attr) },
tableHead: function (attr = {}, modifier = null) {
return builder.genTag("thead", attr, modifier)
.modifier(new Modifier().removeStyleRule("display"))
},
/**
*
* @param {Map<string,string>} attr
* @param {Modifier} modifier
* @returns {Component}
*/
tableHead: function (attr = {}) { return builder.genTag("thead", attr) },
tableFooter: function (attr = {}, modifier = null) {
return builder.genTag("tfoot", attr, modifier)
.modifier(new Modifier().removeStyleRule("display"))
},
/**
*
* @param {Map<string,string>} attr
* @param {Modifier} modifier
* @returns {Component}
*/
tableFooter: function (attr = {}) { return builder.genTag("tfoot", attr) },
iframe: function (attr = {}, modifier = null) { return builder.genTag("iframe", attr, modifier) },
/**
*
* @param {Map<string,string>} attr
* @param {Modifier} modifier
* @returns {Component}
*/
form: function (attr = {}) {
return builder.genTag("form", attr)
.addStyleClass("flex-container-component")
.chainModifier()
.setStyleRule("flex-direction", "column")
.ensureModifier()
.toComponent()
form: function (attr = {}, modifier = null) {
return builder.genTag("form", attr, modifier)
.addStyleClass("flex-container-component")
.modifier(new Modifier().setStyleRule("flex-direction", "column"))
},
/**
*
* @param {*} attr
*
* @param {Map<string,string>} attr
* @param {Modifier} modifier
* @returns {Row}
*/
row: function (attr = {}) { return new Row(attr) },
row: function (attr = {}, modifier = null) {
let comp = new Row(attr, modifier);
if (this.components.previous) {
comp._parentComponent = this.components.previous;
this.components.previous = null;
}
return comp;
},
/**
*
* @param {*} attr
*
* @param {Map<string,string>} attr
* @param {Modifier} modifier
* @returns {Column}
*/
column: function (attr = {}) { return new Column(attr) },
column: function (attr = {}, modifier = null) {
let comp = new Column(attr, modifier);
if (this.components.previous) {
comp._parentComponent = this.components.previous;
this.components.previous = null;
}
return comp;
},
/**
*
* @param {Map<string,string>} attr
* @param {Modifier} modifier
* @returns {FlexContainerComponent}
*/
section: function (attr = {}, modifier = null) {
let comp = new FlexContainerComponent(attr, modifier, "section");
if (this.components.previous) {
comp._parentComponent = this.components.previous;
this.components.previous = null;
}
return comp;
},
/**
*
* @param {*} innerComponents
* @todo upwards bubbling of js or css is not dealt with yet.
*
*
* @param {Component} innerComponents
*/
page: function (innerComponents) {
let main = document.querySelector('main')
let main = document.querySelector('main');
main.parentElement.insertAdjacentElement(
"afterbegin",
builder.genTag("main")
.alignment(Alignment.CENTER)
.arrangement(Arrangement.CENTER)
.childContext(innerComponents)
.generate()
)
.isHigherComponent()
.alignment(Alignment.CENTER)
.arrangement(Arrangement.CENTER)
.childContext(innerComponents)
.generate()
.compext
);
Page.generate();
main.remove();
}

15
src/commonEvents.js

@ -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"
})

246
src/component.js

@ -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);
}
*/
}

234
src/component/ChildbearerComponent.js

@ -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;
}
}

19
src/component/Column.js

@ -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();
}
}

329
src/component/Component.js

@ -0,0 +1,329 @@
/**
* 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;
}
/**
* 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);
}
}

173
src/component/FlexContainerComponent.js

@ -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 of the recess
* that should be left out before distributing the remaining space.
* @param {number} [gapPerChild=1] gapPerChild the gap per child, therefore inbetween children
* that shouldn't be distributed to the 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.
*
* @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 of the recess
* that should be left out before distributing the remaining space.
* @param {number} [gapPerChild=1] gapPerChild the gap per child, therefore inbetween children
* that shouldn't be distributed to the 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);
}
}

69
src/component/InputComponent.js

@ -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);
}
}

46
src/component/Row.js

@ -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;
}
}

194
src/component/StyleAndScriptStoringComponent.js

@ -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;
}
}

53
src/component/modifiableComponent.js

@ -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);
}
}

177
src/context.js

@ -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();

196
src/context/context.js

@ -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();

61
src/context/framework-controls.js

@ -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")
])
]);
}

197
src/context/scriptAndStyleContext.js

@ -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);
}
}
}

7
src/alignment.js → src/decorators/alignment.js

@ -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
*/

11
src/decorators/arrangement.js

@ -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",
});

147
src/sizeSide/border.js → src/decorators/border.js

@ -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
*/
/**
*
*/
@ -22,24 +15,50 @@ const LineStyles = Object.freeze({
})
class BorderDefinition {
/**
*
* @param {number} width
* @param {Color} color
* @param {LineStyles} style
*/
constructor(width = 0, color = Colors.black, style = LineStyles.solid) {
this._width = width;
this._color = color;
this._style = style;
}
/**
*
* @param {number} width
* @returns {BorderDefinition}
*/
width(width) {
this._width = width;
return this;
}
/**
*
* @param {Color} color
* @returns {BorderDefinition}
*/
color(color) {
this._color = color;
return this;
}
/**
*
* @param {LineStyles} style
* @returns {BorderDefinition}
*/
style(style) {
this._style = style;
return this;
}
/**
*
* @param {BorderDefinition} def
* @returns {BorderDefinition}
*/
join(def) {
Object.keys(def)
.forEach(key => this[key] = def[key]);
@ -53,21 +72,30 @@ const Define = Object.freeze({
color: new BorderDefinition().color
})
/**
* @inheritdoc
* @extends Sides
*/
class Border extends Sides {
constructor(width = 0, color = Colors.black, style = LineStyles.solid, defaultUnit = SizeUnits.PIXEL, shape = Shapes.Rectangle) {
super(0, defaultUnit);
constructor(width = 0, color = Colors.black, style = LineStyles.solid, defaultUnit = SizeUnits.PIXEL) {
super('border', 0, defaultUnit);
this._fFirst = new BorderDefinition(width, color, style);
this._fSecond = new BorderDefinition(width, color, style);
this._fThird = new BorderDefinition(width, color, style);
this._fForth = new BorderDefinition(width, color, style);
this._shape = shape;
}
/**
* @override
* @param {number} index
* @param {number} value
* @returns {Border}
*/
setByIndex(index, value) {
if (value instanceof BorderDefinition) {
this.getByIndex(index).join(value)
this.getByIndex(index).join(value);
} else {
this.getByIndex(index)._width = value
this.getByIndex(index)._width = value;
}
return this;
}
@ -76,6 +104,7 @@ class Border extends Sides {
*
* @param {string} key
* @param {*} value
* @returns {typeof Border}
*/
setOnDirections(key, value) {
let orderedAttributes = this.getOrderedAttributes()
@ -88,7 +117,7 @@ class Border extends Sides {
/**
*
* @param {number} width
* @returns {Border}
* @returns {typeof Border}
*/
width(width) {
this._fFirst._width = width;
@ -101,7 +130,7 @@ class Border extends Sides {
/**
*
* @param {*} color
* @returns {Border}
* @returns {typeof Border}
*/
color(color) {
this._fFirst._color = color;
@ -111,6 +140,11 @@ class Border extends Sides {
return this;
}
/**
*
* @param {Shape} shape
* @returns {typeof Border}
*/
shape(shape) {
this._shape = shape;
return this;
@ -119,7 +153,7 @@ class Border extends Sides {
/**
* Sets the border-style of all sides to the given.
* @param {LineStyles} lineStyle style of the border
* @returns {Border}
* @returns {typeof Border}
*/
setStyleAll(lineStyle) {
this._fFirst._style = lineStyle;
@ -133,7 +167,7 @@ class Border extends Sides {
*
* @param {LineStyles} lineStyle
* @param {*} sidingRefSide
* @returns {Border}
* @returns {typeof Border}
*/
setLineStyle(lineStyle, sidingRefSide) {
this._sidingStyles.setBySidingRef(sidingRefSide, lineStyle)
@ -143,17 +177,21 @@ class Border extends Sides {
/**
*
* @param {Map<SidingRefSides, LineStyles} refSideStyleMap
* @returns {Border}
* @returns {typeof Border}
*/
setLineStyles(refSideStyleMap) {
let rkeys = Object.keys(refSideStyleMap);
for (let i = 0; i < array.length; i++) {
this._sidingStyles.setBySidingRef(rkeys[i]) = refSideStyleMap[rkeys[i]];
this._sidingStyles.setBySidingRef(rkeys[i], refSideStyleMap[rkeys[i]]);
}
return this;
}
/**
*
* @returns {Array<{key: string, value: string}>}
*/
toModifications() {
let names = ["left", "top", "right", "bottom"];
return this.getOrderedAttributes()
@ -163,57 +201,62 @@ class Border extends Sides {
return [
{ key: `border-${names[i]}-width`, value: bdef._width + this._unit },
{ key: `border-${names[i]}-color`, value: bdef._color },
{ key: `border-${names[i]}-color`, value: bdef._color.cssRGBString() },
{ key: `border-${names[i]}-style`, value: bdef._style }
]
})
});
}
}
class BorderChain extends Border {
constructor(modifier){
super();
this._modifier = modifier;
}
/**
* If the given amount is a number this method calls width(amount).
* If amount is instanceof BorderDefinition it will set the BorderDefinition
* to all sides.
* If neither it will do nothing and return this Border object.
*
* @returns {Modifier|ChainableModifier}
* @inheritdoc
* @override
* @param {number|BorderDefinition} amount value to set for all directions
* @returns {typeof Sides} this
*/
toModifier() {
return this._modifier
.border(this);
all(amount) {
if(amount instanceof Number){
return this.width(amount);
}
if(amount instanceof BorderDefinition){
return super.all(amount);
}
return this;
}
}
/**
* @inheritdoc
* @extends Border
*/
class BorderChain extends mixinModSubChainEndings(Border) {
/**
*
* @returns {Modifier|ChainableModifier}
* @param {Modifier} modifier
*/
ensureModifier() {
return this.toModifier()
constructor(modifier) {
super(modifier);
}
}
/**
* Applies the border modification on the modifier
* and returns (through the modifier) to the corresponding component.
* @returns {Component}
*/
toComponent() {
return this._modifier
.border(this)
.toComponent();
}
/**
* @inheritdoc
* @extends BorderChain
*/
class BorderChainedModifier extends mixinModSubChainComponentMethods(Border) {
/**
*
* @param {Component} innerComponent will be set to the corresponding component
* @returns {Component} the corr. Component after the childContext was applied.
*/
childContext(innerComponent) {
return this._modifier
.border(this)
.toComponent()
.childContext(innerComponent);
* @param {ChainableModifier} modifier
*/
constructor(modifier) {
super(modifier);
}
}

18
src/color.js → src/decorators/color.js

@ -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
*/
/**
* A simple Color class for rgb set color values.
*
@ -13,7 +6,7 @@ class Color {
#red;
#green;
#blue;
#hex;
_hex;
/**
*
@ -41,7 +34,10 @@ class Color {
* @returns
*/
hex(hexcode) {
this.#hex = hexcode;
if (!hexcode.startsWith('#')) {
hexcode = '#' + hexcode;
}
this._hex = hexcode;
return this;
}
}
@ -505,7 +501,6 @@ const Colors = Object.freeze({
gray_99: new Color(252, 252, 252).hex('#FCFCFC'),
gray_98: new Color(250, 250, 250).hex('#FAFAFA'),
gray_97: new Color(247, 247, 247).hex('#F7F7F7'),
white_smoke: new Color(245, 245, 245).hex('#F5F5F5'), // (gray 96)
gray_95: new Color(242, 242, 242).hex('#F2F2F2'),
gray_94: new Color(240, 240, 240).hex('#F0F0F0'),
gray_93: new Color(237, 237, 237).hex('#EDEDED'),
@ -560,7 +555,6 @@ const Colors = Object.freeze({
gray_44: new Color(112, 112, 112).hex('#707070'),
gray_43: new Color(110, 110, 110).hex('#6E6E6E'),
gray_42: new Color(107, 107, 107).hex('#6B6B6B'),
dimgray: new Color(105, 105, 105).hex('#696969'), // (gray 42)
gray_40: new Color(102, 102, 102).hex('#666666'),
gray_39: new Color(99, 99, 99).hex('#636363'),
gray_38: new Color(97, 97, 97).hex('#616161'),
@ -620,4 +614,4 @@ const MaterialFiveHundredlColors = Object.freeze({
GOLD: new Color(255, 235, 59),
YELLO_ORANGE: new Color(255, 152, 0),
ORANGE: new Color(255, 87, 34),
})
});

95
src/sizeSide/dimensions.js → src/decorators/dimensions.js

@ -1,16 +1,17 @@
/**
* 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
*/
/**
* Simple Dimensions container for the height and width in pixels.
*
* @inheritdoc
* @extends DirectionUnitDependentAttribute
*/
class Dimensions extends DirectionUnitDependentAttribute {
/**
*
* @param {number} defaultValue
* @param {SizeUnits} defaultUnit
*/
constructor(defaultValue = 0, defaultUnit = SizeUnits.PIXEL) {
super();
super("dimensions", defaultValue, defaultUnit);
this._unit = defaultUnit;
this._fFirst = defaultValue;
this._fSecond = defaultValue;
@ -19,7 +20,7 @@ class Dimensions extends DirectionUnitDependentAttribute {
/**
* Sets width (x) value of amount
* @param {number} amount
* @returns {Dimensions} this Dimensions Modifier
* @returns {typeof Dimensions} this Dimensions Modifier
*/
width(amount) {
this._fFirst = amount;
@ -29,22 +30,34 @@ class Dimensions extends DirectionUnitDependentAttribute {
/**
* Sets height (y) value of amount
* @param {number} amount
* @returns {Dimensions} this Dimensions Modifier
* @returns {typeof Dimensions} this Dimensions Modifier
*/
height(amount) {
this._fSecond = amount;
return this;
}
/**
*
* @param {number} size
* @returns {typeof Dimensions}
*/
all(size) {
return this.width(size).height(size);
}
/**
*
* @returns
*/
getOrderedValues() {
return this.getOrderedValues().slice(2)
return super.getOrderedValues().slice(2)
}
/**
*
* @returns
*/
toModifications() {
let w = { key: "width", value: this._fFirst + this._unit }
let h = { key: "height", value: this._fSecond + this._unit }
@ -60,53 +73,45 @@ class Dimensions extends DirectionUnitDependentAttribute {
return []
}
}
}
class DimensionsChain extends Dimensions {
_modifier;
constructor(modifier) {
super();
this._modifier = modifier;
}
/**
*
* @returns {Modifier|ChainableModifier}
* @returns {TwoDimPoint}
*/
toModifier() {
return this._modifier
.dimensions(this);
toTwoDimPoint() {
return new TwoDimPoint(
this._fFirst,
this._fSecond
);
}
}
/**
*
* @returns {Modifier|ChainableModifier}
*/
ensureModifier() {
return this.toModifier()
}
/**
* @inheritdoc
* @extends Dimensions
*/
class DimensionsChain extends mixinModSubChainEndings(Dimensions) {
/**
*
* @returns {Component} the Component that was (supposed to be) modified by this obj.
* @param {Modifier} modifier
*/
toComponent() {
return this._modifier
.dimensions(this)
.toComponent();
constructor(modifier) {
super(modifier);
}
}
/**
* @inheritdoc
* @extends DimensionsChain
*/
class DimensionsChainedModifier extends mixinModSubChainComponentMethods(Dimensions) {
/**
*
* @param {Component|Array<Component>} innerComponent children of the Component under modification.
* @returns {Component}
* @param {ChainableModifier} modifier
*/
childContext(innerComponent) {
return this._modifier
.dimensions(this)
.toComponent()
.childContext(innerComponent);
constructor(modifier) {
super(modifier);
}
}

53
src/decorators/margin.js

@ -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);
}
}

44
src/decorators/padding.js

@ -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);
}
}

127
src/sizeSide/shapes.js → src/decorators/shapes.js

@ -1,21 +1,15 @@
/**
* 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
*/
/**
*
* @extends DirectionUnitDependentAttribute
* @inheritdoc
*/
class Shape extends DirectionUnitDependentAttribute {
constructor(defaultValue = 0, defaultUnit = SizeUnits.PIXEL) {
super(defaultValue, defaultUnit);
super('clip',defaultValue, defaultUnit);
}
/**
*
* @param {*} amount
* @returns {Shape}
* @param {number} amount
* @returns {typeof Shape}
*/
topLeft(amount) {
this._fFirst = amount;
@ -23,8 +17,8 @@ class Shape extends DirectionUnitDependentAttribute {
}
/**
*
* @param {*} amount
* @returns {Shape}
* @param {number} amount
* @returns {typeof Shape}
*/
topRight(amount) {
this._fSecond = amount;
@ -32,8 +26,8 @@ class Shape extends DirectionUnitDependentAttribute {
}
/**
*
* @param {*} amount
* @returns {Shape}
* @param {number} amount
* @returns {typeof Shape}
*/
bottomLeft(amount) {
this._fForth = amount;
@ -41,41 +35,66 @@ class Shape extends DirectionUnitDependentAttribute {
}
/**
*
* @param {*} amount
* @returns {Shape}
* @param {number} amount
* @returns {typeof Shape}
*/
bottomRight(amount) {
this.fThird = amount;
this._fThird = amount;
return this;
}
/**
*
* @param {*} amount
* @returns {Shape}
* Sets the BottomLeft and TopRight corners
* @param {number} amount
* @returns {typeof Shape}
*/
diagonalPositive(amount) {
return this.bottomLeft(amount).topRight(amount);
}
/**
*
* @param {*} amount
* @returns {Shape}
* Sets the TopLeft and BottomRight corners
* @param {number} amount
* @returns {typeof Shape}
*/
diagonalNegative(amount) {
return this.topLeft(amount).bottomRight(amount);
}
/**
* Sets both corners on the left side
* @param {number} amount
* @returns {typeof Shape}
*/
left(amount) {
return this.topLeft(amount).bottomLeft(amount);
}
/**
* Sets both corners on the right side
* @param {number} amount
* @returns {typeof Shape}
*/
right(amount) {
return this.topRight(amount).bottomRight(amount);
}
/**
* Sets both top corners
* @param {number} amount
* @returns {typeof Shape}
*/
top(amount) {
return this.topLeft(amount).topRight(amount);
}
/**
* Sets both bottom corners
* @param {number} amount
* @returns {typeof Shape}
*/
bottom(amount) {
return this.bottomLeft(amount).bottomRight(amount);
}
/**
*
* @param {*} amount
* @returns
*/
getSidingRefValueMap() {
return {
@ -87,50 +106,32 @@ class Shape extends DirectionUnitDependentAttribute {
}
}
class ShapeChain extends Shape {
_modifier;
constructor(modifier) {
super();
this._modifier = modifier;
}
/**
*
* @returns {Modifier|ChainableModifier}
*/
toModifier() {
return this._modifier
.clip(this);
}
/**
*
* @returns {Modifier|ChainableModifier}
*/
ensureModifier() {
return this.toModifier()
}
/**
* @inheritdoc
* @extends Shape
*/
class ShapeChain extends mixinModSubChainEndings(Shape) {
/**
*
* @returns {Component} the Component that was (supposed to be) modified by this obj.
* @param {Modifier} modifier
*/
toComponent() {
return this._modifier
.clip(this)
.toComponent();
constructor(modifier) {
super(modifier);
}
}
/**
* @inheritdoc
* @extends ShapeChain
*/
class ShapeChainedModifier extends mixinModSubChainComponentMethods(Shape) {
/**
*
* @param {Component|Array<Component>} innerComponent children of the Component under modification.
* @returns {Component}
* @param {ChainableModifier} modifier
*/
childContext(innerComponent) {
return this._modifier
.clip(this)
.toComponent()
.childContext(innerComponent);
constructor(modifier) {
super(modifier);
}
}

167
src/sizeSide/siding.js → src/decorators/siding.js

@ -1,27 +1,32 @@
/**
* 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 predefined set of Size-Units
*/
const SizeUnits = Object.freeze({
PIXEL: "px",
PERCENT: "%"
})
/**
* @abstract
*/
class DirectionUnitDependentAttribute {
/**
* @type {string} the name of the modifier-method that uses this class.
*/
_modMethod;
_unit;
_fFirst;
_fSecond;
_fThird;
_fForth;
constructor(defaultValue = 0, defaultUnit = SizeUnits.PIXEL) {
/**
* @param {number|string} defaultValue
* @param {SizeUnits} defaultUnit
*/
constructor(modMethod, defaultValue = 0, defaultUnit = SizeUnits.PIXEL) {
this._modMethod=modMethod;
this._unit = defaultUnit;
this._fFirst = defaultValue;
this._fSecond = defaultValue;
@ -32,7 +37,7 @@ class DirectionUnitDependentAttribute {
/**
*
* @param {Units} unit The unit of the amount or style
* @returns {DirectionUnitDependentAttribute} this - Object
* @returns {typeof Sides} this - Object
*/
setUnit(unit) {
this._unit = unit;
@ -41,7 +46,7 @@ class DirectionUnitDependentAttribute {
/**
*
* @returns {array<*>} list of attributes
* @returns
*/
getOrderedAttributes() {
return [this._fFirst, this._fSecond, this._fThird, this._fForth];
@ -61,7 +66,7 @@ class DirectionUnitDependentAttribute {
* Mainly used by the setup of directions of subclasses.
* @param {number} index [1,4]
* @param {number} value
* @returns {DirectionUnitDependentAttribute} this
* @returns {typeof Sides} this
*/
setByIndex(index, value) {
switch (index) {
@ -121,7 +126,7 @@ class DirectionUnitDependentAttribute {
/**
* sets the amount-value for all directions.
* @param {number} amount value to set for all directions
* @returns {DirectionUnitDependentAttribute} this
* @returns {typeof Sides} this
*/
all(amount) {
this._fFirst = amount;
@ -133,22 +138,51 @@ class DirectionUnitDependentAttribute {
}
const SideDirections = Object.freeze({
LEFT: 1,
TOP: 2,
RIGHT: 3,
BOTTOM: 4
});
const SideTransitionDirection = Object.freeze({
HORIZONTAL: 0,
VERTICAL: 1
});
const Corners = Object.freeze({
TOP_LEFT: 0,
TOP_RIGHT: 1,
BOTTOM_LEFT: 2,
BOTTOM_RIGHT: 3
});
const CornerTransitionDirection = Object.freeze({
TOP_LEFT_BOTTOM_RIGHT: 0,
TOP_RIGHT_BOTTOM_LEFT: 1
});
/**
* @inheritdoc
* @extends DirectionUnitDependentAttribute
* @abstract
*/
class Sides extends DirectionUnitDependentAttribute {
/**
*
* @deprecated class is abstract now,
* direct usage is deprecated use extending classes (Padding, Margin)
* @param {number|string} defaultValue
* @param {SizeUnits} defaultUnit
*/
constructor(defaultValue = 0, defaultUnit = SizeUnits.PIXEL) {
super(defaultValue, defaultUnit);
constructor(modMethod, defaultValue = 0, defaultUnit = SizeUnits.PIXEL) {
super(modMethod, defaultValue, defaultUnit);
}
/**
* sets the amount-value for the left side.
* @param {number} amount siding for left
* @returns {Siding} this Siding Object
* @param {number} amount Sides for left
* @returns {typeof Sides} this Sides Object
*/
left(amount) {
return this.setByIndex(1, amount);
@ -156,8 +190,8 @@ class Sides extends DirectionUnitDependentAttribute {
/**
* sets the amount-value for the right side.
* @param {number} amount siding for right
* @returns {Siding} this Siding Object
* @param {number} amount Sides for right
* @returns {typeof Sides} this Sides Object
*/
right(amount) {
return this.setByIndex(3, amount);
@ -165,8 +199,8 @@ class Sides extends DirectionUnitDependentAttribute {
/**
* sets the amount-value for the top side.
* @param {number} amount siding for top
* @returns {Siding} this Siding Object
* @param {number} amount Sides for top
* @returns {typeof Sides} this Sides Object
*/
top(amount) {
return this.setByIndex(2, amount);
@ -174,8 +208,8 @@ class Sides extends DirectionUnitDependentAttribute {
/**
* sets the amount-value for the bottom side.
* @param {number} amount siding for bottom
* @returns {Siding} this Siding Object
* @param {number} amount Sides for bottom
* @returns {typeof Sides} this Sides Object
*/
bottom(amount) {
return this.setByIndex(4, amount);
@ -184,8 +218,8 @@ class Sides extends DirectionUnitDependentAttribute {
/**
* sets the amount-value for the horizontal sides (left and right).
* @param {number} amount siding for left and right.
* @returns {Sides} this Siding Object
* @param {number} amount Sides for left and right.
* @returns {typeof Sides} this Sides Object
*/
horizontal(amount) {
return this.left(amount).right(amount);
@ -193,78 +227,41 @@ class Sides extends DirectionUnitDependentAttribute {
/**
* sets the amount-value for the vertical sides (left and right).
* @param {number} amount siding for top and bottom.
* @returns {Sides} this Siding Object
* @param {number} amount Sides for top and bottom.
* @returns {typeof Sides} this Sides Object
*/
vertical(amount) {
return this.top(amount).bottom(amount);
}
toModifications() {
return [
{ key: "left", value: this._fFirst + this._unit },
{ key: "top", value: this._fSecond + this._unit },
{ key: "right", value: this._fThird + this._unit },
{ key: "bottom", value: this._fForth + this._unit }
]
}
}
class PaddingChain extends Sides {
_modifier;
constructor(modifier) {
super();
this._modifier = modifier;
}
toModifier() {
return this._modifier
.padding(this);
}
/**
* Returns the corresponding Modifier.
* Basically climbs up the chain level.
* @returns {Modifier}
*
* @returns {Object}
*/
ensureModifier() {
return this.toModifier()
getValues() {
return {
"left": this._fFirst,
"top": this._fSecond,
"right": this._fThird,
"bottom": this._fForth,
"horizontal": this._fFirst + this._fThird,
"vertical": this._fSecond + this._fForth
}
}
/**
* Returns the style-modifications of the class.
* @returns {Map<string,string>}
* The style-modification is set as <_stylesKey>-<directional value>.
*
* @returns
*/
toModifications() {
let preKey = this._modMethod + (this._modMethod !== '' ? '-' : '');
return [
{ key: "padding-left", value: this._fFirst + this._unit },
{ key: "padding-top", value: this._fSecond + this._unit },
{ key: "padding-right", value: this._fThird + this._unit },
{ key: "padding-bottom", value: this._fForth + this._unit }
{ key: preKey + "left", value: this._fFirst + this._unit },
{ key: preKey + "top", value: this._fSecond + this._unit },
{ key: preKey + "right", value: this._fThird + this._unit },
{ key: preKey + "bottom", value: this._fForth + this._unit }
]
}
/**
*
* @returns {Component} the Component that was (supposed to be) modified by this obj.
*/
toComponent() {
return this._modifier
.padding(this)
.toComponent();
}
/**
*
* @param {Component|Array<Component>} innerComponent children of the Component under modification.
* @returns {Component}
*/
childContext(innerComponent) {
return this._modifier
.padding(this)
.toComponent()
.childContext(innerComponent);
}
}

1
src/generators/defaultGenerators.js

@ -0,0 +1 @@
const singlepage = new CompelGenerator();

313
src/generators/generator.js

@ -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;
}
}

14
src/handlers/commonEvents.js

@ -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",
});

63
src/handlers/contextMenu.js

@ -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")
}
}
}
};

123
src/handlers/dragAndDrop.js

@ -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);
//}
}
}

55
src/helper/ObjectAccessObject.js

@ -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;
}
}

101
src/helper/general.js

@ -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: {}
}

84
src/helper/indices.js

@ -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)
}
}

348
src/helper/twoDimPoint.js

@ -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;
}
}

276
src/modifier.js

@ -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);
}
}

187
src/modifier/ChainableModifier.js

@ -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();
}
}

87
src/modifier/ModificationSubChain.js

@ -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();
}
}

466
src/modifier/Modifier.js

@ -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);
return this;
} else {
let modSub = new BorderChain(this);
if (Number.isInteger(modify) && modify > 0) {
return modSub.width(modify).ensureModifier();
}
return modSub;
}
}
/**
* 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;
}
}

87
src/modifier/mixinModSubChain.js

@ -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();
}
}
}

32
upcoming.md

@ -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…
Cancel
Save