UNPKG

ractive

Version:

Next-generation DOM manipulation

170 lines (134 loc) 4.7 kB
import types from 'config/types'; import mustacheType from 'parse/converters/mustache/type'; import handlebarsBlockCodes from 'parse/converters/mustache/handlebarsBlockCodes'; import 'legacy'; var indexRefPattern = /^\s*:\s*([a-zA-Z_$][a-zA-Z_$0-9]*)/, arrayMemberPattern = /^[0-9][1-9]*$/, handlebarsBlockPattern = new RegExp( '^(' + Object.keys( handlebarsBlockCodes ).join( '|' ) + ')\\b' ), legalReference; legalReference = /^[a-zA-Z$_0-9]+(?:(\.[a-zA-Z$_0-9]+)|(\[[a-zA-Z$_0-9]+\]))*$/; export default function ( parser, delimiterType ) { var start, pos, mustache, type, block, expression, i, remaining, index, delimiters, referenceExpression; start = parser.pos; mustache = {}; delimiters = parser[ delimiterType.delimiters ]; if ( delimiterType.isStatic ) { mustache.s = true; } // Determine mustache type if ( delimiterType.isTriple ) { mustache.t = types.TRIPLE; } else { // We need to test for expressions before we test for mustache type, because // an expression that begins '!' looks a lot like a comment if ( parser.remaining()[0] === '!' && ( expression = parser.readExpression() ) ) { mustache.t = types.INTERPOLATOR; // Was it actually an expression, or a comment block in disguise? parser.allowWhitespace(); if ( parser.matchString( delimiters[1] ) ) { // expression parser.pos -= delimiters[1].length; } else { // comment block parser.pos = start; expression = null; } } if ( !expression ) { type = mustacheType( parser ); mustache.t = type || types.INTERPOLATOR; // default // See if there's an explicit section type e.g. {{#with}}...{{/with}} if ( type === types.SECTION ) { if ( block = parser.matchPattern( handlebarsBlockPattern ) ) { mustache.n = block; } parser.allowWhitespace(); } // if it's a comment or a section closer, allow any contents except '}}' else if ( type === types.COMMENT || type === types.CLOSING ) { remaining = parser.remaining(); index = remaining.indexOf( delimiters[1] ); if ( index !== -1 ) { mustache.r = remaining.substr( 0, index ); parser.pos += index; return mustache; } } } } if ( !expression ) { // allow whitespace parser.allowWhitespace(); // get expression expression = parser.readExpression(); // With certain valid references that aren't valid expressions, // e.g. {{1.foo}}, we have a problem: it looks like we've got an // expression, but the expression didn't consume the entire // reference. So we need to check that the mustache delimiters // appear next, unless there's an index reference (i.e. a colon) remaining = parser.remaining(); if ( ( remaining.substr( 0, delimiters[1].length ) !== delimiters[1] ) && ( remaining.charAt( 0 ) !== ':' ) ) { pos = parser.pos; parser.pos = start; remaining = parser.remaining(); index = remaining.indexOf( delimiters[1] ); if ( index !== -1 ) { mustache.r = remaining.substr( 0, index ).trim(); // Check it's a legal reference if ( !legalReference.test( mustache.r ) ) { parser.error( 'Expected a legal Mustache reference' ); } parser.pos += index; return mustache; } parser.pos = pos; // reset, so we get more informative error messages } } if ( expression ) { while ( expression.t === types.BRACKETED && expression.x ) { expression = expression.x; } // special case - integers should be treated as array members references, // rather than as expressions in their own right if ( expression.t === types.REFERENCE ) { mustache.r = expression.n; } else { if ( expression.t === types.NUMBER_LITERAL && arrayMemberPattern.test( expression.v ) ) { mustache.r = expression.v; } else if ( referenceExpression = getReferenceExpression( parser, expression ) ) { mustache.rx = referenceExpression; } else { mustache.x = parser.flattenExpression( expression ); } } } // optional index reference if ( i = parser.matchPattern( indexRefPattern ) ) { mustache.i = i; } return mustache; } // TODO refactor this! it's bewildering function getReferenceExpression ( parser, expression ) { var members = [], refinement; while ( expression.t === types.MEMBER && expression.r.t === types.REFINEMENT ) { refinement = expression.r; if ( refinement.x ) { if ( refinement.x.t === types.REFERENCE ) { members.unshift( refinement.x ); } else { members.unshift( parser.flattenExpression( refinement.x ) ); } } else { members.unshift( refinement.n ); } expression = expression.x; } if ( expression.t !== types.REFERENCE ) { return null; } return { r: expression.n, m: members }; }