dist/okanjo.js
/**
         * Breaks up the given `template` string into a tree of tokens. If the `tags`
         * argument is given here it must be an array with two string values: the
         * opening and closing tags used in the template (e.g. [ "<%", "%>" ]). Of
         * course, the default is to use mustaches (i.e. mustache.tags).
         *
         * A token is an array with at least 4 elements. The first element is the
         * mustache symbol that was used inside the tag, e.g. "#" or "&". If the tag
         * did not contain a symbol (i.e. {{myValue}}) this element is "name". For
         * all text that appears outside a symbol this element is "text".
         *
         * The second element of a token is its "value". For mustache tags this is
         * whatever else was inside the tag besides the opening symbol. For text tokens
         * this is the text itself.
         *
         * The third and fourth elements of the token are the start and end indices,
         * respectively, of the token in the original template.
         *
         * Tokens that are the root node of a subtree contain two more elements: 1) an
         * array of tokens in the subtree and 2) the index in the original template at
         * which the closing tag for that section begins.
         */
Breaks up the given `template` string into a tree of tokens. If the `tags`
argument is given here it must be an array with two string values: the
opening and closing tags used in the template . Of
course, the default is to use mustaches .

A token is an array with at least 4 elements. The first element is the
mustache symbol that was used inside the tag, e.g.

The second element of a token is its "value". For mustache tags this is
whatever else was inside the tag besides the opening symbol. For text tokens
this is the text itself.

The third and fourth elements of the token are the start and end indices,
respectively, of the token in the original template.

Tokens that are the root node of a subtree contain two more elements: 1) an
array of tokens in the subtree and 2) the index in the original template at
which the closing tag for that section begins.
Breaks up the given ` template ` string into a tree of tokens . If the ` tags ` argument is given here it must be an array with two string values : the opening and closing tags used in the template . Of course the default is to use mustaches . A token is an array with at least 4 elements . The first element is the mustache symbol that was used inside the tag e . g . The second element of a token is its " value " . For mustache tags this is whatever else was inside the tag besides the opening symbol . For text tokens this is the text itself . The third and fourth elements of the token are the start and end indices respectively of the token in the original template . Tokens that are the root node of a subtree contain two more elements : 1 ) an array of tokens in the subtree and 2 ) the index in the original template at which the closing tag for that section begins .
function parseTemplate(template, tags) {
            if (!template) return [];
            var sections = []; 
            var tokens = []; 
            var spaces = []; 
            var hasTag = false; 
            var nonSpace = false; 
            function stripSpace() {
                if (hasTag && !nonSpace) {
                    while (spaces.length) {
                        delete tokens[spaces.pop()];
                    }
                } else {
                    spaces = [];
                }
                hasTag = false;
                nonSpace = false;
            }
            var openingTagRe, closingTagRe, closingCurlyRe;
            function compileTags(tagsToCompile) {
                if (typeof tagsToCompile === 'string') tagsToCompile = tagsToCompile.split(spaceRe, 2);
                if (!isArray(tagsToCompile) || tagsToCompile.length !== 2) throw new Error('Invalid tags: ' + tagsToCompile);
                openingTagRe = new RegExp(escapeRegExp(tagsToCompile[0]) + '\\s*');
                closingTagRe = new RegExp('\\s*' + escapeRegExp(tagsToCompile[1]));
                closingCurlyRe = new RegExp('\\s*' + escapeRegExp('}' + tagsToCompile[1]));
            }
            compileTags(tags || mustache.tags);
            var scanner = new Scanner(template);
            var start, type, value, chr, token, openSection;
            while (!scanner.eos()) {
                start = scanner.pos;
                value = scanner.scanUntil(openingTagRe);
                if (value) {
                    for (var i = 0, valueLength = value.length; i < valueLength; ++i) {
                        chr = value.charAt(i);
                        if (isWhitespace(chr)) {
                            spaces.push(tokens.length);
                        } else {
                            nonSpace = true;
                        }
                        tokens.push(['text', chr, start, start + 1]);
                        start += 1;
                        if (chr === '\n') stripSpace();
                    }
                }
                if (!scanner.scan(openingTagRe)) break;
                hasTag = true;
                type = scanner.scan(tagRe) || 'name';
                scanner.scan(whiteRe);
                if (type === '=') {
                    value = scanner.scanUntil(equalsRe);
                    scanner.scan(equalsRe);
                    scanner.scanUntil(closingTagRe);
                } else if (type === '{') {
                    value = scanner.scanUntil(closingCurlyRe);
                    scanner.scan(curlyRe);
                    scanner.scanUntil(closingTagRe);
                    type = '&';
                } else {
                    value = scanner.scanUntil(closingTagRe);
                }
                if (!scanner.scan(closingTagRe)) throw new Error('Unclosed tag at ' + scanner.pos);
                token = [type, value, start, scanner.pos];
                tokens.push(token);
                if (type === '#' || type === '^') {
                    sections.push(token);
                } else if (type === '/') {
                    openSection = sections.pop();
                    if (!openSection) throw new Error('Unopened section "' + value + '" at ' + start);
                    if (openSection[1] !== value) throw new Error('Unclosed section "' + openSection[1] + '" at ' + start);
                } else if (type === 'name' || type === '{' || type === '&') {
                    nonSpace = true;
                } else if (type === '=') {
                    compileTags(value);
                }
            }
            openSection = sections.pop();
            if (openSection) throw new Error('Unclosed section "' + openSection[1] + '" at ' + scanner.pos);
            return nestTokens(squashTokens(tokens));
        }
function parseTemplate ( template , tags ) { if ( ! template ) return [ ] ; var sections = [ ] ; var tokens = [ ] ; var spaces = [ ] ; var hasTag = false ; var nonSpace = false ; function stripSpace ( ) { if ( hasTag && ! nonSpace ) { while ( spaces . length ) { delete tokens [ spaces . pop ( ) ] ; } } else { spaces = [ ] ; } hasTag = false ; nonSpace = false ; } var openingTagRe , closingTagRe , closingCurlyRe ; function compileTags ( tagsToCompile ) { if ( typeof tagsToCompile === 'string' ) tagsToCompile = tagsToCompile . split ( spaceRe , 2 ) ; if ( ! isArray ( tagsToCompile ) || tagsToCompile . length !== 2 ) throw new Error ( 'Invalid tags: ' + tagsToCompile ) ; openingTagRe = new RegExp ( escapeRegExp ( tagsToCompile [ 0 ] ) + '\\s*' ) ; closingTagRe = new RegExp ( '\\s*' + escapeRegExp ( tagsToCompile [ 1 ] ) ) ; closingCurlyRe = new RegExp ( '\\s*' + escapeRegExp ( '}' + tagsToCompile [ 1 ] ) ) ; } compileTags ( tags || mustache . tags ) ; var scanner = new Scanner ( template ) ; var start , type , value , chr , token , openSection ; while ( ! scanner . eos ( ) ) { start = scanner . pos ; value = scanner . scanUntil ( openingTagRe ) ; if ( value ) { for ( var i = 0 , valueLength = value . length ; i < valueLength ; ++ i ) { chr = value . charAt ( i ) ; if ( isWhitespace ( chr ) ) { spaces . push ( tokens . length ) ; } else { nonSpace = true ; } tokens . push ( [ 'text' , chr , start , start + 1 ] ) ; start += 1 ; if ( chr === '\n' ) stripSpace ( ) ; } } if ( ! scanner . scan ( openingTagRe ) ) break ; hasTag = true ; type = scanner . scan ( tagRe ) || 'name' ; scanner . scan ( whiteRe ) ; if ( type === '=' ) { value = scanner . scanUntil ( equalsRe ) ; scanner . scan ( equalsRe ) ; scanner . scanUntil ( closingTagRe ) ; } else if ( type === '{' ) { value = scanner . scanUntil ( closingCurlyRe ) ; scanner . scan ( curlyRe ) ; scanner . scanUntil ( closingTagRe ) ; type = '&' ; } else { value = scanner . scanUntil ( closingTagRe ) ; } if ( ! scanner . scan ( closingTagRe ) ) throw new Error ( 'Unclosed tag at ' + scanner . pos ) ; token = [ type , value , start , scanner . pos ] ; tokens . push ( token ) ; if ( type === '#' || type === '^' ) { sections . push ( token ) ; } else if ( type === '/' ) { openSection = sections . pop ( ) ; if ( ! openSection ) throw new Error ( 'Unopened section "' + value + '" at ' + start ) ; if ( openSection [ 1 ] !== value ) throw new Error ( 'Unclosed section "' + openSection [ 1 ] + '" at ' + start ) ; } else if ( type === 'name' || type === '{' || type === '&' ) { nonSpace = true ; } else if ( type === '=' ) { compileTags ( value ) ; } } openSection = sections . pop ( ) ; if ( openSection ) throw new Error ( 'Unclosed section "' + openSection [ 1 ] + '" at ' + scanner . pos ) ; return nestTokens ( squashTokens ( tokens ) ) ; }
Breaks up the given `template` string into a tree of tokens.
Breaks up the given ` template ` string into a tree of tokens .
// Stack to hold section tokens // Buffer to hold the tokens // Indices of whitespace tokens on the current line // Is there a {{tag}} on the current line? // Is there a non-space char on the current line? // Strips all whitespace tokens array for the current line // if there was a {{#tag}} on it and otherwise only space. // Match any text between tags. // Check for whitespace on the current line. // Match the opening tag. // Get the tag type. // Get the tag value. // Match the closing tag. // Check section nesting. // Set the tags for the next time around. // Make sure there are no open sections when we're done.