import { IncrementalIDGenerator } from "../random/IncrementalIDGenerator"; import { ElementAttribute } from "../dom/ElementAttribute"; import { TemplateReplacer } from "./TemplateReplacer"; import { ElementProcessor } from "./ElementProcessor"; import { DOMEditor as HTMLEditor } from "../dom/DOMEditor"; import { StylesProcessor } from "./styles-processor/StylesProcessor"; import { ElementType } from "../dom/ElementType"; export enum TemplatesManagerMode { ADD_STYLES_TO_HEAD, IGNORE_STYLES } export class TemplatesManager { static readonly defaultOverloadAttributeName = "templates-overload-type"; static readonly defaultIDAttributeName = "templates-id"; static readonly id = new ElementAttribute( TemplatesManager.defaultIDAttributeName ); private _overloadAttribute = new ElementAttribute( TemplatesManager.defaultOverloadAttributeName ); private _idAttribute = new ElementAttribute( TemplatesManager.defaultIDAttributeName ); private _templatesMap:Map = new Map(); private _idGenerator:IncrementalIDGenerator = new IncrementalIDGenerator(); private _replacer:TemplateReplacer = new TemplateReplacer(); get replacer(){ return this._replacer; } private _mode = TemplatesManagerMode.ADD_STYLES_TO_HEAD; private _templateStyles:string[] = []; private _additionalProcessor = new Map(); setMode( mode:TemplatesManagerMode ) { this._mode = mode; } addProcessor( processor:ElementProcessor ) { this._additionalProcessor.set( processor.getElementName(), processor ); } addTemplate( element:Element ) { if ( Node.ELEMENT_NODE !== element.nodeType ) { return; } if ( "STYLE" === element.nodeName.toUpperCase() ) { this._addStyle( element ); return; } let hash = element.nodeName; if ( this._overloadAttribute.in( element ) ) { let overloadType = this._overloadAttribute.from( element ); hash += " " + overloadType; } this._templatesMap.set( hash, element ); } static templateIDCounter = 0; addTemplateHTML( html:string ) { TemplatesManager.templateIDCounter ++; let generatedDocument = HTMLEditor.stringToDocument( `${html}` ); let children = HTMLEditor.nodeListToArray( generatedDocument.body.childNodes ); for ( let i = 0; i < children.length; i++ ) { this.addTemplate( children[ i ] ); } } processChildren( root:Element ) { let children = HTMLEditor.nodeListToArray( root.childNodes ); for ( let i = 0; i < children.length; i++ ) { this._processElement( children[ i ] ); } } processElement( element:Element ) { return this._processElement( element ); } getTemplate( nodeName:string ) { return this._templatesMap.get( nodeName.toUpperCase() ); } createTemplate( nodeName:string, doc:Document = undefined ) { doc = doc || document; let templateNode = doc.importNode( this.getTemplate( nodeName ), true ); templateNode = this._processElement( templateNode ); return templateNode; } private _addStyle( styleElement:Element ) { if ( TemplatesManagerMode.IGNORE_STYLES === this._mode ) { //this._templateStyles.push( styleElement.innerHTML ); //console.log( "Adding style to templates.css", styleElement ); return; } let styleContent = styleElement.textContent; let processedStyleContent = StylesProcessor.convert( styleContent ); let clonedStyle = ElementType.style.create(); clonedStyle.innerHTML = processedStyleContent; //let clonedStyle = document.importNode( styleElement, true ); document.head.appendChild( clonedStyle ); let id = this._idGenerator.createID(); this._idAttribute.to( styleElement, id ); this._idAttribute.to( clonedStyle, id ); //console.log( "Adding style to head", styleElement, clonedStyle ); } private _processElement( element:Element ):Element { //console.log( "Processing:", HierarchyName.of( element ) ); if ( this._idAttribute.in( element ) ) { this.processChildren( element ); return element; } if ( this._additionalProcessor.has( element.nodeName ) ) { let processor = this._additionalProcessor.get( element.nodeName ); let processedElement:Element = null; //console.log( "ADDITIONAL PROCESSOR", processor ); try { processedElement = processor.processElement( element ); } catch ( e ) { console.log( "Processor:", processor, `ERROR: in <${element.nodeName.toLowerCase()}>` ); throw e; } this._idAttribute.to( processedElement, this._idGenerator.createID() ); //console.log( "Replacing Kids:", HierarchyName.of( processedElement ) ); this._replacer.replaceChildren( element, processedElement ); //console.log( "PROCESSING Kids:", HierarchyName.of( processedElement ) ); this.processChildren( processedElement ); if ( processor.hasPostProcessor ) { processor.postProcessElement( element, processedElement ); } return processedElement; } if ( ! this._templatesMap.has( element.nodeName ) ) { this.processChildren( element ); return element; } let classElement = this._templatesMap.get( element.nodeName.toUpperCase() ); let replacedElement:Element = null; try { replacedElement = this._replacer.replace( element, classElement ); } catch ( e ) { console.log( `Replacer-Error: in <${element.nodeName.toLowerCase()}>`, e.stack ); throw e; } //console.log( "CLASS ELEMENT", classElement, replacedElement ); this._idAttribute.to( replacedElement, this._idGenerator.createID() ); this.processChildren( replacedElement ); return replacedElement; } static addCustomElements() { let customElementsManager = new TemplatesManager(); let customElements = document.querySelector( "custom-elements" ); if ( ! customElements ) { return; } let customElementsNodes = customElements.childNodes; for ( let i = 0; i < customElementsNodes.length; i++ ) { if ( Node.ELEMENT_NODE != customElementsNodes[ i ].nodeType ) { continue; } //console.log( customElementsNodes[ i ].nodeName ); customElementsManager.addTemplate( customElementsNodes[ i ] as Element); } HTMLEditor.remove( customElements ); customElementsManager.processChildren( document.body ); } }