import { HTMLNodeTreeWalker } from "../graphs/HTMLNodeTreeWalker"; import { DOMEditor as HTMLEditor } from "../dom/DOMEditor"; import { TemplatesSourceLexer } from "./TemplatesSourceLexer"; import { TemplateSourceMatchers } from "./TemplateSourceMatchers"; import { ElementAttribute } from "../dom/ElementAttribute"; export class TemplateReplacer { static readonly dataAttributeAssignmentPrefix = "data-@"; static readonly unwrapTemplate = new ElementAttribute( "-unwrap--template" ); replace( target:Element, classElement:Element ) { let replacingElement = target.ownerDocument.importNode( classElement, true ) as Element; this._processElement( target, replacingElement ); if ( target.parentElement ) { target.parentElement.replaceChild( replacingElement, target ); } let walker = new HTMLNodeTreeWalker(); walker.forAll( replacingElement, e => this._processAttributes( target, e as Element ) ); try { HTMLEditor.copyAllAttributes( target, replacingElement ); } catch( e ) { replacingElement = HTMLEditor.copyAllAttributesThroughRawHTML( target, replacingElement ); } return replacingElement; } replaceChildren( target:Element, replacement:Element ) { //console.log( "REPLACING CHILDREN", target.nodeName, ">>", replacement.nodeName ); this._processElement( target, replacement ); } private _processAttributes( target:Element, element:Element ) { if ( ! element.attributes ) { return; } let attributes = []; for ( let i = 0; i < element.attributes.length; i++ ) { attributes.push( element.attributes[ i ] ); } for ( let i = 0; i < attributes.length; i++ ) { let attributeName = attributes[ i ].nodeName; if ( ! attributeName.startsWith( TemplateReplacer.dataAttributeAssignmentPrefix ) ) { //console.log( "NOT ASSIGNMENT:", attributeName ); continue; } else { //console.log( "IS ASSIGNMENT:", attributeName ); } let name = attributeName.substring( TemplateReplacer.dataAttributeAssignmentPrefix.length ); element.removeAttribute( attributeName ); TemplateSourceMatchers.regex.lastIndex = 0; let match = TemplateSourceMatchers.regex.exec( attributes[ i ].nodeValue ); if ( ! match ) { let nodeValueSnippet = "(...too much data...)"; if ( attributes[ i ].nodeValue.length < 50 ) { nodeValueSnippet = attributes[ i ].nodeValue; } else if ( attributes[ i ].nodeValue.length < 250 ) { nodeValueSnippet = attributes[ i ].nodeValue.substring( 0, 50 ); } // console.log( "NOTHING FOR ASSIGNMENT:", match, name, attributeName, nodeValueSnippet ); continue; } let lexer = new TemplatesSourceLexer(); let outputValue:string[] = []; let nodeValueBefore = attributes[ i ].nodeValue; // outputValue = this._processTemplateSourceText( target, nodeValueBefore ); let lexerEvents = lexer.lexToList( nodeValueBefore ); let defaultValue = null; for ( let i = 0; i < lexerEvents.length; i++ ) { let event = lexerEvents[ i ]; let subMatchValue = event.getMatch( nodeValueBefore ); if ( event.isError ) { console.log( `Error lexing attribute ${nodeValueBefore} at position ${event.offset}` ); return; } if ( event.isDone ) { continue; } if ( TemplatesSourceLexer.DEFAULT === event.type ) { defaultValue = TemplatesSourceLexer.defaultMatcher.getMatch( subMatchValue, 1 ); continue; } if ( TemplatesSourceLexer.CONTENT === event.type ) { outputValue.push( subMatchValue ); } else { TemplateSourceMatchers.regex.lastIndex = 0; let subMatch = TemplateSourceMatchers.regex.exec( subMatchValue ); let templateSource = TemplateSourceMatchers.createTemplateSource( subMatch ); if ( defaultValue ) { templateSource.defaultValue = defaultValue; } let sourceValue = templateSource.getInnerHTML( target ); sourceValue = sourceValue.replace( "&&", "&" ); outputValue.push( sourceValue ); } defaultValue = null; } let newNodeValue = outputValue.join( "" ); if ( newNodeValue !== "" ) { element.setAttribute( name, newNodeValue ); //console.log( "ASSIGNING ", name, newNodeValue ); } else { //console.log( "NOT ASSIGNING (VALUE INVALID): ", name, "(" + attributeName + ")", lexerEvents ); } /* let elementSource = this._createElementSource( match ); let sourceValue = elementSource.getInnerHTML( target ); let setAttributeValue = true; if ( sourceValue === "" ) { setAttributeValue = false; } if ( setAttributeValue ) { element.setAttribute( name, sourceValue ); //console.log( "setAttribute", name, attributeName, elementSource ); }*/ } } private _processElement( target:Element, childElement:Element ) { var children = []; for ( let i = 0; i < childElement.childNodes.length; i++ ) { let child = childElement.childNodes[ i ]; children.push( child ); } for ( let i = 0; i < children.length; i++ ) { let child = children[ i ]; if ( Node.TEXT_NODE == child.nodeType ) { this._processTextNode( target, child ); } else if ( Node.ELEMENT_NODE == child.nodeType ) { this._processElement( target, child as Element); } } } private _processTextNodeOld( target:Element, textNode:Node ) { let text = textNode.nodeValue; TemplateSourceMatchers.regex.lastIndex = 0; let match = TemplateSourceMatchers.regex.exec( text ); //console.log( "Matching", `"${text}"`, match ); if ( ! match ) { return; } let lastIndex = 0; let nodes:Node[] = []; while ( match ) { let matchIndex = match.index; let range = matchIndex - lastIndex; if ( range > 0 ) { let textRange = text.substring( lastIndex, matchIndex ); let rangeNode = document.createTextNode( textRange ) nodes.push( rangeNode ); } let templateSource = TemplateSourceMatchers.createTemplateSource( match ); let targetNodes = templateSource.getNodes( target ); targetNodes.forEach( n => nodes.push( n ) ); lastIndex = matchIndex + match[ 0 ].length; match = TemplateSourceMatchers.regex.exec( text ); if ( match ) { continue; } range = text.length - lastIndex; if ( range > 0 ) { let textRange = text.substring( lastIndex, text.length ); let rangeNode = document.createTextNode( textRange ) nodes.push( rangeNode ); } } for ( let i = 0; i < nodes.length; i++ ) { textNode.parentElement.insertBefore( nodes[ i ], textNode ); } textNode.parentElement.removeChild( textNode ); } private _processTextNode( target:Element, textNode:Node ) { let text = textNode.nodeValue; let nodes = this._processTemplateSourceText( target, text ); //let nodes = outputValue.map( o => document.createTextNode( o ) ); for ( let i = 0; i < nodes.length; i++ ) { textNode.parentElement.insertBefore( nodes[ i ], textNode ); } textNode.parentElement.removeChild( textNode ); } _processTemplateSourceText( target:Element, text:string ):Node[] { let lexer = new TemplatesSourceLexer(); let lexerEvents = lexer.lexToList( text ); let outputs:Node[] = []; let defaultValue = null; for ( let i = 0; i < lexerEvents.length; i++ ) { let event = lexerEvents[ i ]; let subMatchValue = event.getMatch( text ); if ( event.isError ) { console.log( `Error lexing '${text}' at position ${event.offset}` ); return [ document.createTextNode( text ) ]; } if ( event.isDone ) { continue; } if ( TemplatesSourceLexer.DEFAULT === event.type ) { defaultValue = TemplatesSourceLexer.defaultMatcher.getMatch( subMatchValue, 1 ); continue; } if ( TemplatesSourceLexer.CONTENT === event.type ) { outputs.push( document.createTextNode( subMatchValue ) ); } else { TemplateSourceMatchers.regex.lastIndex = 0; let subMatch = TemplateSourceMatchers.regex.exec( subMatchValue ); let templateSource = TemplateSourceMatchers.createTemplateSource( subMatch ); if ( defaultValue ) { templateSource.defaultValue = defaultValue; } let targetNodes = templateSource.getNodes( target ); outputs = outputs.concat( targetNodes ); //targetNodes.forEach( n => outputs.push( n ) ); //let sourceValue = templateSource.getInnerHTML( target ); //outputs.push( sourceValue ); } defaultValue = null; } return outputs; } /* static createElementSource( match:RegExpExecArray ) { let elementSource = new TemplateSource(); //console.log( "Create Element SOurce", match ); if ( match[ 1 ] ) { elementSource.selector = match[ 1 ]; elementSource.attribute = match[ 2 ]; } else if ( match[ 3 ] ) { elementSource.selector = match[ 3 ]; } else if ( match[ 4 ] ) { elementSource.attribute = match[ 4 ]; } else if ( match[ 5 ] ) { elementSource.innerHTML = true; } return elementSource; } */ }