| @@ -0,0 +1,536 @@ | |||
| /*! | |||
| * 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.14"; | |||
| 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; | |||
| } | |||
| }; | |||
| var _toString = Object.prototype.toString; | |||
| var _isArray = Array.isArray; | |||
| var _forEach = Array.prototype.forEach; | |||
| var _trim = String.prototype.trim; | |||
| var isArray; | |||
| if (_isArray) { | |||
| isArray = _isArray; | |||
| } else { | |||
| isArray = function (obj) { | |||
| return _toString.call(obj) === "[object Array]"; | |||
| }; | |||
| } | |||
| 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 spaceRe = /^\s*$/; | |||
| function isWhitespace(string) { | |||
| return spaceRe.test(string); | |||
| } | |||
| var trim; | |||
| if (_trim) { | |||
| trim = function (string) { | |||
| return string == null ? "" : _trim.call(string); | |||
| }; | |||
| } else { | |||
| var trimLeft, trimRight; | |||
| 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]+$/; | |||
| } | |||
| trim = function (string) { | |||
| return string == null ? "" : | |||
| String(string).replace(trimLeft, "").replace(trimRight, ""); | |||
| }; | |||
| } | |||
| var escapeMap = { | |||
| "&": "&", | |||
| "<": "<", | |||
| ">": ">", | |||
| '"': '"', | |||
| "'": ''' | |||
| }; | |||
| function escapeHTML(string) { | |||
| return String(string).replace(/&(?!\w+;)|[<>"']/g, function (s) { | |||
| return escapeMap[s] || s; | |||
| }); | |||
| } | |||
| /** | |||
| * Adds the `template`, `line`, and `file` properties to the given error | |||
| * object and alters the message to provide more useful debugging information. | |||
| */ | |||
| 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]; | |||
| } | |||
| e.template = template; | |||
| e.line = line; | |||
| e.file = file; | |||
| e.message = [file + ":" + line, context.join("\n"), "", e.message].join("\n"); | |||
| return e; | |||
| } | |||
| /** | |||
| * Looks up the value of the given `name` in the given context `stack`. | |||
| */ | |||
| function lookup(name, stack, defaultValue) { | |||
| if (name === ".") { | |||
| return stack[stack.length - 1]; | |||
| } | |||
| var names = name.split("."); | |||
| var lastIndex = names.length - 1; | |||
| var target = names[lastIndex]; | |||
| var value, context, i = stack.length, j, localStack; | |||
| while (i) { | |||
| localStack = stack.slice(0); | |||
| context = stack[--i]; | |||
| j = 0; | |||
| while (j < lastIndex) { | |||
| context = context[names[j++]]; | |||
| if (context == null) { | |||
| break; | |||
| } | |||
| localStack.push(context); | |||
| } | |||
| if (context && typeof context === "object" && target in context) { | |||
| value = context[target]; | |||
| break; | |||
| } | |||
| } | |||
| // If the value is a function, call it in the current context. | |||
| if (typeof value === "function") { | |||
| value = value.call(localStack[localStack.length - 1]); | |||
| } | |||
| if (value == null) { | |||
| return defaultValue; | |||
| } | |||
| return value; | |||
| } | |||
| function renderSection(name, stack, callback, inverted) { | |||
| var buffer = ""; | |||
| var value = lookup(name, stack); | |||
| 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(); | |||
| } | |||
| return buffer; | |||
| } | |||
| /** | |||
| * 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 += "' | |||
| ]; | |||
| 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? | |||
| // 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); | |||
| } | |||
| } else { | |||
| spaces = []; | |||
| } | |||
| hasTag = false; | |||
| nonSpace = false; | |||
| }; | |||
| var sectionStack = [], updateLine, nextOpenTag, nextCloseTag; | |||
| var setTags = function (source) { | |||
| tags = trim(source).split(/\s+/); | |||
| nextOpenTag = tags[0]; | |||
| nextCloseTag = tags[tags.length - 1]; | |||
| }; | |||
| 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 += "' | |||
| ); | |||
| }; | |||
| var openSection = function (source, inverted) { | |||
| var name = trim(source); | |||
| if (name === "") { | |||
| throw debug(new Error("Section name may not be empty"), template, line, options.file); | |||
| } | |||
| sectionStack.push({name: name, inverted: inverted}); | |||
| code.push( | |||
| '";', | |||
| updateLine, | |||
| '\nvar name = "' + name + '";', | |||
| '\nvar callback = (function () {', | |||
| '\n return function () {', | |||
| '\n var buffer = "";', | |||
| '\nbuffer += "' | |||
| ); | |||
| }; | |||
| var openInvertedSection = function (source) { | |||
| openSection(source, true); | |||
| }; | |||
| var closeSection = function (source) { | |||
| var name = trim(source); | |||
| var openName = sectionStack.length != 0 && sectionStack[sectionStack.length - 1].name; | |||
| if (!openName || name != openName) { | |||
| throw debug(new Error('Section named "' + name + '" was never opened'), template, line, options.file); | |||
| } | |||
| var section = sectionStack.pop(); | |||
| code.push( | |||
| '";', | |||
| '\n return buffer;', | |||
| '\n };', | |||
| '\n})();' | |||
| ); | |||
| if (section.inverted) { | |||
| code.push("\nbuffer += renderSection(name,stack,callback,true);"); | |||
| } else { | |||
| code.push("\nbuffer += renderSection(name,stack,callback);"); | |||
| } | |||
| code.push('\nbuffer += "'); | |||
| }; | |||
| var sendPlain = function (source) { | |||
| code.push( | |||
| '";', | |||
| updateLine, | |||
| '\nbuffer += lookup("' + trim(source) + '",stack,"");', | |||
| '\nbuffer += "' | |||
| ); | |||
| }; | |||
| var sendEscaped = function (source) { | |||
| code.push( | |||
| '";', | |||
| updateLine, | |||
| '\nbuffer += escapeHTML(lookup("' + trim(source) + '",stack,""));', | |||
| '\nbuffer += "' | |||
| ); | |||
| }; | |||
| 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; | |||
| break; | |||
| case "=": // change open/close tags, e.g. {{=<% %>=}} | |||
| i++; | |||
| closeTag = "=" + closeTag; | |||
| callback = setTags; | |||
| break; | |||
| case ">": // include partial | |||
| i++; | |||
| callback = includePartial; | |||
| break; | |||
| case "#": // start section | |||
| i++; | |||
| callback = openSection; | |||
| break; | |||
| case "^": // start inverted section | |||
| i++; | |||
| callback = openInvertedSection; | |||
| break; | |||
| case "/": // end section | |||
| i++; | |||
| callback = closeSection; | |||
| 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); | |||
| if (end === -1) { | |||
| throw debug(new Error('Tag "' + openTag + '" was not closed properly'), template, line, options.file); | |||
| } | |||
| var source = template.substring(i, end); | |||
| if (callback) { | |||
| callback(source); | |||
| } | |||
| // Maintain line count for \n in source. | |||
| var n = 0; | |||
| while (~(n = source.indexOf("\n", n))) { | |||
| line++; | |||
| n++; | |||
| } | |||
| i = end + closeTag.length - 1; | |||
| openTag = nextOpenTag; | |||
| closeTag = nextCloseTag; | |||
| } else { | |||
| c = template.substr(i, 1); | |||
| 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); | |||
| } else { | |||
| nonSpace = true; | |||
| } | |||
| code.push(c); | |||
| } | |||
| } | |||
| } | |||
| if (sectionStack.length != 0) { | |||
| throw debug(new Error('Section "' + sectionStack[sectionStack.length - 1].name + '" was not closed properly'), template, line, options.file); | |||
| } | |||
| // Clean up any whitespace from a closing {{tag}} that was at the end | |||
| // of the template without a trailing \n. | |||
| stripSpace(); | |||
| code.push( | |||
| '";', | |||
| "\nreturn buffer;", | |||
| "\n} catch (e) { throw {error: e, line: line}; }" | |||
| ); | |||
| // Ignore `buffer += "";` statements. | |||
| var body = code.join("").replace(/buffer \+= "";\n/g, ""); | |||
| if (options.debug) { | |||
| if (typeof console != "undefined" && console.log) { | |||
| console.log(body); | |||
| } else if (typeof print === "function") { | |||
| print(body); | |||
| } | |||
| } | |||
| return body; | |||
| } | |||
| /** | |||
| * Used by `compile` to generate a reusable function for the given `template`. | |||
| */ | |||
| 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. | |||
| return 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); | |||
| } | |||
| }; | |||
| } | |||
| // Cache of pre-compiled templates. | |||
| var _cache = {}; | |||
| /** | |||
| * Clear the cache of compiled templates. | |||
| */ | |||
| function clearCache() { | |||
| _cache = {}; | |||
| } | |||
| /** | |||
| * 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 | |||
| */ | |||
| function compile(template, options) { | |||
| options = options || {}; | |||
| // Use a pre-compiled version from the cache if we have one. | |||
| if (options.cache !== false) { | |||
| if (!_cache[template]) { | |||
| _cache[template] = _compile(template, options); | |||
| } | |||
| return _cache[template]; | |||
| } | |||
| 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); | |||
| } | |||
| })(Mustache); | |||
| @@ -0,0 +1,8 @@ | |||
| { | |||
| "name": "mustache", | |||
| "version": "0.5.21", | |||
| "description": "Logic-less {{mustache}} templates with JavaScript", | |||
| "author": "mustache.js Authors <http://github.com/janl/mustache.js>", | |||
| "keywords": ["mustache", "template", "templates", "ejs"], | |||
| "main": "./mustache" | |||
| } | |||
| @@ -0,0 +1,549 @@ | |||
| /* | |||
| Shameless port of a shameless port | |||
| @defunkt => @janl => @aq => @voodootikigod | |||
| See http://github.com/defunkt/mustache for more info. | |||
| */ | |||
| dojo.provide("dojox.mustache._base"); | |||
| (function(){ | |||
| /*! | |||
| * 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.14"; | |||
| 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; | |||
| } | |||
| }; | |||
| var _toString = Object.prototype.toString; | |||
| var _isArray = Array.isArray; | |||
| var _forEach = Array.prototype.forEach; | |||
| var _trim = String.prototype.trim; | |||
| var isArray; | |||
| if (_isArray) { | |||
| isArray = _isArray; | |||
| } else { | |||
| isArray = function (obj) { | |||
| return _toString.call(obj) === "[object Array]"; | |||
| }; | |||
| } | |||
| 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 spaceRe = /^\s*$/; | |||
| function isWhitespace(string) { | |||
| return spaceRe.test(string); | |||
| } | |||
| var trim; | |||
| if (_trim) { | |||
| trim = function (string) { | |||
| return string == null ? "" : _trim.call(string); | |||
| }; | |||
| } else { | |||
| var trimLeft, trimRight; | |||
| 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]+$/; | |||
| } | |||
| trim = function (string) { | |||
| return string == null ? "" : | |||
| String(string).replace(trimLeft, "").replace(trimRight, ""); | |||
| }; | |||
| } | |||
| var escapeMap = { | |||
| "&": "&", | |||
| "<": "<", | |||
| ">": ">", | |||
| '"': '"', | |||
| "'": ''' | |||
| }; | |||
| function escapeHTML(string) { | |||
| return String(string).replace(/&(?!\w+;)|[<>"']/g, function (s) { | |||
| return escapeMap[s] || s; | |||
| }); | |||
| } | |||
| /** | |||
| * Adds the `template`, `line`, and `file` properties to the given error | |||
| * object and alters the message to provide more useful debugging information. | |||
| */ | |||
| 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]; | |||
| } | |||
| e.template = template; | |||
| e.line = line; | |||
| e.file = file; | |||
| e.message = [file + ":" + line, context.join("\n"), "", e.message].join("\n"); | |||
| return e; | |||
| } | |||
| /** | |||
| * Looks up the value of the given `name` in the given context `stack`. | |||
| */ | |||
| function lookup(name, stack, defaultValue) { | |||
| if (name === ".") { | |||
| return stack[stack.length - 1]; | |||
| } | |||
| var names = name.split("."); | |||
| var lastIndex = names.length - 1; | |||
| var target = names[lastIndex]; | |||
| var value, context, i = stack.length, j, localStack; | |||
| while (i) { | |||
| localStack = stack.slice(0); | |||
| context = stack[--i]; | |||
| j = 0; | |||
| while (j < lastIndex) { | |||
| context = context[names[j++]]; | |||
| if (context == null) { | |||
| break; | |||
| } | |||
| localStack.push(context); | |||
| } | |||
| if (context && typeof context === "object" && target in context) { | |||
| value = context[target]; | |||
| break; | |||
| } | |||
| } | |||
| // If the value is a function, call it in the current context. | |||
| if (typeof value === "function") { | |||
| value = value.call(localStack[localStack.length - 1]); | |||
| } | |||
| if (value == null) { | |||
| return defaultValue; | |||
| } | |||
| return value; | |||
| } | |||
| function renderSection(name, stack, callback, inverted) { | |||
| var buffer = ""; | |||
| var value = lookup(name, stack); | |||
| 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(); | |||
| } | |||
| return buffer; | |||
| } | |||
| /** | |||
| * 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 += "' | |||
| ]; | |||
| 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? | |||
| // 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); | |||
| } | |||
| } else { | |||
| spaces = []; | |||
| } | |||
| hasTag = false; | |||
| nonSpace = false; | |||
| }; | |||
| var sectionStack = [], updateLine, nextOpenTag, nextCloseTag; | |||
| var setTags = function (source) { | |||
| tags = trim(source).split(/\s+/); | |||
| nextOpenTag = tags[0]; | |||
| nextCloseTag = tags[tags.length - 1]; | |||
| }; | |||
| 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 += "' | |||
| ); | |||
| }; | |||
| var openSection = function (source, inverted) { | |||
| var name = trim(source); | |||
| if (name === "") { | |||
| throw debug(new Error("Section name may not be empty"), template, line, options.file); | |||
| } | |||
| sectionStack.push({name: name, inverted: inverted}); | |||
| code.push( | |||
| '";', | |||
| updateLine, | |||
| '\nvar name = "' + name + '";', | |||
| '\nvar callback = (function () {', | |||
| '\n return function () {', | |||
| '\n var buffer = "";', | |||
| '\nbuffer += "' | |||
| ); | |||
| }; | |||
| var openInvertedSection = function (source) { | |||
| openSection(source, true); | |||
| }; | |||
| var closeSection = function (source) { | |||
| var name = trim(source); | |||
| var openName = sectionStack.length != 0 && sectionStack[sectionStack.length - 1].name; | |||
| if (!openName || name != openName) { | |||
| throw debug(new Error('Section named "' + name + '" was never opened'), template, line, options.file); | |||
| } | |||
| var section = sectionStack.pop(); | |||
| code.push( | |||
| '";', | |||
| '\n return buffer;', | |||
| '\n };', | |||
| '\n})();' | |||
| ); | |||
| if (section.inverted) { | |||
| code.push("\nbuffer += renderSection(name,stack,callback,true);"); | |||
| } else { | |||
| code.push("\nbuffer += renderSection(name,stack,callback);"); | |||
| } | |||
| code.push('\nbuffer += "'); | |||
| }; | |||
| var sendPlain = function (source) { | |||
| code.push( | |||
| '";', | |||
| updateLine, | |||
| '\nbuffer += lookup("' + trim(source) + '",stack,"");', | |||
| '\nbuffer += "' | |||
| ); | |||
| }; | |||
| var sendEscaped = function (source) { | |||
| code.push( | |||
| '";', | |||
| updateLine, | |||
| '\nbuffer += escapeHTML(lookup("' + trim(source) + '",stack,""));', | |||
| '\nbuffer += "' | |||
| ); | |||
| }; | |||
| 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; | |||
| break; | |||
| case "=": // change open/close tags, e.g. {{=<% %>=}} | |||
| i++; | |||
| closeTag = "=" + closeTag; | |||
| callback = setTags; | |||
| break; | |||
| case ">": // include partial | |||
| i++; | |||
| callback = includePartial; | |||
| break; | |||
| case "#": // start section | |||
| i++; | |||
| callback = openSection; | |||
| break; | |||
| case "^": // start inverted section | |||
| i++; | |||
| callback = openInvertedSection; | |||
| break; | |||
| case "/": // end section | |||
| i++; | |||
| callback = closeSection; | |||
| 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); | |||
| if (end === -1) { | |||
| throw debug(new Error('Tag "' + openTag + '" was not closed properly'), template, line, options.file); | |||
| } | |||
| var source = template.substring(i, end); | |||
| if (callback) { | |||
| callback(source); | |||
| } | |||
| // Maintain line count for \n in source. | |||
| var n = 0; | |||
| while (~(n = source.indexOf("\n", n))) { | |||
| line++; | |||
| n++; | |||
| } | |||
| i = end + closeTag.length - 1; | |||
| openTag = nextOpenTag; | |||
| closeTag = nextCloseTag; | |||
| } else { | |||
| c = template.substr(i, 1); | |||
| 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); | |||
| } else { | |||
| nonSpace = true; | |||
| } | |||
| code.push(c); | |||
| } | |||
| } | |||
| } | |||
| if (sectionStack.length != 0) { | |||
| throw debug(new Error('Section "' + sectionStack[sectionStack.length - 1].name + '" was not closed properly'), template, line, options.file); | |||
| } | |||
| // Clean up any whitespace from a closing {{tag}} that was at the end | |||
| // of the template without a trailing \n. | |||
| stripSpace(); | |||
| code.push( | |||
| '";', | |||
| "\nreturn buffer;", | |||
| "\n} catch (e) { throw {error: e, line: line}; }" | |||
| ); | |||
| // Ignore `buffer += "";` statements. | |||
| var body = code.join("").replace(/buffer \+= "";\n/g, ""); | |||
| if (options.debug) { | |||
| if (typeof console != "undefined" && console.log) { | |||
| console.log(body); | |||
| } else if (typeof print === "function") { | |||
| print(body); | |||
| } | |||
| } | |||
| return body; | |||
| } | |||
| /** | |||
| * Used by `compile` to generate a reusable function for the given `template`. | |||
| */ | |||
| 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. | |||
| return 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); | |||
| } | |||
| }; | |||
| } | |||
| // Cache of pre-compiled templates. | |||
| var _cache = {}; | |||
| /** | |||
| * Clear the cache of compiled templates. | |||
| */ | |||
| function clearCache() { | |||
| _cache = {}; | |||
| } | |||
| /** | |||
| * 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 | |||
| */ | |||
| function compile(template, options) { | |||
| options = options || {}; | |||
| // Use a pre-compiled version from the cache if we have one. | |||
| if (options.cache !== false) { | |||
| if (!_cache[template]) { | |||
| _cache[template] = _compile(template, options); | |||
| } | |||
| return _cache[template]; | |||
| } | |||
| 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); | |||
| } | |||
| })(Mustache); | |||
| dojox.mustache = dojo.hitch(Mustache, "render"); | |||
| })(); | |||
| @@ -0,0 +1,559 @@ | |||
| /* | |||
| Shameless port of a shameless port | |||
| @defunkt => @janl => @aq | |||
| See http://github.com/defunkt/mustache for more info. | |||
| */ | |||
| ;(function($) { | |||
| /*! | |||
| * 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.14"; | |||
| 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; | |||
| } | |||
| }; | |||
| var _toString = Object.prototype.toString; | |||
| var _isArray = Array.isArray; | |||
| var _forEach = Array.prototype.forEach; | |||
| var _trim = String.prototype.trim; | |||
| var isArray; | |||
| if (_isArray) { | |||
| isArray = _isArray; | |||
| } else { | |||
| isArray = function (obj) { | |||
| return _toString.call(obj) === "[object Array]"; | |||
| }; | |||
| } | |||
| 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 spaceRe = /^\s*$/; | |||
| function isWhitespace(string) { | |||
| return spaceRe.test(string); | |||
| } | |||
| var trim; | |||
| if (_trim) { | |||
| trim = function (string) { | |||
| return string == null ? "" : _trim.call(string); | |||
| }; | |||
| } else { | |||
| var trimLeft, trimRight; | |||
| 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]+$/; | |||
| } | |||
| trim = function (string) { | |||
| return string == null ? "" : | |||
| String(string).replace(trimLeft, "").replace(trimRight, ""); | |||
| }; | |||
| } | |||
| var escapeMap = { | |||
| "&": "&", | |||
| "<": "<", | |||
| ">": ">", | |||
| '"': '"', | |||
| "'": ''' | |||
| }; | |||
| function escapeHTML(string) { | |||
| return String(string).replace(/&(?!\w+;)|[<>"']/g, function (s) { | |||
| return escapeMap[s] || s; | |||
| }); | |||
| } | |||
| /** | |||
| * Adds the `template`, `line`, and `file` properties to the given error | |||
| * object and alters the message to provide more useful debugging information. | |||
| */ | |||
| 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]; | |||
| } | |||
| e.template = template; | |||
| e.line = line; | |||
| e.file = file; | |||
| e.message = [file + ":" + line, context.join("\n"), "", e.message].join("\n"); | |||
| return e; | |||
| } | |||
| /** | |||
| * Looks up the value of the given `name` in the given context `stack`. | |||
| */ | |||
| function lookup(name, stack, defaultValue) { | |||
| if (name === ".") { | |||
| return stack[stack.length - 1]; | |||
| } | |||
| var names = name.split("."); | |||
| var lastIndex = names.length - 1; | |||
| var target = names[lastIndex]; | |||
| var value, context, i = stack.length, j, localStack; | |||
| while (i) { | |||
| localStack = stack.slice(0); | |||
| context = stack[--i]; | |||
| j = 0; | |||
| while (j < lastIndex) { | |||
| context = context[names[j++]]; | |||
| if (context == null) { | |||
| break; | |||
| } | |||
| localStack.push(context); | |||
| } | |||
| if (context && typeof context === "object" && target in context) { | |||
| value = context[target]; | |||
| break; | |||
| } | |||
| } | |||
| // If the value is a function, call it in the current context. | |||
| if (typeof value === "function") { | |||
| value = value.call(localStack[localStack.length - 1]); | |||
| } | |||
| if (value == null) { | |||
| return defaultValue; | |||
| } | |||
| return value; | |||
| } | |||
| function renderSection(name, stack, callback, inverted) { | |||
| var buffer = ""; | |||
| var value = lookup(name, stack); | |||
| 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(); | |||
| } | |||
| return buffer; | |||
| } | |||
| /** | |||
| * 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 += "' | |||
| ]; | |||
| 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? | |||
| // 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); | |||
| } | |||
| } else { | |||
| spaces = []; | |||
| } | |||
| hasTag = false; | |||
| nonSpace = false; | |||
| }; | |||
| var sectionStack = [], updateLine, nextOpenTag, nextCloseTag; | |||
| var setTags = function (source) { | |||
| tags = trim(source).split(/\s+/); | |||
| nextOpenTag = tags[0]; | |||
| nextCloseTag = tags[tags.length - 1]; | |||
| }; | |||
| 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 += "' | |||
| ); | |||
| }; | |||
| var openSection = function (source, inverted) { | |||
| var name = trim(source); | |||
| if (name === "") { | |||
| throw debug(new Error("Section name may not be empty"), template, line, options.file); | |||
| } | |||
| sectionStack.push({name: name, inverted: inverted}); | |||
| code.push( | |||
| '";', | |||
| updateLine, | |||
| '\nvar name = "' + name + '";', | |||
| '\nvar callback = (function () {', | |||
| '\n return function () {', | |||
| '\n var buffer = "";', | |||
| '\nbuffer += "' | |||
| ); | |||
| }; | |||
| var openInvertedSection = function (source) { | |||
| openSection(source, true); | |||
| }; | |||
| var closeSection = function (source) { | |||
| var name = trim(source); | |||
| var openName = sectionStack.length != 0 && sectionStack[sectionStack.length - 1].name; | |||
| if (!openName || name != openName) { | |||
| throw debug(new Error('Section named "' + name + '" was never opened'), template, line, options.file); | |||
| } | |||
| var section = sectionStack.pop(); | |||
| code.push( | |||
| '";', | |||
| '\n return buffer;', | |||
| '\n };', | |||
| '\n})();' | |||
| ); | |||
| if (section.inverted) { | |||
| code.push("\nbuffer += renderSection(name,stack,callback,true);"); | |||
| } else { | |||
| code.push("\nbuffer += renderSection(name,stack,callback);"); | |||
| } | |||
| code.push('\nbuffer += "'); | |||
| }; | |||
| var sendPlain = function (source) { | |||
| code.push( | |||
| '";', | |||
| updateLine, | |||
| '\nbuffer += lookup("' + trim(source) + '",stack,"");', | |||
| '\nbuffer += "' | |||
| ); | |||
| }; | |||
| var sendEscaped = function (source) { | |||
| code.push( | |||
| '";', | |||
| updateLine, | |||
| '\nbuffer += escapeHTML(lookup("' + trim(source) + '",stack,""));', | |||
| '\nbuffer += "' | |||
| ); | |||
| }; | |||
| 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; | |||
| break; | |||
| case "=": // change open/close tags, e.g. {{=<% %>=}} | |||
| i++; | |||
| closeTag = "=" + closeTag; | |||
| callback = setTags; | |||
| break; | |||
| case ">": // include partial | |||
| i++; | |||
| callback = includePartial; | |||
| break; | |||
| case "#": // start section | |||
| i++; | |||
| callback = openSection; | |||
| break; | |||
| case "^": // start inverted section | |||
| i++; | |||
| callback = openInvertedSection; | |||
| break; | |||
| case "/": // end section | |||
| i++; | |||
| callback = closeSection; | |||
| 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); | |||
| if (end === -1) { | |||
| throw debug(new Error('Tag "' + openTag + '" was not closed properly'), template, line, options.file); | |||
| } | |||
| var source = template.substring(i, end); | |||
| if (callback) { | |||
| callback(source); | |||
| } | |||
| // Maintain line count for \n in source. | |||
| var n = 0; | |||
| while (~(n = source.indexOf("\n", n))) { | |||
| line++; | |||
| n++; | |||
| } | |||
| i = end + closeTag.length - 1; | |||
| openTag = nextOpenTag; | |||
| closeTag = nextCloseTag; | |||
| } else { | |||
| c = template.substr(i, 1); | |||
| 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); | |||
| } else { | |||
| nonSpace = true; | |||
| } | |||
| code.push(c); | |||
| } | |||
| } | |||
| } | |||
| if (sectionStack.length != 0) { | |||
| throw debug(new Error('Section "' + sectionStack[sectionStack.length - 1].name + '" was not closed properly'), template, line, options.file); | |||
| } | |||
| // Clean up any whitespace from a closing {{tag}} that was at the end | |||
| // of the template without a trailing \n. | |||
| stripSpace(); | |||
| code.push( | |||
| '";', | |||
| "\nreturn buffer;", | |||
| "\n} catch (e) { throw {error: e, line: line}; }" | |||
| ); | |||
| // Ignore `buffer += "";` statements. | |||
| var body = code.join("").replace(/buffer \+= "";\n/g, ""); | |||
| if (options.debug) { | |||
| if (typeof console != "undefined" && console.log) { | |||
| console.log(body); | |||
| } else if (typeof print === "function") { | |||
| print(body); | |||
| } | |||
| } | |||
| return body; | |||
| } | |||
| /** | |||
| * Used by `compile` to generate a reusable function for the given `template`. | |||
| */ | |||
| 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. | |||
| return 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); | |||
| } | |||
| }; | |||
| } | |||
| // Cache of pre-compiled templates. | |||
| var _cache = {}; | |||
| /** | |||
| * Clear the cache of compiled templates. | |||
| */ | |||
| function clearCache() { | |||
| _cache = {}; | |||
| } | |||
| /** | |||
| * 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 | |||
| */ | |||
| function compile(template, options) { | |||
| options = options || {}; | |||
| // Use a pre-compiled version from the cache if we have one. | |||
| if (options.cache !== false) { | |||
| if (!_cache[template]) { | |||
| _cache[template] = _compile(template, options); | |||
| } | |||
| return _cache[template]; | |||
| } | |||
| 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); | |||
| } | |||
| })(Mustache); | |||
| $.mustache = function (template, view, partials) { | |||
| return Mustache.render(template, view, partials); | |||
| }; | |||
| $.fn.mustache = function (view, partials) { | |||
| return $(this).map(function (i, elm) { | |||
| var template = $(elm).html().trim(); | |||
| var output = $.mustache(template, view, partials); | |||
| return $(output).get(); | |||
| }); | |||
| }; | |||
| })(jQuery); | |||
| @@ -0,0 +1,543 @@ | |||
| (function(){ | |||
| /*! | |||
| * 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.14"; | |||
| 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; | |||
| } | |||
| }; | |||
| var _toString = Object.prototype.toString; | |||
| var _isArray = Array.isArray; | |||
| var _forEach = Array.prototype.forEach; | |||
| var _trim = String.prototype.trim; | |||
| var isArray; | |||
| if (_isArray) { | |||
| isArray = _isArray; | |||
| } else { | |||
| isArray = function (obj) { | |||
| return _toString.call(obj) === "[object Array]"; | |||
| }; | |||
| } | |||
| 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 spaceRe = /^\s*$/; | |||
| function isWhitespace(string) { | |||
| return spaceRe.test(string); | |||
| } | |||
| var trim; | |||
| if (_trim) { | |||
| trim = function (string) { | |||
| return string == null ? "" : _trim.call(string); | |||
| }; | |||
| } else { | |||
| var trimLeft, trimRight; | |||
| 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]+$/; | |||
| } | |||
| trim = function (string) { | |||
| return string == null ? "" : | |||
| String(string).replace(trimLeft, "").replace(trimRight, ""); | |||
| }; | |||
| } | |||
| var escapeMap = { | |||
| "&": "&", | |||
| "<": "<", | |||
| ">": ">", | |||
| '"': '"', | |||
| "'": ''' | |||
| }; | |||
| function escapeHTML(string) { | |||
| return String(string).replace(/&(?!\w+;)|[<>"']/g, function (s) { | |||
| return escapeMap[s] || s; | |||
| }); | |||
| } | |||
| /** | |||
| * Adds the `template`, `line`, and `file` properties to the given error | |||
| * object and alters the message to provide more useful debugging information. | |||
| */ | |||
| 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]; | |||
| } | |||
| e.template = template; | |||
| e.line = line; | |||
| e.file = file; | |||
| e.message = [file + ":" + line, context.join("\n"), "", e.message].join("\n"); | |||
| return e; | |||
| } | |||
| /** | |||
| * Looks up the value of the given `name` in the given context `stack`. | |||
| */ | |||
| function lookup(name, stack, defaultValue) { | |||
| if (name === ".") { | |||
| return stack[stack.length - 1]; | |||
| } | |||
| var names = name.split("."); | |||
| var lastIndex = names.length - 1; | |||
| var target = names[lastIndex]; | |||
| var value, context, i = stack.length, j, localStack; | |||
| while (i) { | |||
| localStack = stack.slice(0); | |||
| context = stack[--i]; | |||
| j = 0; | |||
| while (j < lastIndex) { | |||
| context = context[names[j++]]; | |||
| if (context == null) { | |||
| break; | |||
| } | |||
| localStack.push(context); | |||
| } | |||
| if (context && typeof context === "object" && target in context) { | |||
| value = context[target]; | |||
| break; | |||
| } | |||
| } | |||
| // If the value is a function, call it in the current context. | |||
| if (typeof value === "function") { | |||
| value = value.call(localStack[localStack.length - 1]); | |||
| } | |||
| if (value == null) { | |||
| return defaultValue; | |||
| } | |||
| return value; | |||
| } | |||
| function renderSection(name, stack, callback, inverted) { | |||
| var buffer = ""; | |||
| var value = lookup(name, stack); | |||
| 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(); | |||
| } | |||
| return buffer; | |||
| } | |||
| /** | |||
| * 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 += "' | |||
| ]; | |||
| 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? | |||
| // 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); | |||
| } | |||
| } else { | |||
| spaces = []; | |||
| } | |||
| hasTag = false; | |||
| nonSpace = false; | |||
| }; | |||
| var sectionStack = [], updateLine, nextOpenTag, nextCloseTag; | |||
| var setTags = function (source) { | |||
| tags = trim(source).split(/\s+/); | |||
| nextOpenTag = tags[0]; | |||
| nextCloseTag = tags[tags.length - 1]; | |||
| }; | |||
| 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 += "' | |||
| ); | |||
| }; | |||
| var openSection = function (source, inverted) { | |||
| var name = trim(source); | |||
| if (name === "") { | |||
| throw debug(new Error("Section name may not be empty"), template, line, options.file); | |||
| } | |||
| sectionStack.push({name: name, inverted: inverted}); | |||
| code.push( | |||
| '";', | |||
| updateLine, | |||
| '\nvar name = "' + name + '";', | |||
| '\nvar callback = (function () {', | |||
| '\n return function () {', | |||
| '\n var buffer = "";', | |||
| '\nbuffer += "' | |||
| ); | |||
| }; | |||
| var openInvertedSection = function (source) { | |||
| openSection(source, true); | |||
| }; | |||
| var closeSection = function (source) { | |||
| var name = trim(source); | |||
| var openName = sectionStack.length != 0 && sectionStack[sectionStack.length - 1].name; | |||
| if (!openName || name != openName) { | |||
| throw debug(new Error('Section named "' + name + '" was never opened'), template, line, options.file); | |||
| } | |||
| var section = sectionStack.pop(); | |||
| code.push( | |||
| '";', | |||
| '\n return buffer;', | |||
| '\n };', | |||
| '\n})();' | |||
| ); | |||
| if (section.inverted) { | |||
| code.push("\nbuffer += renderSection(name,stack,callback,true);"); | |||
| } else { | |||
| code.push("\nbuffer += renderSection(name,stack,callback);"); | |||
| } | |||
| code.push('\nbuffer += "'); | |||
| }; | |||
| var sendPlain = function (source) { | |||
| code.push( | |||
| '";', | |||
| updateLine, | |||
| '\nbuffer += lookup("' + trim(source) + '",stack,"");', | |||
| '\nbuffer += "' | |||
| ); | |||
| }; | |||
| var sendEscaped = function (source) { | |||
| code.push( | |||
| '";', | |||
| updateLine, | |||
| '\nbuffer += escapeHTML(lookup("' + trim(source) + '",stack,""));', | |||
| '\nbuffer += "' | |||
| ); | |||
| }; | |||
| 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; | |||
| break; | |||
| case "=": // change open/close tags, e.g. {{=<% %>=}} | |||
| i++; | |||
| closeTag = "=" + closeTag; | |||
| callback = setTags; | |||
| break; | |||
| case ">": // include partial | |||
| i++; | |||
| callback = includePartial; | |||
| break; | |||
| case "#": // start section | |||
| i++; | |||
| callback = openSection; | |||
| break; | |||
| case "^": // start inverted section | |||
| i++; | |||
| callback = openInvertedSection; | |||
| break; | |||
| case "/": // end section | |||
| i++; | |||
| callback = closeSection; | |||
| 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); | |||
| if (end === -1) { | |||
| throw debug(new Error('Tag "' + openTag + '" was not closed properly'), template, line, options.file); | |||
| } | |||
| var source = template.substring(i, end); | |||
| if (callback) { | |||
| callback(source); | |||
| } | |||
| // Maintain line count for \n in source. | |||
| var n = 0; | |||
| while (~(n = source.indexOf("\n", n))) { | |||
| line++; | |||
| n++; | |||
| } | |||
| i = end + closeTag.length - 1; | |||
| openTag = nextOpenTag; | |||
| closeTag = nextCloseTag; | |||
| } else { | |||
| c = template.substr(i, 1); | |||
| 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); | |||
| } else { | |||
| nonSpace = true; | |||
| } | |||
| code.push(c); | |||
| } | |||
| } | |||
| } | |||
| if (sectionStack.length != 0) { | |||
| throw debug(new Error('Section "' + sectionStack[sectionStack.length - 1].name + '" was not closed properly'), template, line, options.file); | |||
| } | |||
| // Clean up any whitespace from a closing {{tag}} that was at the end | |||
| // of the template without a trailing \n. | |||
| stripSpace(); | |||
| code.push( | |||
| '";', | |||
| "\nreturn buffer;", | |||
| "\n} catch (e) { throw {error: e, line: line}; }" | |||
| ); | |||
| // Ignore `buffer += "";` statements. | |||
| var body = code.join("").replace(/buffer \+= "";\n/g, ""); | |||
| if (options.debug) { | |||
| if (typeof console != "undefined" && console.log) { | |||
| console.log(body); | |||
| } else if (typeof print === "function") { | |||
| print(body); | |||
| } | |||
| } | |||
| return body; | |||
| } | |||
| /** | |||
| * Used by `compile` to generate a reusable function for the given `template`. | |||
| */ | |||
| 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. | |||
| return 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); | |||
| } | |||
| }; | |||
| } | |||
| // Cache of pre-compiled templates. | |||
| var _cache = {}; | |||
| /** | |||
| * Clear the cache of compiled templates. | |||
| */ | |||
| function clearCache() { | |||
| _cache = {}; | |||
| } | |||
| /** | |||
| * 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 | |||
| */ | |||
| function compile(template, options) { | |||
| options = options || {}; | |||
| // Use a pre-compiled version from the cache if we have one. | |||
| if (options.cache !== false) { | |||
| if (!_cache[template]) { | |||
| _cache[template] = _compile(template, options); | |||
| } | |||
| return _cache[template]; | |||
| } | |||
| 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); | |||
| } | |||
| })(Mustache); | |||
| Object.implement('mustache', function(view, partials){ | |||
| return Mustache.render(view, this, partials); | |||
| }); | |||
| })(); | |||
| @@ -0,0 +1,671 @@ | |||
| /* ************************************************************************ | |||
| qooxdoo - the new era of web development | |||
| http://qooxdoo.org | |||
| Copyright: | |||
| 2004-2011 1&1 Internet AG, Germany, http://www.1und1.de | |||
| License: | |||
| LGPL: http://www.gnu.org/licenses/lgpl.html | |||
| EPL: http://www.eclipse.org/org/documents/epl-v10.php | |||
| See the LICENSE file in the project's top-level directory for details. | |||
| Authors: | |||
| * Martin Wittemann (martinwittemann) | |||
| ====================================================================== | |||
| This class contains code based on the following work: | |||
| * Mustache.js version 0.5.0-dev | |||
| Code: | |||
| https://github.com/janl/mustache.js | |||
| Copyright: | |||
| (c) 2009 Chris Wanstrath (Ruby) | |||
| (c) 2010 Jan Lehnardt (JavaScript) | |||
| License: | |||
| MIT: http://www.opensource.org/licenses/mit-license.php | |||
| ---------------------------------------------------------------------- | |||
| Copyright (c) 2009 Chris Wanstrath (Ruby) | |||
| Copyright (c) 2010 Jan Lehnardt (JavaScript) | |||
| Permission is hereby granted, free of charge, to any person obtaining | |||
| a copy of this software and associated documentation files (the | |||
| "Software"), to deal in the Software without restriction, including | |||
| without limitation the rights to use, copy, modify, merge, publish, | |||
| distribute, sublicense, and/or sell copies of the Software, and to | |||
| permit persons to whom the Software is furnished to do so, subject to | |||
| the following conditions: | |||
| The above copyright notice and this permission notice shall be | |||
| included in all copies or substantial portions of the Software. | |||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |||
| EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |||
| MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||
| NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | |||
| LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | |||
| OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |||
| WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||
| ************************************************************************ */ | |||
| /** | |||
| * The is a template class which can be used for HTML templating. In fact, | |||
| * this is a wrapper for mustache.js which is a "framework-agnostic way to | |||
| * render logic-free views". | |||
| * | |||
| * Here is a basic example how to use it: | |||
| * Template: | |||
| * <pre> | |||
| * var template = "Hi, my name is {{name}}!"; | |||
| * var view = {name: "qooxdoo"}; | |||
| * qx.bom.Template.toHtml(template, view); | |||
| * // return "Hi, my name is qooxdoo!" | |||
| * </pre> | |||
| * | |||
| * For further details, please visit the mustache.js documentation here: | |||
| * https://github.com/janl/mustache.js/blob/master/README.md | |||
| */ | |||
| qx.Class.define("qx.bom.Template", { | |||
| statics : { | |||
| /** Contains the mustache.js version. */ | |||
| version: null, | |||
| /** | |||
| * Original and only template method of mustache.js. For further | |||
| * documentation, please visit https://github.com/janl/mustache.js | |||
| * | |||
| * @signature function(template, view, partials, send_fun) | |||
| * @param template {String} The String containing the template. | |||
| * @param view {Object} The object holding the data to render. | |||
| * @param partials {Object} Object holding parts of a template. | |||
| * @param send_fun {Function?} Callback function for streaming. | |||
| * @return {String} The parsed template. | |||
| */ | |||
| toHtml: null, | |||
| /** | |||
| * Helper method which provides you with a direct access to templates | |||
| * stored as HTML in the DOM. The DOM node with the given ID will be reated | |||
| * as a template, parsed and a new DOM node will be returned containing the | |||
| * parsed data. | |||
| * | |||
| * @param id {String} The id of the HTML template in the DOM. | |||
| * @param view {Object} The object holding the data to render. | |||
| * @param partials {Object} Object holding parts of a template. | |||
| * @return {DomNode} A DOM element holding the parsed template data. | |||
| */ | |||
| get : function(id, view, partials) { | |||
| var template = document.getElementById(id); | |||
| var inner = template.innerHTML; | |||
| inner = this.toHtml(inner, view, partials); | |||
| var helper = qx.bom.Element.create("div"); | |||
| helper.innerHTML = inner; | |||
| return helper.children[0]; | |||
| } | |||
| } | |||
| }); | |||
| (function() { | |||
| /** | |||
| * Below is the original mustache.js code. Snapshot date is mentioned in | |||
| * the head of this file. | |||
| */ | |||
| /*! | |||
| * 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.14"; | |||
| 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; | |||
| } | |||
| }; | |||
| var _toString = Object.prototype.toString; | |||
| var _isArray = Array.isArray; | |||
| var _forEach = Array.prototype.forEach; | |||
| var _trim = String.prototype.trim; | |||
| var isArray; | |||
| if (_isArray) { | |||
| isArray = _isArray; | |||
| } else { | |||
| isArray = function (obj) { | |||
| return _toString.call(obj) === "[object Array]"; | |||
| }; | |||
| } | |||
| 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 spaceRe = /^\s*$/; | |||
| function isWhitespace(string) { | |||
| return spaceRe.test(string); | |||
| } | |||
| var trim; | |||
| if (_trim) { | |||
| trim = function (string) { | |||
| return string == null ? "" : _trim.call(string); | |||
| }; | |||
| } else { | |||
| var trimLeft, trimRight; | |||
| 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]+$/; | |||
| } | |||
| trim = function (string) { | |||
| return string == null ? "" : | |||
| String(string).replace(trimLeft, "").replace(trimRight, ""); | |||
| }; | |||
| } | |||
| var escapeMap = { | |||
| "&": "&", | |||
| "<": "<", | |||
| ">": ">", | |||
| '"': '"', | |||
| "'": ''' | |||
| }; | |||
| function escapeHTML(string) { | |||
| return String(string).replace(/&(?!\w+;)|[<>"']/g, function (s) { | |||
| return escapeMap[s] || s; | |||
| }); | |||
| } | |||
| /** | |||
| * Adds the `template`, `line`, and `file` properties to the given error | |||
| * object and alters the message to provide more useful debugging information. | |||
| */ | |||
| 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]; | |||
| } | |||
| e.template = template; | |||
| e.line = line; | |||
| e.file = file; | |||
| e.message = [file + ":" + line, context.join("\n"), "", e.message].join("\n"); | |||
| return e; | |||
| } | |||
| /** | |||
| * Looks up the value of the given `name` in the given context `stack`. | |||
| */ | |||
| function lookup(name, stack, defaultValue) { | |||
| if (name === ".") { | |||
| return stack[stack.length - 1]; | |||
| } | |||
| var names = name.split("."); | |||
| var lastIndex = names.length - 1; | |||
| var target = names[lastIndex]; | |||
| var value, context, i = stack.length, j, localStack; | |||
| while (i) { | |||
| localStack = stack.slice(0); | |||
| context = stack[--i]; | |||
| j = 0; | |||
| while (j < lastIndex) { | |||
| context = context[names[j++]]; | |||
| if (context == null) { | |||
| break; | |||
| } | |||
| localStack.push(context); | |||
| } | |||
| if (context && typeof context === "object" && target in context) { | |||
| value = context[target]; | |||
| break; | |||
| } | |||
| } | |||
| // If the value is a function, call it in the current context. | |||
| if (typeof value === "function") { | |||
| value = value.call(localStack[localStack.length - 1]); | |||
| } | |||
| if (value == null) { | |||
| return defaultValue; | |||
| } | |||
| return value; | |||
| } | |||
| function renderSection(name, stack, callback, inverted) { | |||
| var buffer = ""; | |||
| var value = lookup(name, stack); | |||
| 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(); | |||
| } | |||
| return buffer; | |||
| } | |||
| /** | |||
| * 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 += "' | |||
| ]; | |||
| 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? | |||
| // 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); | |||
| } | |||
| } else { | |||
| spaces = []; | |||
| } | |||
| hasTag = false; | |||
| nonSpace = false; | |||
| }; | |||
| var sectionStack = [], updateLine, nextOpenTag, nextCloseTag; | |||
| var setTags = function (source) { | |||
| tags = trim(source).split(/\s+/); | |||
| nextOpenTag = tags[0]; | |||
| nextCloseTag = tags[tags.length - 1]; | |||
| }; | |||
| 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 += "' | |||
| ); | |||
| }; | |||
| var openSection = function (source, inverted) { | |||
| var name = trim(source); | |||
| if (name === "") { | |||
| throw debug(new Error("Section name may not be empty"), template, line, options.file); | |||
| } | |||
| sectionStack.push({name: name, inverted: inverted}); | |||
| code.push( | |||
| '";', | |||
| updateLine, | |||
| '\nvar name = "' + name + '";', | |||
| '\nvar callback = (function () {', | |||
| '\n return function () {', | |||
| '\n var buffer = "";', | |||
| '\nbuffer += "' | |||
| ); | |||
| }; | |||
| var openInvertedSection = function (source) { | |||
| openSection(source, true); | |||
| }; | |||
| var closeSection = function (source) { | |||
| var name = trim(source); | |||
| var openName = sectionStack.length != 0 && sectionStack[sectionStack.length - 1].name; | |||
| if (!openName || name != openName) { | |||
| throw debug(new Error('Section named "' + name + '" was never opened'), template, line, options.file); | |||
| } | |||
| var section = sectionStack.pop(); | |||
| code.push( | |||
| '";', | |||
| '\n return buffer;', | |||
| '\n };', | |||
| '\n})();' | |||
| ); | |||
| if (section.inverted) { | |||
| code.push("\nbuffer += renderSection(name,stack,callback,true);"); | |||
| } else { | |||
| code.push("\nbuffer += renderSection(name,stack,callback);"); | |||
| } | |||
| code.push('\nbuffer += "'); | |||
| }; | |||
| var sendPlain = function (source) { | |||
| code.push( | |||
| '";', | |||
| updateLine, | |||
| '\nbuffer += lookup("' + trim(source) + '",stack,"");', | |||
| '\nbuffer += "' | |||
| ); | |||
| }; | |||
| var sendEscaped = function (source) { | |||
| code.push( | |||
| '";', | |||
| updateLine, | |||
| '\nbuffer += escapeHTML(lookup("' + trim(source) + '",stack,""));', | |||
| '\nbuffer += "' | |||
| ); | |||
| }; | |||
| 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; | |||
| break; | |||
| case "=": // change open/close tags, e.g. {{=<% %>=}} | |||
| i++; | |||
| closeTag = "=" + closeTag; | |||
| callback = setTags; | |||
| break; | |||
| case ">": // include partial | |||
| i++; | |||
| callback = includePartial; | |||
| break; | |||
| case "#": // start section | |||
| i++; | |||
| callback = openSection; | |||
| break; | |||
| case "^": // start inverted section | |||
| i++; | |||
| callback = openInvertedSection; | |||
| break; | |||
| case "/": // end section | |||
| i++; | |||
| callback = closeSection; | |||
| 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); | |||
| if (end === -1) { | |||
| throw debug(new Error('Tag "' + openTag + '" was not closed properly'), template, line, options.file); | |||
| } | |||
| var source = template.substring(i, end); | |||
| if (callback) { | |||
| callback(source); | |||
| } | |||
| // Maintain line count for \n in source. | |||
| var n = 0; | |||
| while (~(n = source.indexOf("\n", n))) { | |||
| line++; | |||
| n++; | |||
| } | |||
| i = end + closeTag.length - 1; | |||
| openTag = nextOpenTag; | |||
| closeTag = nextCloseTag; | |||
| } else { | |||
| c = template.substr(i, 1); | |||
| 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); | |||
| } else { | |||
| nonSpace = true; | |||
| } | |||
| code.push(c); | |||
| } | |||
| } | |||
| } | |||
| if (sectionStack.length != 0) { | |||
| throw debug(new Error('Section "' + sectionStack[sectionStack.length - 1].name + '" was not closed properly'), template, line, options.file); | |||
| } | |||
| // Clean up any whitespace from a closing {{tag}} that was at the end | |||
| // of the template without a trailing \n. | |||
| stripSpace(); | |||
| code.push( | |||
| '";', | |||
| "\nreturn buffer;", | |||
| "\n} catch (e) { throw {error: e, line: line}; }" | |||
| ); | |||
| // Ignore `buffer += "";` statements. | |||
| var body = code.join("").replace(/buffer \+= "";\n/g, ""); | |||
| if (options.debug) { | |||
| if (typeof console != "undefined" && console.log) { | |||
| console.log(body); | |||
| } else if (typeof print === "function") { | |||
| print(body); | |||
| } | |||
| } | |||
| return body; | |||
| } | |||
| /** | |||
| * Used by `compile` to generate a reusable function for the given `template`. | |||
| */ | |||
| 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. | |||
| return 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); | |||
| } | |||
| }; | |||
| } | |||
| // Cache of pre-compiled templates. | |||
| var _cache = {}; | |||
| /** | |||
| * Clear the cache of compiled templates. | |||
| */ | |||
| function clearCache() { | |||
| _cache = {}; | |||
| } | |||
| /** | |||
| * 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 | |||
| */ | |||
| function compile(template, options) { | |||
| options = options || {}; | |||
| // Use a pre-compiled version from the cache if we have one. | |||
| if (options.cache !== false) { | |||
| if (!_cache[template]) { | |||
| _cache[template] = _compile(template, options); | |||
| } | |||
| return _cache[template]; | |||
| } | |||
| 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); | |||
| } | |||
| })(Mustache); | |||
| /** | |||
| * Above is the original mustache code. | |||
| */ | |||
| // EXPOSE qooxdoo variant | |||
| qx.bom.Template.version = Mustache.version; | |||
| qx.bom.Template.toHtml = Mustache.render; | |||
| })(); | |||
| @@ -0,0 +1,545 @@ | |||
| /* | |||
| Shameless port of a shameless port ^ 2 | |||
| @defunkt => @janl => @aq => @voodootikigod => @timruffles | |||
| */ | |||
| define(function(){ | |||
| /*! | |||
| * 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.14"; | |||
| 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; | |||
| } | |||
| }; | |||
| var _toString = Object.prototype.toString; | |||
| var _isArray = Array.isArray; | |||
| var _forEach = Array.prototype.forEach; | |||
| var _trim = String.prototype.trim; | |||
| var isArray; | |||
| if (_isArray) { | |||
| isArray = _isArray; | |||
| } else { | |||
| isArray = function (obj) { | |||
| return _toString.call(obj) === "[object Array]"; | |||
| }; | |||
| } | |||
| 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 spaceRe = /^\s*$/; | |||
| function isWhitespace(string) { | |||
| return spaceRe.test(string); | |||
| } | |||
| var trim; | |||
| if (_trim) { | |||
| trim = function (string) { | |||
| return string == null ? "" : _trim.call(string); | |||
| }; | |||
| } else { | |||
| var trimLeft, trimRight; | |||
| 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]+$/; | |||
| } | |||
| trim = function (string) { | |||
| return string == null ? "" : | |||
| String(string).replace(trimLeft, "").replace(trimRight, ""); | |||
| }; | |||
| } | |||
| var escapeMap = { | |||
| "&": "&", | |||
| "<": "<", | |||
| ">": ">", | |||
| '"': '"', | |||
| "'": ''' | |||
| }; | |||
| function escapeHTML(string) { | |||
| return String(string).replace(/&(?!\w+;)|[<>"']/g, function (s) { | |||
| return escapeMap[s] || s; | |||
| }); | |||
| } | |||
| /** | |||
| * Adds the `template`, `line`, and `file` properties to the given error | |||
| * object and alters the message to provide more useful debugging information. | |||
| */ | |||
| 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]; | |||
| } | |||
| e.template = template; | |||
| e.line = line; | |||
| e.file = file; | |||
| e.message = [file + ":" + line, context.join("\n"), "", e.message].join("\n"); | |||
| return e; | |||
| } | |||
| /** | |||
| * Looks up the value of the given `name` in the given context `stack`. | |||
| */ | |||
| function lookup(name, stack, defaultValue) { | |||
| if (name === ".") { | |||
| return stack[stack.length - 1]; | |||
| } | |||
| var names = name.split("."); | |||
| var lastIndex = names.length - 1; | |||
| var target = names[lastIndex]; | |||
| var value, context, i = stack.length, j, localStack; | |||
| while (i) { | |||
| localStack = stack.slice(0); | |||
| context = stack[--i]; | |||
| j = 0; | |||
| while (j < lastIndex) { | |||
| context = context[names[j++]]; | |||
| if (context == null) { | |||
| break; | |||
| } | |||
| localStack.push(context); | |||
| } | |||
| if (context && typeof context === "object" && target in context) { | |||
| value = context[target]; | |||
| break; | |||
| } | |||
| } | |||
| // If the value is a function, call it in the current context. | |||
| if (typeof value === "function") { | |||
| value = value.call(localStack[localStack.length - 1]); | |||
| } | |||
| if (value == null) { | |||
| return defaultValue; | |||
| } | |||
| return value; | |||
| } | |||
| function renderSection(name, stack, callback, inverted) { | |||
| var buffer = ""; | |||
| var value = lookup(name, stack); | |||
| 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(); | |||
| } | |||
| return buffer; | |||
| } | |||
| /** | |||
| * 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 += "' | |||
| ]; | |||
| 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? | |||
| // 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); | |||
| } | |||
| } else { | |||
| spaces = []; | |||
| } | |||
| hasTag = false; | |||
| nonSpace = false; | |||
| }; | |||
| var sectionStack = [], updateLine, nextOpenTag, nextCloseTag; | |||
| var setTags = function (source) { | |||
| tags = trim(source).split(/\s+/); | |||
| nextOpenTag = tags[0]; | |||
| nextCloseTag = tags[tags.length - 1]; | |||
| }; | |||
| 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 += "' | |||
| ); | |||
| }; | |||
| var openSection = function (source, inverted) { | |||
| var name = trim(source); | |||
| if (name === "") { | |||
| throw debug(new Error("Section name may not be empty"), template, line, options.file); | |||
| } | |||
| sectionStack.push({name: name, inverted: inverted}); | |||
| code.push( | |||
| '";', | |||
| updateLine, | |||
| '\nvar name = "' + name + '";', | |||
| '\nvar callback = (function () {', | |||
| '\n return function () {', | |||
| '\n var buffer = "";', | |||
| '\nbuffer += "' | |||
| ); | |||
| }; | |||
| var openInvertedSection = function (source) { | |||
| openSection(source, true); | |||
| }; | |||
| var closeSection = function (source) { | |||
| var name = trim(source); | |||
| var openName = sectionStack.length != 0 && sectionStack[sectionStack.length - 1].name; | |||
| if (!openName || name != openName) { | |||
| throw debug(new Error('Section named "' + name + '" was never opened'), template, line, options.file); | |||
| } | |||
| var section = sectionStack.pop(); | |||
| code.push( | |||
| '";', | |||
| '\n return buffer;', | |||
| '\n };', | |||
| '\n})();' | |||
| ); | |||
| if (section.inverted) { | |||
| code.push("\nbuffer += renderSection(name,stack,callback,true);"); | |||
| } else { | |||
| code.push("\nbuffer += renderSection(name,stack,callback);"); | |||
| } | |||
| code.push('\nbuffer += "'); | |||
| }; | |||
| var sendPlain = function (source) { | |||
| code.push( | |||
| '";', | |||
| updateLine, | |||
| '\nbuffer += lookup("' + trim(source) + '",stack,"");', | |||
| '\nbuffer += "' | |||
| ); | |||
| }; | |||
| var sendEscaped = function (source) { | |||
| code.push( | |||
| '";', | |||
| updateLine, | |||
| '\nbuffer += escapeHTML(lookup("' + trim(source) + '",stack,""));', | |||
| '\nbuffer += "' | |||
| ); | |||
| }; | |||
| 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; | |||
| break; | |||
| case "=": // change open/close tags, e.g. {{=<% %>=}} | |||
| i++; | |||
| closeTag = "=" + closeTag; | |||
| callback = setTags; | |||
| break; | |||
| case ">": // include partial | |||
| i++; | |||
| callback = includePartial; | |||
| break; | |||
| case "#": // start section | |||
| i++; | |||
| callback = openSection; | |||
| break; | |||
| case "^": // start inverted section | |||
| i++; | |||
| callback = openInvertedSection; | |||
| break; | |||
| case "/": // end section | |||
| i++; | |||
| callback = closeSection; | |||
| 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); | |||
| if (end === -1) { | |||
| throw debug(new Error('Tag "' + openTag + '" was not closed properly'), template, line, options.file); | |||
| } | |||
| var source = template.substring(i, end); | |||
| if (callback) { | |||
| callback(source); | |||
| } | |||
| // Maintain line count for \n in source. | |||
| var n = 0; | |||
| while (~(n = source.indexOf("\n", n))) { | |||
| line++; | |||
| n++; | |||
| } | |||
| i = end + closeTag.length - 1; | |||
| openTag = nextOpenTag; | |||
| closeTag = nextCloseTag; | |||
| } else { | |||
| c = template.substr(i, 1); | |||
| 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); | |||
| } else { | |||
| nonSpace = true; | |||
| } | |||
| code.push(c); | |||
| } | |||
| } | |||
| } | |||
| if (sectionStack.length != 0) { | |||
| throw debug(new Error('Section "' + sectionStack[sectionStack.length - 1].name + '" was not closed properly'), template, line, options.file); | |||
| } | |||
| // Clean up any whitespace from a closing {{tag}} that was at the end | |||
| // of the template without a trailing \n. | |||
| stripSpace(); | |||
| code.push( | |||
| '";', | |||
| "\nreturn buffer;", | |||
| "\n} catch (e) { throw {error: e, line: line}; }" | |||
| ); | |||
| // Ignore `buffer += "";` statements. | |||
| var body = code.join("").replace(/buffer \+= "";\n/g, ""); | |||
| if (options.debug) { | |||
| if (typeof console != "undefined" && console.log) { | |||
| console.log(body); | |||
| } else if (typeof print === "function") { | |||
| print(body); | |||
| } | |||
| } | |||
| return body; | |||
| } | |||
| /** | |||
| * Used by `compile` to generate a reusable function for the given `template`. | |||
| */ | |||
| 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. | |||
| return 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); | |||
| } | |||
| }; | |||
| } | |||
| // Cache of pre-compiled templates. | |||
| var _cache = {}; | |||
| /** | |||
| * Clear the cache of compiled templates. | |||
| */ | |||
| function clearCache() { | |||
| _cache = {}; | |||
| } | |||
| /** | |||
| * 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 | |||
| */ | |||
| function compile(template, options) { | |||
| options = options || {}; | |||
| // Use a pre-compiled version from the cache if we have one. | |||
| if (options.cache !== false) { | |||
| if (!_cache[template]) { | |||
| _cache[template] = _compile(template, options); | |||
| } | |||
| return _cache[template]; | |||
| } | |||
| 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); | |||
| } | |||
| })(Mustache); | |||
| return Mustache; | |||
| }); | |||
| @@ -0,0 +1,541 @@ | |||
| YUI.add("mustache", function(Y) { | |||
| /*! | |||
| * 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.14"; | |||
| 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; | |||
| } | |||
| }; | |||
| var _toString = Object.prototype.toString; | |||
| var _isArray = Array.isArray; | |||
| var _forEach = Array.prototype.forEach; | |||
| var _trim = String.prototype.trim; | |||
| var isArray; | |||
| if (_isArray) { | |||
| isArray = _isArray; | |||
| } else { | |||
| isArray = function (obj) { | |||
| return _toString.call(obj) === "[object Array]"; | |||
| }; | |||
| } | |||
| 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 spaceRe = /^\s*$/; | |||
| function isWhitespace(string) { | |||
| return spaceRe.test(string); | |||
| } | |||
| var trim; | |||
| if (_trim) { | |||
| trim = function (string) { | |||
| return string == null ? "" : _trim.call(string); | |||
| }; | |||
| } else { | |||
| var trimLeft, trimRight; | |||
| 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]+$/; | |||
| } | |||
| trim = function (string) { | |||
| return string == null ? "" : | |||
| String(string).replace(trimLeft, "").replace(trimRight, ""); | |||
| }; | |||
| } | |||
| var escapeMap = { | |||
| "&": "&", | |||
| "<": "<", | |||
| ">": ">", | |||
| '"': '"', | |||
| "'": ''' | |||
| }; | |||
| function escapeHTML(string) { | |||
| return String(string).replace(/&(?!\w+;)|[<>"']/g, function (s) { | |||
| return escapeMap[s] || s; | |||
| }); | |||
| } | |||
| /** | |||
| * Adds the `template`, `line`, and `file` properties to the given error | |||
| * object and alters the message to provide more useful debugging information. | |||
| */ | |||
| 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]; | |||
| } | |||
| e.template = template; | |||
| e.line = line; | |||
| e.file = file; | |||
| e.message = [file + ":" + line, context.join("\n"), "", e.message].join("\n"); | |||
| return e; | |||
| } | |||
| /** | |||
| * Looks up the value of the given `name` in the given context `stack`. | |||
| */ | |||
| function lookup(name, stack, defaultValue) { | |||
| if (name === ".") { | |||
| return stack[stack.length - 1]; | |||
| } | |||
| var names = name.split("."); | |||
| var lastIndex = names.length - 1; | |||
| var target = names[lastIndex]; | |||
| var value, context, i = stack.length, j, localStack; | |||
| while (i) { | |||
| localStack = stack.slice(0); | |||
| context = stack[--i]; | |||
| j = 0; | |||
| while (j < lastIndex) { | |||
| context = context[names[j++]]; | |||
| if (context == null) { | |||
| break; | |||
| } | |||
| localStack.push(context); | |||
| } | |||
| if (context && typeof context === "object" && target in context) { | |||
| value = context[target]; | |||
| break; | |||
| } | |||
| } | |||
| // If the value is a function, call it in the current context. | |||
| if (typeof value === "function") { | |||
| value = value.call(localStack[localStack.length - 1]); | |||
| } | |||
| if (value == null) { | |||
| return defaultValue; | |||
| } | |||
| return value; | |||
| } | |||
| function renderSection(name, stack, callback, inverted) { | |||
| var buffer = ""; | |||
| var value = lookup(name, stack); | |||
| 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(); | |||
| } | |||
| return buffer; | |||
| } | |||
| /** | |||
| * 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 += "' | |||
| ]; | |||
| 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? | |||
| // 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); | |||
| } | |||
| } else { | |||
| spaces = []; | |||
| } | |||
| hasTag = false; | |||
| nonSpace = false; | |||
| }; | |||
| var sectionStack = [], updateLine, nextOpenTag, nextCloseTag; | |||
| var setTags = function (source) { | |||
| tags = trim(source).split(/\s+/); | |||
| nextOpenTag = tags[0]; | |||
| nextCloseTag = tags[tags.length - 1]; | |||
| }; | |||
| 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 += "' | |||
| ); | |||
| }; | |||
| var openSection = function (source, inverted) { | |||
| var name = trim(source); | |||
| if (name === "") { | |||
| throw debug(new Error("Section name may not be empty"), template, line, options.file); | |||
| } | |||
| sectionStack.push({name: name, inverted: inverted}); | |||
| code.push( | |||
| '";', | |||
| updateLine, | |||
| '\nvar name = "' + name + '";', | |||
| '\nvar callback = (function () {', | |||
| '\n return function () {', | |||
| '\n var buffer = "";', | |||
| '\nbuffer += "' | |||
| ); | |||
| }; | |||
| var openInvertedSection = function (source) { | |||
| openSection(source, true); | |||
| }; | |||
| var closeSection = function (source) { | |||
| var name = trim(source); | |||
| var openName = sectionStack.length != 0 && sectionStack[sectionStack.length - 1].name; | |||
| if (!openName || name != openName) { | |||
| throw debug(new Error('Section named "' + name + '" was never opened'), template, line, options.file); | |||
| } | |||
| var section = sectionStack.pop(); | |||
| code.push( | |||
| '";', | |||
| '\n return buffer;', | |||
| '\n };', | |||
| '\n})();' | |||
| ); | |||
| if (section.inverted) { | |||
| code.push("\nbuffer += renderSection(name,stack,callback,true);"); | |||
| } else { | |||
| code.push("\nbuffer += renderSection(name,stack,callback);"); | |||
| } | |||
| code.push('\nbuffer += "'); | |||
| }; | |||
| var sendPlain = function (source) { | |||
| code.push( | |||
| '";', | |||
| updateLine, | |||
| '\nbuffer += lookup("' + trim(source) + '",stack,"");', | |||
| '\nbuffer += "' | |||
| ); | |||
| }; | |||
| var sendEscaped = function (source) { | |||
| code.push( | |||
| '";', | |||
| updateLine, | |||
| '\nbuffer += escapeHTML(lookup("' + trim(source) + '",stack,""));', | |||
| '\nbuffer += "' | |||
| ); | |||
| }; | |||
| 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; | |||
| break; | |||
| case "=": // change open/close tags, e.g. {{=<% %>=}} | |||
| i++; | |||
| closeTag = "=" + closeTag; | |||
| callback = setTags; | |||
| break; | |||
| case ">": // include partial | |||
| i++; | |||
| callback = includePartial; | |||
| break; | |||
| case "#": // start section | |||
| i++; | |||
| callback = openSection; | |||
| break; | |||
| case "^": // start inverted section | |||
| i++; | |||
| callback = openInvertedSection; | |||
| break; | |||
| case "/": // end section | |||
| i++; | |||
| callback = closeSection; | |||
| 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); | |||
| if (end === -1) { | |||
| throw debug(new Error('Tag "' + openTag + '" was not closed properly'), template, line, options.file); | |||
| } | |||
| var source = template.substring(i, end); | |||
| if (callback) { | |||
| callback(source); | |||
| } | |||
| // Maintain line count for \n in source. | |||
| var n = 0; | |||
| while (~(n = source.indexOf("\n", n))) { | |||
| line++; | |||
| n++; | |||
| } | |||
| i = end + closeTag.length - 1; | |||
| openTag = nextOpenTag; | |||
| closeTag = nextCloseTag; | |||
| } else { | |||
| c = template.substr(i, 1); | |||
| 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); | |||
| } else { | |||
| nonSpace = true; | |||
| } | |||
| code.push(c); | |||
| } | |||
| } | |||
| } | |||
| if (sectionStack.length != 0) { | |||
| throw debug(new Error('Section "' + sectionStack[sectionStack.length - 1].name + '" was not closed properly'), template, line, options.file); | |||
| } | |||
| // Clean up any whitespace from a closing {{tag}} that was at the end | |||
| // of the template without a trailing \n. | |||
| stripSpace(); | |||
| code.push( | |||
| '";', | |||
| "\nreturn buffer;", | |||
| "\n} catch (e) { throw {error: e, line: line}; }" | |||
| ); | |||
| // Ignore `buffer += "";` statements. | |||
| var body = code.join("").replace(/buffer \+= "";\n/g, ""); | |||
| if (options.debug) { | |||
| if (typeof console != "undefined" && console.log) { | |||
| console.log(body); | |||
| } else if (typeof print === "function") { | |||
| print(body); | |||
| } | |||
| } | |||
| return body; | |||
| } | |||
| /** | |||
| * Used by `compile` to generate a reusable function for the given `template`. | |||
| */ | |||
| 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. | |||
| return 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); | |||
| } | |||
| }; | |||
| } | |||
| // Cache of pre-compiled templates. | |||
| var _cache = {}; | |||
| /** | |||
| * Clear the cache of compiled templates. | |||
| */ | |||
| function clearCache() { | |||
| _cache = {}; | |||
| } | |||
| /** | |||
| * 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 | |||
| */ | |||
| function compile(template, options) { | |||
| options = options || {}; | |||
| // Use a pre-compiled version from the cache if we have one. | |||
| if (options.cache !== false) { | |||
| if (!_cache[template]) { | |||
| _cache[template] = _compile(template, options); | |||
| } | |||
| return _cache[template]; | |||
| } | |||
| 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); | |||
| } | |||
| })(Mustache); | |||
| Y.mustache = Mustache.render; | |||
| }, "0"); | |||
| @@ -0,0 +1,536 @@ | |||
| /*! | |||
| * 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.14"; | |||
| 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; | |||
| } | |||
| }; | |||
| var _toString = Object.prototype.toString; | |||
| var _isArray = Array.isArray; | |||
| var _forEach = Array.prototype.forEach; | |||
| var _trim = String.prototype.trim; | |||
| var isArray; | |||
| if (_isArray) { | |||
| isArray = _isArray; | |||
| } else { | |||
| isArray = function (obj) { | |||
| return _toString.call(obj) === "[object Array]"; | |||
| }; | |||
| } | |||
| 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 spaceRe = /^\s*$/; | |||
| function isWhitespace(string) { | |||
| return spaceRe.test(string); | |||
| } | |||
| var trim; | |||
| if (_trim) { | |||
| trim = function (string) { | |||
| return string == null ? "" : _trim.call(string); | |||
| }; | |||
| } else { | |||
| var trimLeft, trimRight; | |||
| 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]+$/; | |||
| } | |||
| trim = function (string) { | |||
| return string == null ? "" : | |||
| String(string).replace(trimLeft, "").replace(trimRight, ""); | |||
| }; | |||
| } | |||
| var escapeMap = { | |||
| "&": "&", | |||
| "<": "<", | |||
| ">": ">", | |||
| '"': '"', | |||
| "'": ''' | |||
| }; | |||
| function escapeHTML(string) { | |||
| return String(string).replace(/&(?!\w+;)|[<>"']/g, function (s) { | |||
| return escapeMap[s] || s; | |||
| }); | |||
| } | |||
| /** | |||
| * Adds the `template`, `line`, and `file` properties to the given error | |||
| * object and alters the message to provide more useful debugging information. | |||
| */ | |||
| 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]; | |||
| } | |||
| e.template = template; | |||
| e.line = line; | |||
| e.file = file; | |||
| e.message = [file + ":" + line, context.join("\n"), "", e.message].join("\n"); | |||
| return e; | |||
| } | |||
| /** | |||
| * Looks up the value of the given `name` in the given context `stack`. | |||
| */ | |||
| function lookup(name, stack, defaultValue) { | |||
| if (name === ".") { | |||
| return stack[stack.length - 1]; | |||
| } | |||
| var names = name.split("."); | |||
| var lastIndex = names.length - 1; | |||
| var target = names[lastIndex]; | |||
| var value, context, i = stack.length, j, localStack; | |||
| while (i) { | |||
| localStack = stack.slice(0); | |||
| context = stack[--i]; | |||
| j = 0; | |||
| while (j < lastIndex) { | |||
| context = context[names[j++]]; | |||
| if (context == null) { | |||
| break; | |||
| } | |||
| localStack.push(context); | |||
| } | |||
| if (context && typeof context === "object" && target in context) { | |||
| value = context[target]; | |||
| break; | |||
| } | |||
| } | |||
| // If the value is a function, call it in the current context. | |||
| if (typeof value === "function") { | |||
| value = value.call(localStack[localStack.length - 1]); | |||
| } | |||
| if (value == null) { | |||
| return defaultValue; | |||
| } | |||
| return value; | |||
| } | |||
| function renderSection(name, stack, callback, inverted) { | |||
| var buffer = ""; | |||
| var value = lookup(name, stack); | |||
| 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(); | |||
| } | |||
| return buffer; | |||
| } | |||
| /** | |||
| * 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 += "' | |||
| ]; | |||
| 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? | |||
| // 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); | |||
| } | |||
| } else { | |||
| spaces = []; | |||
| } | |||
| hasTag = false; | |||
| nonSpace = false; | |||
| }; | |||
| var sectionStack = [], updateLine, nextOpenTag, nextCloseTag; | |||
| var setTags = function (source) { | |||
| tags = trim(source).split(/\s+/); | |||
| nextOpenTag = tags[0]; | |||
| nextCloseTag = tags[tags.length - 1]; | |||
| }; | |||
| 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 += "' | |||
| ); | |||
| }; | |||
| var openSection = function (source, inverted) { | |||
| var name = trim(source); | |||
| if (name === "") { | |||
| throw debug(new Error("Section name may not be empty"), template, line, options.file); | |||
| } | |||
| sectionStack.push({name: name, inverted: inverted}); | |||
| code.push( | |||
| '";', | |||
| updateLine, | |||
| '\nvar name = "' + name + '";', | |||
| '\nvar callback = (function () {', | |||
| '\n return function () {', | |||
| '\n var buffer = "";', | |||
| '\nbuffer += "' | |||
| ); | |||
| }; | |||
| var openInvertedSection = function (source) { | |||
| openSection(source, true); | |||
| }; | |||
| var closeSection = function (source) { | |||
| var name = trim(source); | |||
| var openName = sectionStack.length != 0 && sectionStack[sectionStack.length - 1].name; | |||
| if (!openName || name != openName) { | |||
| throw debug(new Error('Section named "' + name + '" was never opened'), template, line, options.file); | |||
| } | |||
| var section = sectionStack.pop(); | |||
| code.push( | |||
| '";', | |||
| '\n return buffer;', | |||
| '\n };', | |||
| '\n})();' | |||
| ); | |||
| if (section.inverted) { | |||
| code.push("\nbuffer += renderSection(name,stack,callback,true);"); | |||
| } else { | |||
| code.push("\nbuffer += renderSection(name,stack,callback);"); | |||
| } | |||
| code.push('\nbuffer += "'); | |||
| }; | |||
| var sendPlain = function (source) { | |||
| code.push( | |||
| '";', | |||
| updateLine, | |||
| '\nbuffer += lookup("' + trim(source) + '",stack,"");', | |||
| '\nbuffer += "' | |||
| ); | |||
| }; | |||
| var sendEscaped = function (source) { | |||
| code.push( | |||
| '";', | |||
| updateLine, | |||
| '\nbuffer += escapeHTML(lookup("' + trim(source) + '",stack,""));', | |||
| '\nbuffer += "' | |||
| ); | |||
| }; | |||
| 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; | |||
| break; | |||
| case "=": // change open/close tags, e.g. {{=<% %>=}} | |||
| i++; | |||
| closeTag = "=" + closeTag; | |||
| callback = setTags; | |||
| break; | |||
| case ">": // include partial | |||
| i++; | |||
| callback = includePartial; | |||
| break; | |||
| case "#": // start section | |||
| i++; | |||
| callback = openSection; | |||
| break; | |||
| case "^": // start inverted section | |||
| i++; | |||
| callback = openInvertedSection; | |||
| break; | |||
| case "/": // end section | |||
| i++; | |||
| callback = closeSection; | |||
| 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); | |||
| if (end === -1) { | |||
| throw debug(new Error('Tag "' + openTag + '" was not closed properly'), template, line, options.file); | |||
| } | |||
| var source = template.substring(i, end); | |||
| if (callback) { | |||
| callback(source); | |||
| } | |||
| // Maintain line count for \n in source. | |||
| var n = 0; | |||
| while (~(n = source.indexOf("\n", n))) { | |||
| line++; | |||
| n++; | |||
| } | |||
| i = end + closeTag.length - 1; | |||
| openTag = nextOpenTag; | |||
| closeTag = nextCloseTag; | |||
| } else { | |||
| c = template.substr(i, 1); | |||
| 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); | |||
| } else { | |||
| nonSpace = true; | |||
| } | |||
| code.push(c); | |||
| } | |||
| } | |||
| } | |||
| if (sectionStack.length != 0) { | |||
| throw debug(new Error('Section "' + sectionStack[sectionStack.length - 1].name + '" was not closed properly'), template, line, options.file); | |||
| } | |||
| // Clean up any whitespace from a closing {{tag}} that was at the end | |||
| // of the template without a trailing \n. | |||
| stripSpace(); | |||
| code.push( | |||
| '";', | |||
| "\nreturn buffer;", | |||
| "\n} catch (e) { throw {error: e, line: line}; }" | |||
| ); | |||
| // Ignore `buffer += "";` statements. | |||
| var body = code.join("").replace(/buffer \+= "";\n/g, ""); | |||
| if (options.debug) { | |||
| if (typeof console != "undefined" && console.log) { | |||
| console.log(body); | |||
| } else if (typeof print === "function") { | |||
| print(body); | |||
| } | |||
| } | |||
| return body; | |||
| } | |||
| /** | |||
| * Used by `compile` to generate a reusable function for the given `template`. | |||
| */ | |||
| 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. | |||
| return 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); | |||
| } | |||
| }; | |||
| } | |||
| // Cache of pre-compiled templates. | |||
| var _cache = {}; | |||
| /** | |||
| * Clear the cache of compiled templates. | |||
| */ | |||
| function clearCache() { | |||
| _cache = {}; | |||
| } | |||
| /** | |||
| * 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 | |||
| */ | |||
| function compile(template, options) { | |||
| options = options || {}; | |||
| // Use a pre-compiled version from the cache if we have one. | |||
| if (options.cache !== false) { | |||
| if (!_cache[template]) { | |||
| _cache[template] = _compile(template, options); | |||
| } | |||
| return _cache[template]; | |||
| } | |||
| 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); | |||
| } | |||
| })(Mustache); | |||
| @@ -0,0 +1,8 @@ | |||
| { | |||
| "name": "mustache", | |||
| "version": "0.5.21", | |||
| "description": "Logic-less {{mustache}} templates with JavaScript", | |||
| "author": "mustache.js Authors <http://github.com/janl/mustache.js>", | |||
| "keywords": ["mustache", "template", "templates", "ejs"], | |||
| "main": "./mustache" | |||
| } | |||