|
|
@ -1,332 +1,274 @@
|
|
|
|
import { getLocator } from 'locate-character';
|
|
|
|
import { getLocator } from 'locate-character';
|
|
|
|
import deindent from './utils/deindent.js';
|
|
|
|
import deindent from './utils/deindent.js';
|
|
|
|
import walkHtml from './utils/walkHtml.js';
|
|
|
|
import walkHtml from './utils/walkHtml.js';
|
|
|
|
|
|
|
|
import flattenReference from './utils/flattenReference.js';
|
|
|
|
|
|
|
|
|
|
|
|
const ROOT = 'options.target';
|
|
|
|
function createRenderer ( fragment ) {
|
|
|
|
|
|
|
|
return deindent`
|
|
|
|
|
|
|
|
function ${fragment.name} ( target${fragment.useAnchor ? ', anchor' : ''} ) {
|
|
|
|
|
|
|
|
${fragment.initStatements.join( '\n\n' )}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
|
|
update: function ( ${fragment.contextChain.join( ', ' )} ) {
|
|
|
|
|
|
|
|
${fragment.updateStatements.join( '\n\n' )}
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
teardown: function () {
|
|
|
|
|
|
|
|
${fragment.teardownStatements.join( '\n\n' )}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
`;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export default function generate ( parsed, template ) {
|
|
|
|
export default function generate ( parsed, template ) {
|
|
|
|
|
|
|
|
const locator = getLocator( template );
|
|
|
|
|
|
|
|
const renderers = [];
|
|
|
|
|
|
|
|
|
|
|
|
const counters = {
|
|
|
|
const counters = {
|
|
|
|
fragment: 0,
|
|
|
|
|
|
|
|
element: 0,
|
|
|
|
|
|
|
|
text: 0,
|
|
|
|
|
|
|
|
anchor: 0,
|
|
|
|
|
|
|
|
if: 0,
|
|
|
|
if: 0,
|
|
|
|
each: 0,
|
|
|
|
each: 0
|
|
|
|
loop: 0
|
|
|
|
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const initStatements = [];
|
|
|
|
|
|
|
|
const setStatements = [ deindent`
|
|
|
|
|
|
|
|
const oldState = state;
|
|
|
|
|
|
|
|
state = Object.assign( {}, oldState, newState );
|
|
|
|
|
|
|
|
` ];
|
|
|
|
|
|
|
|
const teardownStatements = [];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// TODO add contents of <script> tag, with `export default` replaced with `var template =`
|
|
|
|
// TODO add contents of <script> tag, with `export default` replaced with `var template =`
|
|
|
|
// TODO css
|
|
|
|
// TODO css
|
|
|
|
|
|
|
|
|
|
|
|
const locator = getLocator( template );
|
|
|
|
let current = {
|
|
|
|
|
|
|
|
useAnchor: false,
|
|
|
|
|
|
|
|
name: 'renderMainFragment',
|
|
|
|
|
|
|
|
target: 'target',
|
|
|
|
|
|
|
|
|
|
|
|
parsed.html.children.forEach( child => {
|
|
|
|
initStatements: [],
|
|
|
|
const declarations = [];
|
|
|
|
updateStatements: [],
|
|
|
|
|
|
|
|
teardownStatements: [],
|
|
|
|
|
|
|
|
|
|
|
|
let current = {
|
|
|
|
contexts: {},
|
|
|
|
target: ROOT,
|
|
|
|
contextChain: [ 'context' ],
|
|
|
|
conditions: [],
|
|
|
|
|
|
|
|
children: [],
|
|
|
|
counters: {
|
|
|
|
renderBlocks: [],
|
|
|
|
element: 0,
|
|
|
|
removeBlocks: [],
|
|
|
|
text: 0,
|
|
|
|
anchor: null,
|
|
|
|
anchor: 0
|
|
|
|
renderImmediately: true
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
parent: null
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const stack = [ current ];
|
|
|
|
function flattenExpression ( node, contexts ) {
|
|
|
|
|
|
|
|
const flattened = flattenReference( node );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ( flattened ) {
|
|
|
|
|
|
|
|
if ( flattened.name in contexts ) return flattened.keypath;
|
|
|
|
|
|
|
|
// TODO handle globals, e.g. {{Math.round(foo)}}
|
|
|
|
|
|
|
|
return `context.${flattened.keypath}`;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
throw new Error( 'TODO expressions' );
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
parsed.html.children.forEach( child => {
|
|
|
|
walkHtml( child, {
|
|
|
|
walkHtml( child, {
|
|
|
|
Element: {
|
|
|
|
Element: {
|
|
|
|
enter ( node ) {
|
|
|
|
enter ( node ) {
|
|
|
|
const target = `element_${counters.element++}`;
|
|
|
|
const name = `element_${current.counters.element++}`;
|
|
|
|
|
|
|
|
|
|
|
|
stack.push( current );
|
|
|
|
current.initStatements.push( deindent`
|
|
|
|
|
|
|
|
var ${name} = document.createElement( '${node.name}' );
|
|
|
|
declarations.push( `var ${target};` );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ( current.renderImmediately ) {
|
|
|
|
|
|
|
|
current.renderBlocks.push( deindent`
|
|
|
|
|
|
|
|
${target} = document.createElement( '${node.name}' );
|
|
|
|
|
|
|
|
${current.target}.appendChild( ${target} );
|
|
|
|
|
|
|
|
` );
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
current.renderBlocks.push( deindent`
|
|
|
|
|
|
|
|
${target} = document.createElement( '${node.name}' );
|
|
|
|
|
|
|
|
${current.anchor}.parentNode.insertBefore( ${target}, ${current.anchor} );
|
|
|
|
|
|
|
|
` );
|
|
|
|
` );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
current.removeBlocks.push( deindent`
|
|
|
|
current.teardownStatements.push( deindent`
|
|
|
|
${target}.parentNode.removeChild( ${target} );
|
|
|
|
${name}.parentNode.removeChild( ${name} );
|
|
|
|
` );
|
|
|
|
` );
|
|
|
|
|
|
|
|
|
|
|
|
current = {
|
|
|
|
current = Object.assign( {}, current, {
|
|
|
|
target,
|
|
|
|
target: name,
|
|
|
|
conditions: current.conditions,
|
|
|
|
parent: current
|
|
|
|
children: current.children,
|
|
|
|
});
|
|
|
|
renderBlocks: current.renderBlocks,
|
|
|
|
|
|
|
|
removeBlocks: current.removeBlocks,
|
|
|
|
|
|
|
|
anchor: current.anchor,
|
|
|
|
|
|
|
|
renderImmediately: false
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
leave () {
|
|
|
|
leave () {
|
|
|
|
stack.pop();
|
|
|
|
const name = current.target;
|
|
|
|
current = stack[ stack.length - 1 ];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Text: {
|
|
|
|
current = current.parent;
|
|
|
|
enter ( node ) {
|
|
|
|
|
|
|
|
if ( current.target === ROOT ) {
|
|
|
|
|
|
|
|
const identifier = `text_${counters.text++}`;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
declarations.push( `var ${identifier};` );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
current.renderBlocks.push( deindent`
|
|
|
|
if ( current.useAnchor && current.target === 'target' ) {
|
|
|
|
${identifier} = document.createTextNode( ${JSON.stringify( node.data )} );
|
|
|
|
current.initStatements.push( deindent`
|
|
|
|
${current.target}.appendChild( ${identifier} );
|
|
|
|
target.insertBefore( ${name}, anchor );
|
|
|
|
` );
|
|
|
|
` );
|
|
|
|
|
|
|
|
} else {
|
|
|
|
current.removeBlocks.push( deindent`
|
|
|
|
current.initStatements.push( deindent`
|
|
|
|
${identifier}.parentNode.removeChild( ${identifier} );
|
|
|
|
${current.target}.appendChild( ${name} );
|
|
|
|
${identifier} = null;
|
|
|
|
|
|
|
|
` );
|
|
|
|
` );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
else {
|
|
|
|
Text: {
|
|
|
|
current.renderBlocks.push( deindent`
|
|
|
|
enter ( node ) {
|
|
|
|
${current.target}.appendChild( document.createTextNode( ${JSON.stringify( node.data )} ) );
|
|
|
|
current.initStatements.push( deindent`
|
|
|
|
|
|
|
|
${current.target}.appendChild( document.createTextNode( ${JSON.stringify( node.data ) }) );
|
|
|
|
` );
|
|
|
|
` );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
MustacheTag: {
|
|
|
|
MustacheTag: {
|
|
|
|
enter ( node ) {
|
|
|
|
enter ( node ) {
|
|
|
|
const identifier = `text_${counters.text++}`;
|
|
|
|
const name = `text_${current.counters.text++}`;
|
|
|
|
const expression = node.expression.type === 'Identifier' ? node.expression.name : 'TODO'; // TODO handle block-local state
|
|
|
|
const expression = flattenExpression( node.expression, current.contexts );
|
|
|
|
|
|
|
|
|
|
|
|
declarations.push( `var ${identifier};` );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
current.renderBlocks.push( deindent`
|
|
|
|
current.initStatements.push( deindent`
|
|
|
|
${identifier} = document.createTextNode( '' );
|
|
|
|
var ${name} = document.createTextNode( '' );
|
|
|
|
${current.target}.appendChild( ${identifier} );
|
|
|
|
var ${name}_value = '';
|
|
|
|
|
|
|
|
${current.target}.appendChild( ${name} );
|
|
|
|
` );
|
|
|
|
` );
|
|
|
|
|
|
|
|
|
|
|
|
setStatements.push( deindent`
|
|
|
|
current.updateStatements.push( deindent`
|
|
|
|
if ( state.${expression} !== oldState.${expression} ) { // TODO and conditions
|
|
|
|
if ( ${expression} !== ${name}_value ) {
|
|
|
|
${identifier}.data = state.${expression};
|
|
|
|
${name}_value = ${expression};
|
|
|
|
|
|
|
|
${name}.data = ${name}_value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
` );
|
|
|
|
` );
|
|
|
|
|
|
|
|
|
|
|
|
current.removeBlocks.push( deindent`
|
|
|
|
|
|
|
|
${identifier}.parentNode.removeChild( ${identifier} );
|
|
|
|
|
|
|
|
${identifier} = null;
|
|
|
|
|
|
|
|
` );
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
IfBlock: {
|
|
|
|
IfBlock: {
|
|
|
|
enter ( node ) {
|
|
|
|
enter ( node ) {
|
|
|
|
const anchor = `anchor_${counters.anchor++}`;
|
|
|
|
const i = counters.if++;
|
|
|
|
const suffix = `if_${counters.if++}`;
|
|
|
|
const name = `ifBlock_${i}`;
|
|
|
|
|
|
|
|
const renderer = `renderIfBlock_${i}`;
|
|
|
|
declarations.push( `var ${anchor};` );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const expression = node.expression.type === 'Identifier' ? node.expression.name : 'TODO'; // TODO handle block-local state
|
|
|
|
const expression = flattenExpression( node.expression, current.contexts );
|
|
|
|
|
|
|
|
|
|
|
|
current.renderBlocks.push( deindent`
|
|
|
|
current.initStatements.push( deindent`
|
|
|
|
${anchor} = document.createComment( '#if ${template.slice( node.expression.start, node.expression.end)}' );
|
|
|
|
var ${name}_anchor = document.createComment( '#if ${template.slice( node.expression.start, node.expression.end )}' );
|
|
|
|
${current.target}.appendChild( ${anchor} );
|
|
|
|
${current.target}.appendChild( ${name}_anchor );
|
|
|
|
|
|
|
|
var ${name} = null;
|
|
|
|
` );
|
|
|
|
` );
|
|
|
|
|
|
|
|
|
|
|
|
current.removeBlocks.push( deindent`
|
|
|
|
current.updateStatements.push( deindent`
|
|
|
|
${anchor}.parentNode.removeChild( ${anchor} );
|
|
|
|
if ( ${expression} && !${name} ) {
|
|
|
|
${anchor} = null;
|
|
|
|
${name} = ${renderer}( ${current.target}, ${name}_anchor );
|
|
|
|
` );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
current = {
|
|
|
|
|
|
|
|
renderName: `render_${suffix}`,
|
|
|
|
|
|
|
|
removeName: `remove_${suffix}`,
|
|
|
|
|
|
|
|
target: current.target,
|
|
|
|
|
|
|
|
conditions: current.conditions.concat( expression ),
|
|
|
|
|
|
|
|
renderBlocks: [],
|
|
|
|
|
|
|
|
removeBlocks: [],
|
|
|
|
|
|
|
|
anchor,
|
|
|
|
|
|
|
|
renderImmediately: false
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
setStatements.push( deindent`
|
|
|
|
|
|
|
|
// TODO account for conditions (nested ifs)
|
|
|
|
|
|
|
|
if ( state.${expression} && !oldState.${expression} ) ${current.renderName}();
|
|
|
|
|
|
|
|
else if ( !state.${expression} && oldState.${expression} ) ${current.removeName}();
|
|
|
|
|
|
|
|
` );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
teardownStatements.push( deindent`
|
|
|
|
|
|
|
|
// TODO account for conditions (nested ifs)
|
|
|
|
|
|
|
|
if ( state.${expression} ) ${current.removeName}();
|
|
|
|
|
|
|
|
` );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
stack.push( current );
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
leave ( node ) {
|
|
|
|
|
|
|
|
const { line, column } = locator( node.start );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
initStatements.push( deindent`
|
|
|
|
|
|
|
|
// (${line}:${column}) {{#if ${template.slice( node.expression.start, node.expression.end )}}}...{{/if}}
|
|
|
|
|
|
|
|
function ${current.renderName} () {
|
|
|
|
|
|
|
|
${current.renderBlocks.join( '\n\n' )}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function ${current.removeName} () {
|
|
|
|
else if ( !${expression} && ${name} ) {
|
|
|
|
${current.removeBlocks.join( '\n\n' )}
|
|
|
|
${name}.teardown();
|
|
|
|
|
|
|
|
${name} = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
` );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
stack.pop();
|
|
|
|
if ( ${name} ) {
|
|
|
|
current = stack[ stack.length - 1 ];
|
|
|
|
${name}.update( context );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
EachBlock: {
|
|
|
|
|
|
|
|
enter ( node ) {
|
|
|
|
|
|
|
|
const loopIndex = counters.loop++;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const anchor = `anchor_${counters.anchor++}`;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
declarations.push( `var fragment_${loopIndex} = document.createDocumentFragment();` );
|
|
|
|
|
|
|
|
declarations.push( `var ${anchor};` );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const expression = node.expression.type === 'Identifier' ? node.expression.name : 'TODO'; // TODO handle block-local state
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
current.renderBlocks.push( deindent`
|
|
|
|
|
|
|
|
${anchor} = document.createComment( '#each ${template.slice( node.expression.start, node.expression.end)}' );
|
|
|
|
|
|
|
|
${current.target}.appendChild( ${anchor} );
|
|
|
|
|
|
|
|
` );
|
|
|
|
` );
|
|
|
|
|
|
|
|
|
|
|
|
current.removeBlocks.push( deindent`
|
|
|
|
current.teardownStatements.push( deindent`
|
|
|
|
${anchor}.parentNode.removeChild( ${anchor} );
|
|
|
|
if ( ${name} ) ${name}.teardown();
|
|
|
|
${anchor} = null;
|
|
|
|
|
|
|
|
` );
|
|
|
|
` );
|
|
|
|
|
|
|
|
|
|
|
|
current = {
|
|
|
|
current = {
|
|
|
|
target: `fragment_${loopIndex}`,
|
|
|
|
useAnchor: true,
|
|
|
|
expression,
|
|
|
|
name: renderer,
|
|
|
|
conditions: current.conditions,
|
|
|
|
target: 'target',
|
|
|
|
renderBlocks: [],
|
|
|
|
|
|
|
|
removeBlocks: [],
|
|
|
|
|
|
|
|
anchor,
|
|
|
|
|
|
|
|
loopIndex,
|
|
|
|
|
|
|
|
renderImmediately: true
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
setStatements.push( deindent`
|
|
|
|
contextChain: current.contextChain,
|
|
|
|
// TODO account for conditions (nested ifs)
|
|
|
|
|
|
|
|
if ( '${expression}' in state ) each_${loopIndex}.update();
|
|
|
|
|
|
|
|
` );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// need to add teardown logic if this is at the
|
|
|
|
initStatements: [],
|
|
|
|
// top level (TODO or if there are event handlers attached?)
|
|
|
|
updateStatements: [],
|
|
|
|
if ( current.target === ROOT ) {
|
|
|
|
teardownStatements: [],
|
|
|
|
teardownStatements.push( deindent`
|
|
|
|
|
|
|
|
if ( true ) { // <!-- TODO conditions
|
|
|
|
|
|
|
|
for ( let i = 0; i < state.${expression}.length; i += 1 ) {
|
|
|
|
|
|
|
|
each_${loopIndex}.removeIteration( i );
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
` );
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
stack.push( current );
|
|
|
|
counters: {
|
|
|
|
|
|
|
|
element: 0,
|
|
|
|
|
|
|
|
text: 0,
|
|
|
|
|
|
|
|
anchor: 0
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
leave ( node ) {
|
|
|
|
parent: current
|
|
|
|
const { line, column } = locator( node.start );
|
|
|
|
};
|
|
|
|
|
|
|
|
},
|
|
|
|
const loopIndex = current.loopIndex;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
initStatements.push( deindent`
|
|
|
|
leave () {
|
|
|
|
// (${line}:${column}) {{#each ${template.slice( node.expression.start, node.expression.end )}}}...{{/each}}
|
|
|
|
renderers.push( createRenderer( current ) );
|
|
|
|
${current.renderBlocks.join( '\n\n' )}
|
|
|
|
current = current.parent;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
var each_${loopIndex} = {
|
|
|
|
EachBlock: {
|
|
|
|
iterations: [],
|
|
|
|
enter ( node ) {
|
|
|
|
|
|
|
|
const i = counters.each++;
|
|
|
|
|
|
|
|
const name = `eachBlock_${i}`;
|
|
|
|
|
|
|
|
const renderer = `renderEachBlock_${i}`;
|
|
|
|
|
|
|
|
|
|
|
|
update: function () {
|
|
|
|
const expression = flattenExpression( node.expression, current.contexts );
|
|
|
|
var target = document.createDocumentFragment();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var i;
|
|
|
|
current.initStatements.push( deindent`
|
|
|
|
|
|
|
|
var ${name}_anchor = document.createComment( '#each ${template.slice( node.expression.start, node.expression.end )}' );
|
|
|
|
|
|
|
|
${current.target}.appendChild( ${name}_anchor );
|
|
|
|
|
|
|
|
var ${name}_iterations = [];
|
|
|
|
|
|
|
|
const ${name}_fragment = document.createDocumentFragment();
|
|
|
|
|
|
|
|
` );
|
|
|
|
|
|
|
|
|
|
|
|
for ( i = 0; i < state.${current.expression}.length; i += 1 ) {
|
|
|
|
current.updateStatements.push( deindent`
|
|
|
|
if ( !this.iterations[i] ) {
|
|
|
|
for ( var i = 0; i < ${expression}.length; i += 1 ) {
|
|
|
|
this.iterations[i] = this.renderIteration( target );
|
|
|
|
if ( !${name}_iterations[i] ) {
|
|
|
|
|
|
|
|
${name}_iterations[i] = ${renderer}( ${name}_fragment );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const iteration = this.iterations[i];
|
|
|
|
const iteration = ${name}_iterations[i];
|
|
|
|
this.updateIteration( this.iterations[i], state.${current.expression}[i] );
|
|
|
|
${name}_iterations[i].update( ${current.contextChain.join( ', ' )}, ${expression}[i] );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for ( ; i < this.iterations.length; i += 1 ) {
|
|
|
|
for ( var i = ${expression}.length; i < ${name}_iterations.length; i += 1 ) {
|
|
|
|
this.removeIteration( i );
|
|
|
|
${name}_iterations[i].teardown();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
${current.anchor}.parentNode.insertBefore( target, ${current.anchor} );
|
|
|
|
${name}_anchor.parentNode.insertBefore( ${name}_fragment, ${name}_anchor );
|
|
|
|
each_${loopIndex}.length = state.${current.expression}.length;
|
|
|
|
${name}_iterations.length = ${expression}.length;
|
|
|
|
},
|
|
|
|
` );
|
|
|
|
|
|
|
|
|
|
|
|
renderIteration: function ( target ) {
|
|
|
|
current.teardownStatements.push( deindent`
|
|
|
|
var fragment = fragment_0.cloneNode( true );
|
|
|
|
for ( let i = 0; i < ${name}_iterations.length; i += 1 ) {
|
|
|
|
|
|
|
|
${name}_iterations[i].teardown();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
` );
|
|
|
|
|
|
|
|
|
|
|
|
var element_0 = fragment.childNodes[0];
|
|
|
|
const contexts = Object.assign( {}, current.contexts );
|
|
|
|
var text_0 = element_0.childNodes[0];
|
|
|
|
contexts[ node.context ] = true;
|
|
|
|
|
|
|
|
|
|
|
|
var iteration = {
|
|
|
|
current = {
|
|
|
|
element_0: element_0,
|
|
|
|
useAnchor: false,
|
|
|
|
text_0: text_0
|
|
|
|
name: renderer,
|
|
|
|
};
|
|
|
|
target: 'target',
|
|
|
|
|
|
|
|
|
|
|
|
target.appendChild( fragment );
|
|
|
|
contexts,
|
|
|
|
return iteration;
|
|
|
|
contextChain: current.contextChain.concat( node.context ),
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
initStatements: [],
|
|
|
|
|
|
|
|
updateStatements: [],
|
|
|
|
|
|
|
|
teardownStatements: [],
|
|
|
|
|
|
|
|
|
|
|
|
updateIteration: function ( iteration, context ) {
|
|
|
|
counters: {
|
|
|
|
iteration.text_0.data = context;
|
|
|
|
element: 0,
|
|
|
|
|
|
|
|
text: 0,
|
|
|
|
|
|
|
|
anchor: 0
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
removeIteration: function ( i ) {
|
|
|
|
parent: current
|
|
|
|
var iteration = this.iterations[i];
|
|
|
|
|
|
|
|
iteration.element_0.parentNode.removeChild( iteration.element_0 );
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
};
|
|
|
|
` );
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
teardownStatements.push( ...current.removeBlocks );
|
|
|
|
leave () {
|
|
|
|
|
|
|
|
renderers.push( createRenderer( current ) );
|
|
|
|
|
|
|
|
|
|
|
|
stack.pop();
|
|
|
|
current = current.parent;
|
|
|
|
current = stack[ stack.length - 1 ];
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
initStatements.push( ...current.renderBlocks );
|
|
|
|
|
|
|
|
initStatements.unshift( declarations.join( '\n' ) );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
teardownStatements.push( ...current.removeBlocks );
|
|
|
|
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
teardownStatements.push( 'state = {};' );
|
|
|
|
renderers.push( createRenderer( current ) );
|
|
|
|
|
|
|
|
|
|
|
|
const code = deindent`
|
|
|
|
const code = deindent`
|
|
|
|
|
|
|
|
${renderers.reverse().join( '\n\n' )}
|
|
|
|
|
|
|
|
|
|
|
|
export default function createComponent ( options ) {
|
|
|
|
export default function createComponent ( options ) {
|
|
|
|
var component = {};
|
|
|
|
var component = {};
|
|
|
|
var state = {};
|
|
|
|
var state = {};
|
|
|
@ -373,16 +315,18 @@ export default function generate ( parsed, template ) {
|
|
|
|
|
|
|
|
|
|
|
|
// component-specific methods
|
|
|
|
// component-specific methods
|
|
|
|
component.set = function set ( newState ) {
|
|
|
|
component.set = function set ( newState ) {
|
|
|
|
${setStatements.join( '\n\n' )}
|
|
|
|
Object.assign( state, newState );
|
|
|
|
|
|
|
|
mainFragment.update( state );
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
component.teardown = function teardown () {
|
|
|
|
component.teardown = function teardown () {
|
|
|
|
${teardownStatements.join( '\n\n' )}
|
|
|
|
mainFragment.teardown();
|
|
|
|
};
|
|
|
|
mainFragment = null;
|
|
|
|
|
|
|
|
|
|
|
|
// initialisation
|
|
|
|
state = {};
|
|
|
|
${initStatements.join( '\n\n' )}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let mainFragment = renderMainFragment( options.target );
|
|
|
|
component.set( options.data );
|
|
|
|
component.set( options.data );
|
|
|
|
|
|
|
|
|
|
|
|
return component;
|
|
|
|
return component;
|
|
|
|