library-ts/browser/templates/TemplateReplacer.ts

406 lines
10 KiB
TypeScript
Raw Normal View History

2025-03-08 08:16:54 +00:00
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( "&amp;&amp;", "&" );
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;
}
*/
}