[extensions-web] js: Update mustache
- From: Jasper St. Pierre <jstpierre src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [extensions-web] js: Update mustache
- Date: Wed, 26 Sep 2012 03:53:38 +0000 (UTC)
commit bae571f632b6de5832e4eec2bba5796c4a94ba76
Author: Jasper St. Pierre <jstpierre mecheye net>
Date: Tue Sep 25 18:02:04 2012 -0300
js: Update mustache
sweettooth/static/js/mustache.js | 914 ++++++++++++++++++++-----------------
sweettooth/static/js/templates.js | 17 +-
2 files changed, 501 insertions(+), 430 deletions(-)
---
diff --git a/sweettooth/static/js/mustache.js b/sweettooth/static/js/mustache.js
index 9c61671..7165a8a 100644
--- a/sweettooth/static/js/mustache.js
+++ b/sweettooth/static/js/mustache.js
@@ -2,541 +2,621 @@
* mustache.js - Logic-less {{mustache}} templates with JavaScript
* http://github.com/janl/mustache.js
*/
-var Mustache = (typeof module !== "undefined" && module.exports) || {};
-(function (exports) {
-
- exports.name = "mustache.js";
- exports.version = "0.5.0-dev";
- exports.tags = ["{{", "}}"];
- exports.parse = parse;
- exports.compile = compile;
- exports.render = render;
- exports.clearCache = clearCache;
-
- // This is here for backwards compatibility with 0.4.x.
- exports.to_html = function (template, view, partials, send) {
- var result = render(template, view, partials);
-
- if (typeof send === "function") {
- send(result);
- } else {
- return result;
- }
- };
+/*global define: false*/
- var _toString = Object.prototype.toString;
- var _isArray = Array.isArray;
- var _forEach = Array.prototype.forEach;
- var _trim = String.prototype.trim;
+var Mustache;
- var isArray;
- if (_isArray) {
- isArray = _isArray;
+(function (exports) {
+ if (typeof module !== "undefined" && module.exports) {
+ module.exports = exports; // CommonJS
+ } else if (typeof define === "function") {
+ define(exports); // AMD
} else {
- isArray = function (obj) {
- return _toString.call(obj) === "[object Array]";
- };
+ Mustache = exports; // <script>
}
+}((function () {
- var forEach;
- if (_forEach) {
- forEach = function (obj, callback, scope) {
- return _forEach.call(obj, callback, scope);
- };
- } else {
- forEach = function (obj, callback, scope) {
- for (var i = 0, len = obj.length; i < len; ++i) {
- callback.call(scope, obj[i], i, obj);
- }
- };
- }
+ var exports = {};
- var spaceRe = /^\s*$/;
+ exports.name = "mustache.js";
+ exports.version = "0.7.0";
+ exports.tags = ["{{", "}}"];
- function isWhitespace(string) {
- return spaceRe.test(string);
+ exports.Scanner = Scanner;
+ exports.Context = Context;
+ exports.Writer = Writer;
+
+ var whiteRe = /\s*/;
+ var spaceRe = /\s+/;
+ var nonSpaceRe = /\S/;
+ var eqRe = /\s*=/;
+ var curlyRe = /\s*\}/;
+ var tagRe = /#|\^|\/|>|\{|&|=|!/;
+
+ // Workaround for https://issues.apache.org/jira/browse/COUCHDB-577
+ // See https://github.com/janl/mustache.js/issues/189
+ function testRe(re, string) {
+ return RegExp.prototype.test.call(re, string);
}
- var trim;
- if (_trim) {
- trim = function (string) {
- return string == null ? "" : _trim.call(string);
- };
- } else {
- var trimLeft, trimRight;
+ function isWhitespace(string) {
+ return !testRe(nonSpaceRe, string);
+ }
- if (isWhitespace("\xA0")) {
- trimLeft = /^\s+/;
- trimRight = /\s+$/;
- } else {
- // IE doesn't match non-breaking spaces with \s, thanks jQuery.
- trimLeft = /^[\s\xA0]+/;
- trimRight = /[\s\xA0]+$/;
- }
+ var isArray = Array.isArray || function (obj) {
+ return Object.prototype.toString.call(obj) === "[object Array]";
+ };
- trim = function (string) {
- return string == null ? "" :
- String(string).replace(trimLeft, "").replace(trimRight, "");
- };
+ function escapeRe(string) {
+ return string.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
}
- var escapeMap = {
+ var entityMap = {
"&": "&",
"<": "<",
">": ">",
'"': '"',
- "'": '''
+ "'": ''',
+ "/": '/'
};
- function escapeHTML(string) {
- return String(string).replace(/&(?!\w+;)|[<>"']/g, function (s) {
- return escapeMap[s] || s;
+ function escapeHtml(string) {
+ return String(string).replace(/[&<>"'\/]/g, function (s) {
+ return entityMap[s];
});
}
+ // Export the escaping function so that the user may override it.
+ // See https://github.com/janl/mustache.js/issues/244
+ exports.escape = escapeHtml;
+
+ function Scanner(string) {
+ this.string = string;
+ this.tail = string;
+ this.pos = 0;
+ }
+
/**
- * Adds the `template`, `line`, and `file` properties to the given error
- * object and alters the message to provide more useful debugging information.
+ * Returns `true` if the tail is empty (end of string).
*/
- function debug(e, template, line, file) {
- file = file || "<template>";
-
- var lines = template.split("\n"),
- start = Math.max(line - 3, 0),
- end = Math.min(lines.length, line + 3),
- context = lines.slice(start, end);
-
- var c;
- for (var i = 0, len = context.length; i < len; ++i) {
- c = i + start + 1;
- context[i] = (c === line ? " >> " : " ") + context[i];
- }
+ Scanner.prototype.eos = function () {
+ return this.tail === "";
+ };
- e.template = template;
- e.line = line;
- e.file = file;
- e.message = [file + ":" + line, context.join("\n"), "", e.message].join("\n");
+ /**
+ * Tries to match the given regular expression at the current position.
+ * Returns the matched text if it can match, the empty string otherwise.
+ */
+ Scanner.prototype.scan = function (re) {
+ var match = this.tail.match(re);
- return e;
- }
+ if (match && match.index === 0) {
+ this.tail = this.tail.substring(match[0].length);
+ this.pos += match[0].length;
+ return match[0];
+ }
+
+ return "";
+ };
/**
- * Looks up the value of the given `name` in the given context `stack`.
+ * Skips all text until the given regular expression can be matched. Returns
+ * the skipped string, which is the entire tail if no match can be made.
*/
- function lookup(name, stack, defaultValue) {
- if (name === ".") {
- return stack[stack.length - 1];
+ Scanner.prototype.scanUntil = function (re) {
+ var match, pos = this.tail.search(re);
+
+ switch (pos) {
+ case -1:
+ match = this.tail;
+ this.pos += this.tail.length;
+ this.tail = "";
+ break;
+ case 0:
+ match = "";
+ break;
+ default:
+ match = this.tail.substring(0, pos);
+ this.tail = this.tail.substring(pos);
+ this.pos += pos;
}
- var names = name.split(".");
- var lastIndex = names.length - 1;
- var target = names[lastIndex];
+ return match;
+ };
- var value, context, i = stack.length, j, localStack;
- while (i) {
- localStack = stack.slice(0);
- context = stack[--i];
+ function Context(view, parent) {
+ this.view = view;
+ this.parent = parent;
+ this.clearCache();
+ }
- j = 0;
- while (j < lastIndex) {
- context = context[names[j++]];
+ Context.make = function (view) {
+ return (view instanceof Context) ? view : new Context(view);
+ };
- if (context == null) {
- break;
- }
+ Context.prototype.clearCache = function () {
+ this._cache = {};
+ };
- localStack.push(context);
- }
+ Context.prototype.push = function (view) {
+ return new Context(view, this);
+ };
- if (context && typeof context === "object" && target in context) {
- value = context[target];
- break;
+ Context.prototype.lookup = function (name) {
+ var value = this._cache[name];
+
+ if (!value) {
+ if (name === ".") {
+ value = this.view;
+ } else {
+ var context = this;
+
+ while (context) {
+ if (name.indexOf(".") > 0) {
+ var names = name.split("."), i = 0;
+
+ value = context.view;
+
+ while (value && i < names.length) {
+ value = value[names[i++]];
+ }
+ } else {
+ value = context.view[name];
+ }
+
+ if (value != null) {
+ break;
+ }
+
+ context = context.parent;
+ }
}
- }
- // If the value is a function, call it in the current context.
- if (typeof value === "function") {
- value = value.call(localStack[localStack.length - 1]);
+ this._cache[name] = value;
}
- if (value == null) {
- return defaultValue;
+ if (typeof value === "function") {
+ value = value.call(this.view);
}
return value;
+ };
+
+ function Writer() {
+ this.clearCache();
}
- function renderSection(name, stack, callback, inverted) {
- var buffer = "";
- var value = lookup(name, stack);
+ Writer.prototype.clearCache = function () {
+ this._cache = {};
+ this._partialCache = {};
+ };
- if (inverted) {
- // From the spec: inverted sections may render text once based on the
- // inverse value of the key. That is, they will be rendered if the key
- // doesn't exist, is false, or is an empty list.
- if (value == null || value === false || (isArray(value) && value.length === 0)) {
- buffer += callback();
- }
- } else if (isArray(value)) {
- forEach(value, function (value) {
- stack.push(value);
- buffer += callback();
- stack.pop();
- });
- } else if (typeof value === "object") {
- stack.push(value);
- buffer += callback();
- stack.pop();
- } else if (typeof value === "function") {
- var scope = stack[stack.length - 1];
- var scopedRender = function (template) {
- return render(template, scope);
- };
- buffer += value.call(scope, callback(), scopedRender) || "";
- } else if (value) {
- buffer += callback();
- }
+ Writer.prototype.compile = function (template, tags) {
+ var fn = this._cache[template];
- return buffer;
- }
+ if (!fn) {
+ var tokens = exports.parse(template, tags);
+ fn = this._cache[template] = this.compileTokens(tokens, template);
+ }
- /**
- * Parses the given `template` and returns the source of a function that,
- * with the proper arguments, will render the template. Recognized options
- * include the following:
- *
- * - file The name of the file the template comes from (displayed in
- * error messages)
- * - tags An array of open and close tags the `template` uses. Defaults
- * to the value of Mustache.tags
- * - debug Set `true` to log the body of the generated function to the
- * console
- * - space Set `true` to preserve whitespace from lines that otherwise
- * contain only a {{tag}}. Defaults to `false`
- */
- function parse(template, options) {
- options = options || {};
-
- var tags = options.tags || exports.tags,
- openTag = tags[0],
- closeTag = tags[tags.length - 1];
-
- var code = [
- 'var buffer = "";', // output buffer
- "\nvar line = 1;", // keep track of source line number
- "\ntry {",
- '\nbuffer += "'
- ];
+ return fn;
+ };
- var spaces = [], // indices of whitespace in code 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?
+ Writer.prototype.compilePartial = function (name, template, tags) {
+ var fn = this.compile(template, tags);
+ this._partialCache[name] = fn;
+ return fn;
+ };
- // Strips all space characters from the code array for the current line
- // if there was a {{tag}} on it and otherwise only spaces.
- var stripSpace = function () {
- if (hasTag && !nonSpace && !options.space) {
- while (spaces.length) {
- code.splice(spaces.pop(), 1);
+ Writer.prototype.compileTokens = function (tokens, template) {
+ var fn = compileTokens(tokens);
+ var self = this;
+
+ return function (view, partials) {
+ if (partials) {
+ if (typeof partials === "function") {
+ self._loadPartial = partials;
+ } else {
+ for (var name in partials) {
+ self.compilePartial(name, partials[name]);
+ }
}
- } else {
- spaces = [];
}
- hasTag = false;
- nonSpace = false;
+ return fn(self, Context.make(view), template);
};
+ };
- var sectionStack = [], updateLine, nextOpenTag, nextCloseTag;
+ Writer.prototype.render = function (template, view, partials) {
+ return this.compile(template)(view, partials);
+ };
- var setTags = function (source) {
- tags = trim(source).split(/\s+/);
- nextOpenTag = tags[0];
- nextCloseTag = tags[tags.length - 1];
- };
+ Writer.prototype._section = function (name, context, text, callback) {
+ var value = context.lookup(name);
- var includePartial = function (source) {
- code.push(
- '";',
- updateLine,
- '\nvar partial = partials["' + trim(source) + '"];',
- '\nif (partial) {',
- '\n buffer += render(partial,stack[stack.length - 1],partials);',
- '\n}',
- '\nbuffer += "'
- );
- };
+ switch (typeof value) {
+ case "object":
+ if (isArray(value)) {
+ var buffer = "";
- var openSection = function (source, inverted) {
- var name = trim(source);
+ for (var i = 0, len = value.length; i < len; ++i) {
+ buffer += callback(this, context.push(value[i]));
+ }
- if (name === "") {
- throw debug(new Error("Section name may not be empty"), template, line, options.file);
+ return buffer;
}
- sectionStack.push({name: name, inverted: inverted});
-
- code.push(
- '";',
- updateLine,
- '\nvar name = "' + name + '";',
- '\nvar callback = (function () {',
- '\n return function () {',
- '\n var buffer = "";',
- '\nbuffer += "'
- );
- };
+ return value ? callback(this, context.push(value)) : "";
+ case "function":
+ var self = this;
+ var scopedRender = function (template) {
+ return self.render(template, context);
+ };
- var openInvertedSection = function (source) {
- openSection(source, true);
- };
+ return value.call(context.view, text, scopedRender) || "";
+ default:
+ if (value) {
+ return callback(this, context);
+ }
+ }
- var closeSection = function (source) {
- var name = trim(source);
- var openName = sectionStack.length != 0 && sectionStack[sectionStack.length - 1].name;
+ return "";
+ };
- if (!openName || name != openName) {
- throw debug(new Error('Section named "' + name + '" was never opened'), template, line, options.file);
- }
+ Writer.prototype._inverted = function (name, context, callback) {
+ var value = context.lookup(name);
- var section = sectionStack.pop();
+ // Use JavaScript's definition of falsy. Include empty arrays.
+ // See https://github.com/janl/mustache.js/issues/186
+ if (!value || (isArray(value) && value.length === 0)) {
+ return callback(this, context);
+ }
- code.push(
- '";',
- '\n return buffer;',
- '\n };',
- '\n})();'
- );
+ return "";
+ };
- if (section.inverted) {
- code.push("\nbuffer += renderSection(name,stack,callback,true);");
- } else {
- code.push("\nbuffer += renderSection(name,stack,callback);");
+ Writer.prototype._partial = function (name, context) {
+ if (!(name in this._partialCache) && this._loadPartial) {
+ this.compilePartial(name, this._loadPartial(name));
+ }
+
+ var fn = this._partialCache[name];
+
+ return fn ? fn(context) : "";
+ };
+
+ Writer.prototype._name = function (name, context) {
+ var value = context.lookup(name);
+
+ if (typeof value === "function") {
+ value = value.call(context.view);
+ }
+
+ return (value == null) ? "" : String(value);
+ };
+
+ Writer.prototype._escaped = function (name, context) {
+ return exports.escape(this._name(name, context));
+ };
+
+ /**
+ * Calculates the bounds of the section represented by the given `token` in
+ * the original template by drilling down into nested sections to find the
+ * last token that is part of that section. Returns an array of [start, end].
+ */
+ function sectionBounds(token) {
+ var start = token[3];
+ var end = start;
+
+ var tokens;
+ while ((tokens = token[4]) && tokens.length) {
+ token = tokens[tokens.length - 1];
+ end = token[3];
+ }
+
+ return [start, end];
+ }
+
+ /**
+ * Low-level function that compiles the given `tokens` into a function
+ * that accepts three arguments: a Writer, a Context, and the template.
+ */
+ function compileTokens(tokens) {
+ var subRenders = {};
+
+ function subRender(i, tokens, template) {
+ if (!subRenders[i]) {
+ var fn = compileTokens(tokens);
+ subRenders[i] = function (writer, context) {
+ return fn(writer, context, template);
+ };
}
- code.push('\nbuffer += "');
- };
+ return subRenders[i];
+ }
- var sendPlain = function (source) {
- code.push(
- '";',
- updateLine,
- '\nbuffer += lookup("' + trim(source) + '",stack,"");',
- '\nbuffer += "'
- );
- };
+ return function (writer, context, template) {
+ var buffer = "";
+ var token, sectionText;
- var sendEscaped = function (source) {
- code.push(
- '";',
- updateLine,
- '\nbuffer += escapeHTML(lookup("' + trim(source) + '",stack,""));',
- '\nbuffer += "'
- );
- };
+ for (var i = 0, len = tokens.length; i < len; ++i) {
+ token = tokens[i];
- var line = 1, c, callback;
- for (var i = 0, len = template.length; i < len; ++i) {
- if (template.slice(i, i + openTag.length) === openTag) {
- i += openTag.length;
- c = template.substr(i, 1);
- updateLine = '\nline = ' + line + ';';
- nextOpenTag = openTag;
- nextCloseTag = closeTag;
- hasTag = true;
-
- switch (c) {
- case "!": // comment
- i++;
- callback = null;
+ switch (token[0]) {
+ case "#":
+ sectionText = template.slice.apply(template, sectionBounds(token));
+ buffer += writer._section(token[1], context, sectionText, subRender(i, token[4], template));
break;
- case "=": // change open/close tags, e.g. {{=<% %>=}}
- i++;
- closeTag = "=" + closeTag;
- callback = setTags;
+ case "^":
+ buffer += writer._inverted(token[1], context, subRender(i, token[4], template));
break;
- case ">": // include partial
- i++;
- callback = includePartial;
+ case ">":
+ buffer += writer._partial(token[1], context);
break;
- case "#": // start section
- i++;
- callback = openSection;
+ case "&":
+ buffer += writer._name(token[1], context);
break;
- case "^": // start inverted section
- i++;
- callback = openInvertedSection;
+ case "name":
+ buffer += writer._escaped(token[1], context);
break;
- case "/": // end section
- i++;
- callback = closeSection;
+ case "text":
+ buffer += token[1];
break;
- case "{": // plain variable
- closeTag = "}" + closeTag;
- // fall through
- case "&": // plain variable
- i++;
- nonSpace = true;
- callback = sendPlain;
- break;
- default: // escaped variable
- nonSpace = true;
- callback = sendEscaped;
}
+ }
- var end = template.indexOf(closeTag, i);
+ return buffer;
+ };
+ }
- if (end === -1) {
- throw debug(new Error('Tag "' + openTag + '" was not closed properly'), template, line, options.file);
+ /**
+ * Forms the given array of `tokens` into a nested tree structure where
+ * tokens that represent a section have a fifth item: an array that contains
+ * all tokens in that section.
+ */
+ function nestTokens(tokens) {
+ var tree = [];
+ var collector = tree;
+ var sections = [];
+ var token, section;
+
+ for (var i = 0; i < tokens.length; ++i) {
+ token = tokens[i];
+
+ switch (token[0]) {
+ case "#":
+ case "^":
+ token[4] = [];
+ sections.push(token);
+ collector.push(token);
+ collector = token[4];
+ break;
+ case "/":
+ if (sections.length === 0) {
+ throw new Error("Unopened section: " + token[1]);
}
- var source = template.substring(i, end);
+ section = sections.pop();
- if (callback) {
- callback(source);
+ if (section[1] !== token[1]) {
+ throw new Error("Unclosed section: " + section[1]);
}
- // Maintain line count for \n in source.
- var n = 0;
- while (~(n = source.indexOf("\n", n))) {
- line++;
- n++;
+ if (sections.length > 0) {
+ collector = sections[sections.length - 1][4];
+ } else {
+ collector = tree;
}
+ break;
+ default:
+ collector.push(token);
+ }
+ }
- i = end + closeTag.length - 1;
- openTag = nextOpenTag;
- closeTag = nextCloseTag;
+ // Make sure there were no open sections when we're done.
+ section = sections.pop();
+
+ if (section) {
+ throw new Error("Unclosed section: " + section[1]);
+ }
+
+ return tree;
+ }
+
+ /**
+ * Combines the values of consecutive text tokens in the given `tokens` array
+ * to a single token.
+ */
+ function squashTokens(tokens) {
+ var token, lastToken;
+
+ for (var i = 0; i < tokens.length; ++i) {
+ token = tokens[i];
+
+ if (lastToken && lastToken[0] === "text" && token[0] === "text") {
+ lastToken[1] += token[1];
+ lastToken[3] = token[3];
+ tokens.splice(i--, 1); // Remove this token from the array.
} else {
- c = template.substr(i, 1);
+ lastToken = token;
+ }
+ }
+ }
- switch (c) {
- case '"':
- case "\\":
- nonSpace = true;
- code.push("\\" + c);
- break;
- case "\r":
- // Ignore carriage returns.
- break;
- case "\n":
- spaces.push(code.length);
- code.push("\\n");
- stripSpace(); // Check for whitespace on the current line.
- line++;
- break;
- default:
- if (isWhitespace(c)) {
- spaces.push(code.length);
+ function escapeTags(tags) {
+ if (tags.length !== 2) {
+ throw new Error("Invalid tags: " + tags.join(" "));
+ }
+
+ return [
+ new RegExp(escapeRe(tags[0]) + "\\s*"),
+ new RegExp("\\s*" + escapeRe(tags[1]))
+ ];
+ }
+
+ /**
+ * Breaks up the given `template` string into a tree of token objects. If
+ * `tags` 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).
+ */
+ exports.parse = function (template, tags) {
+ tags = tags || exports.tags;
+
+ var tagRes = escapeTags(tags);
+ var scanner = new Scanner(template);
+
+ var 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?
+
+ // 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) {
+ tokens.splice(spaces.pop(), 1);
+ }
+ } else {
+ spaces = [];
+ }
+
+ hasTag = false;
+ nonSpace = false;
+ }
+
+ var start, type, value, chr;
+
+ while (!scanner.eos()) {
+ start = scanner.pos;
+ value = scanner.scanUntil(tagRes[0]);
+
+ if (value) {
+ for (var i = 0, len = value.length; i < len; ++i) {
+ chr = value.charAt(i);
+
+ if (isWhitespace(chr)) {
+ spaces.push(tokens.length);
} else {
nonSpace = true;
}
- code.push(c);
+ tokens.push(["text", chr, start, start + 1]);
+ start += 1;
+
+ if (chr === "\n") {
+ stripSpace(); // Check for whitespace on the current line.
+ }
}
}
- }
- if (sectionStack.length != 0) {
- throw debug(new Error('Section "' + sectionStack[sectionStack.length - 1].name + '" was not closed properly'), template, line, options.file);
- }
+ start = scanner.pos;
- // Clean up any whitespace from a closing {{tag}} that was at the end
- // of the template without a trailing \n.
- stripSpace();
+ // Match the opening tag.
+ if (!scanner.scan(tagRes[0])) {
+ break;
+ }
- code.push(
- '";',
- "\nreturn buffer;",
- "\n} catch (e) { throw {error: e, line: line}; }"
- );
+ hasTag = true;
+ type = scanner.scan(tagRe) || "name";
+
+ // Skip any whitespace between tag and value.
+ scanner.scan(whiteRe);
+
+ // Extract the tag value.
+ if (type === "=") {
+ value = scanner.scanUntil(eqRe);
+ scanner.scan(eqRe);
+ scanner.scanUntil(tagRes[1]);
+ } else if (type === "{") {
+ var closeRe = new RegExp("\\s*" + escapeRe("}" + tags[1]));
+ value = scanner.scanUntil(closeRe);
+ scanner.scan(curlyRe);
+ scanner.scanUntil(tagRes[1]);
+ type = "&";
+ } else {
+ value = scanner.scanUntil(tagRes[1]);
+ }
+
+ // Match the closing tag.
+ if (!scanner.scan(tagRes[1])) {
+ throw new Error("Unclosed tag at " + scanner.pos);
+ }
- // Ignore `buffer += "";` statements.
- var body = code.join("").replace(/buffer \+= "";\n/g, "");
+ tokens.push([type, value, start, scanner.pos]);
- if (options.debug) {
- if (typeof console != "undefined" && console.log) {
- console.log(body);
- } else if (typeof print === "function") {
- print(body);
+ if (type === "name" || type === "{" || type === "&") {
+ nonSpace = true;
+ }
+
+ // Set the tags for the next time around.
+ if (type === "=") {
+ tags = value.split(spaceRe);
+ tagRes = escapeTags(tags);
}
}
- return body;
- }
+ squashTokens(tokens);
+
+ return nestTokens(tokens);
+ };
+
+ // The high-level clearCache, compile, compilePartial, and render functions
+ // use this default writer.
+ var _writer = new Writer();
/**
- * Used by `compile` to generate a reusable function for the given `template`.
+ * Clears all cached templates and partials in the default writer.
*/
- function _compile(template, options) {
- var args = "view,partials,stack,lookup,escapeHTML,renderSection,render";
- var body = parse(template, options);
- var fn = new Function(args, body);
-
- // This anonymous function wraps the generated function so we can do
- // argument coercion, setup some variables, and handle any errors
- // encountered while executing it.
- var template = function (view, partials) {
- partials = partials || {};
-
- var stack = [view]; // context stack
-
- try {
- return fn(view, partials, stack, lookup, escapeHTML, renderSection, render);
- } catch (e) {
- throw debug(e.error, template, e.line, options.file);
- }
- };
- template.compiled = true;
- return template;
- }
+ exports.clearCache = function () {
+ return _writer.clearCache();
+ };
- // Cache of pre-compiled templates.
- var _cache = {};
+ /**
+ * Compiles the given `template` to a reusable function using the default
+ * writer.
+ */
+ exports.compile = function (template, tags) {
+ return _writer.compile(template, tags);
+ };
/**
- * Clear the cache of compiled templates.
+ * Compiles the partial with the given `name` and `template` to a reusable
+ * function using the default writer.
*/
- function clearCache() {
- _cache = {};
- }
+ exports.compilePartial = function (name, template, tags) {
+ return _writer.compilePartial(name, template, tags);
+ };
/**
- * Compiles the given `template` into a reusable function using the given
- * `options`. In addition to the options accepted by Mustache.parse,
- * recognized options include the following:
- *
- * - cache Set `false` to bypass any pre-compiled version of the given
- * template. Otherwise, a given `template` string will be cached
- * the first time it is parsed
+ * Compiles the given array of tokens (the output of a parse) to a reusable
+ * function using the default writer.
*/
- function compile(template, options) {
- options = options || {};
+ exports.compileTokens = function (tokens, template) {
+ return _writer.compileTokens(tokens, template);
+ };
- if (typeof template === "function" && template.compiled) {
- return template;
- }
+ /**
+ * Renders the `template` with the given `view` and `partials` using the
+ * default writer.
+ */
+ exports.render = function (template, view, partials) {
+ return _writer.render(template, view, partials);
+ };
- // Use a pre-compiled version from the cache if we have one.
- if (options.cache !== false) {
- if (!_cache[template]) {
- _cache[template] = _compile(template, options);
- }
+ // This is here for backwards compatibility with 0.4.x.
+ exports.to_html = function (template, view, partials, send) {
+ var result = exports.render(template, view, partials);
- return _cache[template];
+ if (typeof send === "function") {
+ send(result);
+ } else {
+ return result;
}
+ };
- return _compile(template, options);
- }
-
- /**
- * High-level function that renders the given `template` using the given
- * `view` and `partials`. If you need to use any of the template options (see
- * `compile` above), you must compile in a separate step, and then call that
- * compiled function.
- */
- function render(template, view, partials) {
- return compile(template)(view, partials);
- }
+ return exports;
-})(Mustache);
+}())));
diff --git a/sweettooth/static/js/templates.js b/sweettooth/static/js/templates.js
index 3a24daf..57c68b3 100644
--- a/sweettooth/static/js/templates.js
+++ b/sweettooth/static/js/templates.js
@@ -1,22 +1,11 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
-define(['templates/templatedata', 'mustache'], function(templatedata) {
+define(['templates/templatedata', 'mustache'], function(templatedata, mustache) {
"use strict";
var exports = {};
- var partials = exports._P = {};
exports._T = templatedata;
- function compile(template) {
- // We have our own template caching, don't use Mustache's.
- var compiled = Mustache.compile(template, { cache: false });
- var wrapper = function(view) {
- return compiled(view, partials);
- };
- wrapper.compiled = true;
- return wrapper;
- }
-
function _compileTemplateData(data, out, prefix) {
for (var propname in data) {
var v = data[propname], pkey;
@@ -26,9 +15,11 @@ define(['templates/templatedata', 'mustache'], function(templatedata) {
pkey = propname;
if (typeof(v) === typeof({})) {
+ // Subdirectory. Recurse.
out[propname] = _compileTemplateData(v, {}, pkey);
} else {
- out[propname] = partials[pkey] = compile(v);
+ // Template. Mustache will cache all partials for us.
+ out[propname] = mustache.compilePartial(pkey, v);
}
}
return out;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]