library-ts/browser/templates/TemplatesManager.ts

241 lines
6.7 KiB
TypeScript
Raw Permalink Normal View History

2025-03-08 08:16:54 +00:00
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<string,Element> = new Map<string,Element>();
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<string,ElementProcessor>();
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><body>${html}</body>` );
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 );
}
}