406 lines
10 KiB
TypeScript
406 lines
10 KiB
TypeScript
![]() |
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
*/
|
||
|
|
||
|
}
|