/** * 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} childrenWenity * @param {ExtStorage} extStore * @returns {Array} */ 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} childrenWenity * @param {ExtStorage} extStore * @returns {Array} */ 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>} source * @param {Map>} 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} 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} */ resolveChildren(component, styleStore, functionStore) { /** * @type {Array} */ 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} */ let styleCollection = this.processStyles(component, styleStore); /** * @type {Map>} */ 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; } }