| @@ -92,6 +92,8 @@ | |||||
| var equalsRe = /\s*=/; | var equalsRe = /\s*=/; | ||||
| var curlyRe = /\s*\}/; | var curlyRe = /\s*\}/; | ||||
| var tagRe = /#|\^|\/|>|\{|&|=|!/; | var tagRe = /#|\^|\/|>|\{|&|=|!/; | ||||
| var openingTagRe, closingTagRe, closingCurlyRe; | |||||
| var sections, tokens, spaces, hasTag, noneSpace; | |||||
| /** | /** | ||||
| * Breaks up the given `template` string into a tree of tokens. If the `tags` | * Breaks up the given `template` string into a tree of tokens. If the `tags` | ||||
| @@ -119,38 +121,11 @@ | |||||
| if (!template) | if (!template) | ||||
| return []; | return []; | ||||
| var sections = []; // Stack to hold section tokens | |||||
| var tokens = []; // Buffer to hold the tokens | |||||
| var spaces = []; // Indices of whitespace tokens on the current line | |||||
| var hasTag = false; // Is there a {{tag}} on the current line? | |||||
| var nonSpace = false; // 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. | |||||
| 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])); | |||||
| } | |||||
| sections = []; // Stack to hold section tokens | |||||
| tokens = []; // Buffer to hold the tokens | |||||
| spaces = []; // Indices of whitespace tokens on the current line | |||||
| hasTag = false; // Is there a {{tag}} on the current line? | |||||
| nonSpace = false; // Is there a non-space char on the current line? | |||||
| compileTags(tags || mustache.tags); | compileTags(tags || mustache.tags); | ||||
| @@ -164,22 +139,8 @@ | |||||
| value = scanner.scanUntil(openingTagRe); | value = scanner.scanUntil(openingTagRe); | ||||
| if (value) { | 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; | |||||
| // Check for whitespace on the current line. | |||||
| if (chr === '\n') | |||||
| stripSpace(); | |||||
| } | |||||
| parseTemplateBetweenTags(value, start); | |||||
| start = start + value.length; | |||||
| } | } | ||||
| // Match the opening tag. | // Match the opening tag. | ||||
| @@ -193,18 +154,8 @@ | |||||
| scanner.scan(whiteRe); | scanner.scan(whiteRe); | ||||
| // Get the tag value. | // Get the tag value. | ||||
| 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); | |||||
| } | |||||
| value = getTagValue(scanner, type); | |||||
| type = type == '{' ? '&' : type; | |||||
| // Match the closing tag. | // Match the closing tag. | ||||
| if (!scanner.scan(closingTagRe)) | if (!scanner.scan(closingTagRe)) | ||||
| @@ -241,6 +192,75 @@ | |||||
| return nestTokens(squashTokens(tokens)); | return nestTokens(squashTokens(tokens)); | ||||
| } | } | ||||
| /** | |||||
| * Parses text between tags | |||||
| */ | |||||
| function parseTemplateBetweenTags (template, start) { | |||||
| var chr; | |||||
| for (var i = 0, valueLength = template.length; i < valueLength; ++i) { | |||||
| chr = template.charAt(i); | |||||
| if (isWhitespace(chr)) { | |||||
| spaces.push(tokens.length); | |||||
| } else { | |||||
| nonSpace = true; | |||||
| } | |||||
| tokens.push([ 'text', chr, start, start + 1 ]); | |||||
| start += 1; | |||||
| // Check for whitespace on the current line. | |||||
| if (chr === '\n') { | |||||
| // Strips all whitespace tokens array for the current line | |||||
| // if there was a {{#tag}} on it and otherwise only space. | |||||
| if (hasTag && !nonSpace) { | |||||
| while (spaces.length) | |||||
| delete tokens[spaces.pop()]; | |||||
| } else { | |||||
| spaces = []; | |||||
| } | |||||
| hasTag = false; | |||||
| nonSpace = false; | |||||
| } | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Gets value of the tag give it's type | |||||
| */ | |||||
| function getTagValue (scanner, type) { | |||||
| var value = null; | |||||
| 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); | |||||
| } else { | |||||
| value = scanner.scanUntil(closingTagRe); | |||||
| } | |||||
| return value; | |||||
| } | |||||
| /** | |||||
| * Extracts regular expressions for opening tag, closing tag | |||||
| * and closing curly tag from tags | |||||
| */ | |||||
| 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])); | |||||
| } | |||||
| /** | /** | ||||
| * Combines the values of consecutive text tokens in the given `tokens` array | * Combines the values of consecutive text tokens in the given `tokens` array | ||||
| * to a single token. | * to a single token. | ||||