/** * This file is part of the jps-like-websites lib * URL: https://git.labos.goip.de/chris/jpc-like-websites * @copyright by its creator Christian Martin */ /** * @abstract * Class adds function and style storing properties to the context (PageBuilder). */ class ScriptAndStyleContext { /** * @property {Map>} #css * @type {Map>} #css */ #css; /** * @property {Map} #functions * @type {Map} #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 */ let functionNames = this.#functions.keys; if (functionNames.includes(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} 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|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); } } }