Comparing version 0.4.0 to 0.5.1-dev
871
mustache.js
@@ -1,45 +0,83 @@ | ||
/* | ||
* CommonJS-compatible mustache.js module | ||
* | ||
* See http://github.com/janl/mustache.js for more info. | ||
/*! | ||
* mustache.js - Logic-less {{mustache}} templates with JavaScript | ||
* http://github.com/janl/mustache.js | ||
*/ | ||
/* | ||
mustache.js — Logic-less templates in JavaScript | ||
/*global define: false*/ | ||
See http://mustache.github.com/ for more info. | ||
*/ | ||
var Mustache; | ||
var Mustache = function () { | ||
var _toString = Object.prototype.toString; | ||
Array.isArray = Array.isArray || function (obj) { | ||
return _toString.call(obj) == "[object Array]"; | ||
(function (exports) { | ||
if (typeof module !== "undefined") { | ||
module.exports = exports; // CommonJS | ||
} else if (typeof define === "function") { | ||
define(exports); // AMD | ||
} else { | ||
Mustache = exports; // <script> | ||
} | ||
}((function () { | ||
var exports = {}; | ||
var _trim = String.prototype.trim, trim; | ||
exports.name = "mustache.js"; | ||
exports.version = "0.5.2"; | ||
exports.tags = ["{{", "}}"]; | ||
if (_trim) { | ||
trim = function (text) { | ||
return text == null ? "" : _trim.call(text); | ||
} | ||
} else { | ||
var trimLeft, trimRight; | ||
exports.parse = parse; | ||
exports.clearCache = clearCache; | ||
exports.compile = compile; | ||
exports.compilePartial = compilePartial; | ||
exports.render = render; | ||
// IE doesn't match non-breaking spaces with \s. | ||
if ((/\S/).test("\xA0")) { | ||
trimLeft = /^[\s\xA0]+/; | ||
trimRight = /[\s\xA0]+$/; | ||
exports.Scanner = Scanner; | ||
exports.Context = Context; | ||
exports.Renderer = Renderer; | ||
// 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 { | ||
trimLeft = /^\s+/; | ||
trimRight = /\s+$/; | ||
return result; | ||
} | ||
}; | ||
trim = function (text) { | ||
return text == null ? "" : | ||
text.toString().replace(trimLeft, "").replace(trimRight, ""); | ||
} | ||
var whiteRe = /\s*/; | ||
var spaceRe = /\s+/; | ||
var nonSpaceRe = /\S/; | ||
var eqRe = /\s*=/; | ||
var curlyRe = /\s*\}/; | ||
var tagRe = /#|\^|\/|>|\{|&|=|!/; | ||
// Workaround for https://issues.apache.org/jira/browse/COUCHDB-577 | ||
// See https://github.com/janl/mustache.js/issues/189 | ||
function testRe(re, string) { | ||
return RegExp.prototype.test.call(re, string); | ||
} | ||
var escapeMap = { | ||
function isWhitespace(string) { | ||
return !testRe(nonSpaceRe, string); | ||
} | ||
var isArray = Array.isArray || function (obj) { | ||
return Object.prototype.toString.call(obj) === "[object Array]"; | ||
}; | ||
// OSWASP Guidelines: escape all non alphanumeric characters in ASCII space. | ||
var jsCharsRe = /[\x00-\x2F\x3A-\x40\x5B-\x60\x7B-\xFF\u2028\u2029]/gm; | ||
function quote(text) { | ||
var escaped = text.replace(jsCharsRe, function (c) { | ||
return "\\u" + ('0000' + c.charCodeAt(0).toString(16)).slice(-4); | ||
}); | ||
return '"' + escaped + '"'; | ||
} | ||
function escapeRe(string) { | ||
return string.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&"); | ||
} | ||
var entityMap = { | ||
"&": "&", | ||
@@ -49,403 +87,528 @@ "<": "<", | ||
'"': '"', | ||
"'": ''' | ||
"'": ''', | ||
"/": '/' | ||
}; | ||
function escapeHTML(string) { | ||
return String(string).replace(/&(?!\w+;)|[<>"']/g, function (s) { | ||
return escapeMap[s] || s; | ||
function escapeHtml(string) { | ||
return String(string).replace(/[&<>"'\/]/g, function (s) { | ||
return entityMap[s]; | ||
}); | ||
} | ||
var regexCache = {}; | ||
var Renderer = function () {}; | ||
// Export these utility functions. | ||
exports.isWhitespace = isWhitespace; | ||
exports.isArray = isArray; | ||
exports.quote = quote; | ||
exports.escapeRe = escapeRe; | ||
exports.escapeHtml = escapeHtml; | ||
Renderer.prototype = { | ||
otag: "{{", | ||
ctag: "}}", | ||
pragmas: {}, | ||
buffer: [], | ||
pragmas_implemented: { | ||
"IMPLICIT-ITERATOR": true | ||
}, | ||
context: {}, | ||
function Scanner(string) { | ||
this.string = string; | ||
this.tail = string; | ||
this.pos = 0; | ||
} | ||
render: function (template, context, partials, in_recursion) { | ||
// reset buffer & set context | ||
if (!in_recursion) { | ||
this.context = context; | ||
this.buffer = []; // TODO: make this non-lazy | ||
} | ||
/** | ||
* Returns `true` if the tail is empty (end of string). | ||
*/ | ||
Scanner.prototype.eos = function () { | ||
return this.tail === ""; | ||
}; | ||
// fail fast | ||
if (!this.includes("", template)) { | ||
if (in_recursion) { | ||
return template; | ||
} else { | ||
this.send(template); | ||
return; | ||
} | ||
} | ||
/** | ||
* Tries to match the given regular expression at the current position. | ||
* Returns the matched text if it can match, the empty string otherwise. | ||
*/ | ||
Scanner.prototype.scan = function (re) { | ||
var match = this.tail.match(re); | ||
// get the pragmas together | ||
template = this.render_pragmas(template); | ||
if (match && match.index === 0) { | ||
this.tail = this.tail.substring(match[0].length); | ||
this.pos += match[0].length; | ||
return match[0]; | ||
} | ||
// render the template | ||
var html = this.render_section(template, context, partials); | ||
return ""; | ||
}; | ||
// render_section did not find any sections, we still need to render the tags | ||
if (html === false) { | ||
html = this.render_tags(template, context, partials, in_recursion); | ||
} | ||
/** | ||
* Skips all text until the given regular expression can be matched. Returns | ||
* the skipped string, which is the entire tail if no match can be made. | ||
*/ | ||
Scanner.prototype.scanUntil = function (re) { | ||
var match, pos = this.tail.search(re); | ||
if (in_recursion) { | ||
return html; | ||
switch (pos) { | ||
case -1: | ||
match = this.tail; | ||
this.pos += this.tail.length; | ||
this.tail = ""; | ||
break; | ||
case 0: | ||
match = ""; | ||
break; | ||
default: | ||
match = this.tail.substring(0, pos); | ||
this.tail = this.tail.substring(pos); | ||
this.pos += pos; | ||
} | ||
return match; | ||
}; | ||
function Context(view, parent) { | ||
this.view = view; | ||
this.parent = parent; | ||
this.clearCache(); | ||
} | ||
Context.make = function (view) { | ||
return (view instanceof Context) ? view : new Context(view); | ||
}; | ||
Context.prototype.clearCache = function () { | ||
this._cache = {}; | ||
}; | ||
Context.prototype.push = function (view) { | ||
return new Context(view, this); | ||
}; | ||
Context.prototype.lookup = function (name) { | ||
var value = this._cache[name]; | ||
if (!value) { | ||
if (name === ".") { | ||
value = this.view; | ||
} else { | ||
this.sendLines(html); | ||
} | ||
}, | ||
var context = this; | ||
/* | ||
Sends parsed lines | ||
*/ | ||
send: function (line) { | ||
if (line !== "") { | ||
this.buffer.push(line); | ||
} | ||
}, | ||
while (context) { | ||
if (name.indexOf(".") > 0) { | ||
var names = name.split("."), i = 0; | ||
sendLines: function (text) { | ||
if (text) { | ||
var lines = text.split("\n"); | ||
for (var i = 0; i < lines.length; i++) { | ||
this.send(lines[i]); | ||
value = context.view; | ||
while (value && i < names.length) { | ||
value = value[names[i++]]; | ||
} | ||
} else { | ||
value = context.view[name]; | ||
} | ||
if (value != null) { | ||
break; | ||
} | ||
context = context.parent; | ||
} | ||
} | ||
}, | ||
/* | ||
Looks for %PRAGMAS | ||
*/ | ||
render_pragmas: function (template) { | ||
// no pragmas | ||
if (!this.includes("%", template)) { | ||
return template; | ||
} | ||
this._cache[name] = value; | ||
} | ||
var that = this; | ||
var regex = this.getCachedRegex("render_pragmas", function (otag, ctag) { | ||
return new RegExp(otag + "%([\\w-]+) ?([\\w]+=[\\w]+)?" + ctag, "g"); | ||
}); | ||
if (typeof value === "function") { | ||
value = value.call(this.view); | ||
} | ||
return template.replace(regex, function (match, pragma, options) { | ||
if (!that.pragmas_implemented[pragma]) { | ||
throw({message: | ||
"This implementation of mustache doesn't understand the '" + | ||
pragma + "' pragma"}); | ||
return value; | ||
}; | ||
function Renderer() { | ||
this.clearCache(); | ||
} | ||
Renderer.prototype.clearCache = function () { | ||
this._cache = {}; | ||
this._partialCache = {}; | ||
}; | ||
Renderer.prototype.compile = function (tokens, tags) { | ||
if (typeof tokens === "string") { | ||
tokens = parse(tokens, tags); | ||
} | ||
var fn = compileTokens(tokens), | ||
self = this; | ||
return function (view) { | ||
return fn(Context.make(view), self); | ||
}; | ||
}; | ||
Renderer.prototype.compilePartial = function (name, tokens, tags) { | ||
this._partialCache[name] = this.compile(tokens, tags); | ||
return this._partialCache[name]; | ||
}; | ||
Renderer.prototype.render = function (template, view) { | ||
var fn = this._cache[template]; | ||
if (!fn) { | ||
fn = this.compile(template); | ||
this._cache[template] = fn; | ||
} | ||
return fn(view); | ||
}; | ||
Renderer.prototype._section = function (name, context, callback) { | ||
var value = context.lookup(name); | ||
switch (typeof value) { | ||
case "object": | ||
if (isArray(value)) { | ||
var buffer = ""; | ||
for (var i = 0, len = value.length; i < len; ++i) { | ||
buffer += callback(context.push(value[i]), this); | ||
} | ||
that.pragmas[pragma] = {}; | ||
if (options) { | ||
var opts = options.split("="); | ||
that.pragmas[pragma][opts[0]] = opts[1]; | ||
} | ||
return ""; | ||
// ignore unknown pragmas silently | ||
}); | ||
}, | ||
/* | ||
Tries to find a partial in the curent scope and render it | ||
*/ | ||
render_partial: function (name, context, partials) { | ||
name = trim(name); | ||
if (!partials || partials[name] === undefined) { | ||
throw({message: "unknown_partial '" + name + "'"}); | ||
return buffer; | ||
} | ||
if (!context || typeof context[name] != "object") { | ||
return this.render(partials[name], context, partials, true); | ||
} | ||
return this.render(partials[name], context[name], partials, true); | ||
}, | ||
/* | ||
Renders inverted (^) and normal (#) sections | ||
*/ | ||
render_section: function (template, context, partials) { | ||
if (!this.includes("#", template) && !this.includes("^", template)) { | ||
// did not render anything, there were no sections | ||
return false; | ||
return value ? callback(context.push(value), this) : ""; | ||
case "function": | ||
// TODO: The text should be passed to the callback plain, not rendered. | ||
var sectionText = callback(context, this), | ||
self = this; | ||
var scopedRender = function (template) { | ||
return self.render(template, context); | ||
}; | ||
return value.call(context.view, sectionText, scopedRender) || ""; | ||
default: | ||
if (value) { | ||
return callback(context, this); | ||
} | ||
} | ||
var that = this; | ||
return ""; | ||
}; | ||
var regex = this.getCachedRegex("render_section", function (otag, ctag) { | ||
// This regex matches _the first_ section ({{#foo}}{{/foo}}), and captures the remainder | ||
return new RegExp( | ||
"^([\\s\\S]*?)" + // all the crap at the beginning that is not {{*}} ($1) | ||
Renderer.prototype._inverted = function (name, context, callback) { | ||
var value = context.lookup(name); | ||
otag + // {{ | ||
"(\\^|\\#)\\s*(.+)\\s*" + // #foo (# == $2, foo == $3) | ||
ctag + // }} | ||
// 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)) { | ||
return callback(context, this); | ||
} | ||
"\n*([\\s\\S]*?)" + // between the tag ($2). leading newlines are dropped | ||
return ""; | ||
}; | ||
otag + // {{ | ||
"\\/\\s*\\3\\s*" + // /foo (backreference to the opening tag). | ||
ctag + // }} | ||
Renderer.prototype._partial = function (name, context) { | ||
var fn = this._partialCache[name]; | ||
"\\s*([\\s\\S]*)$", // everything else in the string ($4). leading whitespace is dropped. | ||
if (fn) { | ||
return fn(context); | ||
} | ||
"g"); | ||
}); | ||
return ""; | ||
}; | ||
Renderer.prototype._name = function (name, context, escape) { | ||
var value = context.lookup(name); | ||
// for each {{#foo}}{{/foo}} section do... | ||
return template.replace(regex, function (match, before, type, name, content, after) { | ||
// before contains only tags, no sections | ||
var renderedBefore = before ? that.render_tags(before, context, partials, true) : "", | ||
if (typeof value === "function") { | ||
value = value.call(context.view); | ||
} | ||
// after may contain both sections and tags, so use full rendering function | ||
renderedAfter = after ? that.render(after, context, partials, true) : "", | ||
var string = (value == null) ? "" : String(value); | ||
// will be computed below | ||
renderedContent, | ||
if (escape) { | ||
return escapeHtml(string); | ||
} | ||
value = that.find(name, context); | ||
return string; | ||
}; | ||
if (type === "^") { // inverted section | ||
if (!value || Array.isArray(value) && value.length === 0) { | ||
// false or empty list, render it | ||
renderedContent = that.render(content, context, partials, true); | ||
} else { | ||
renderedContent = ""; | ||
} | ||
} else if (type === "#") { // normal section | ||
if (Array.isArray(value)) { // Enumerable, Let's loop! | ||
renderedContent = that.map(value, function (row) { | ||
return that.render(content, that.create_context(row), partials, true); | ||
}).join(""); | ||
} else if (that.is_object(value)) { // Object, Use it as subcontext! | ||
renderedContent = that.render(content, that.create_context(value), | ||
partials, true); | ||
} else if (typeof value == "function") { | ||
// higher order section | ||
renderedContent = value.call(context, content, function (text) { | ||
return that.render(text, context, partials, true); | ||
}); | ||
} else if (value) { // boolean section | ||
renderedContent = that.render(content, context, partials, true); | ||
} else { | ||
renderedContent = ""; | ||
} | ||
} | ||
/** | ||
* Low-level function that compiles the given `tokens` into a | ||
* function that accepts two arguments: a Context and a | ||
* Renderer. Returns the body of the function as a string if | ||
* `returnBody` is true. | ||
*/ | ||
function compileTokens(tokens, returnBody) { | ||
var body = ['""']; | ||
var token, method, escape; | ||
return renderedBefore + renderedContent + renderedAfter; | ||
}); | ||
}, | ||
for (var i = 0, len = tokens.length; i < len; ++i) { | ||
token = tokens[i]; | ||
/* | ||
Replace {{foo}} and friends with values from our view | ||
*/ | ||
render_tags: function (template, context, partials, in_recursion) { | ||
// tit for tat | ||
var that = this; | ||
switch (token.type) { | ||
case "#": | ||
case "^": | ||
method = (token.type === "#") ? "_section" : "_inverted"; | ||
body.push("r." + method + "(" + quote(token.value) + ", c, function (c, r) {\n" + | ||
" " + compileTokens(token.tokens, true) + "\n" + | ||
"})"); | ||
break; | ||
case "{": | ||
case "&": | ||
case "name": | ||
escape = token.type === "name" ? "true" : "false"; | ||
body.push("r._name(" + quote(token.value) + ", c, " + escape + ")"); | ||
break; | ||
case ">": | ||
body.push("r._partial(" + quote(token.value) + ", c)"); | ||
break; | ||
case "text": | ||
body.push(quote(token.value)); | ||
break; | ||
} | ||
} | ||
var new_regex = function () { | ||
return that.getCachedRegex("render_tags", function (otag, ctag) { | ||
return new RegExp(otag + "(=|!|>|&|\\{|%)?([^#\\^]+?)\\1?" + ctag + "+", "g"); | ||
}); | ||
}; | ||
// Convert to a string body. | ||
body = "return " + body.join(" + ") + ";"; | ||
var regex = new_regex(); | ||
var tag_replace_callback = function (match, operator, name) { | ||
switch(operator) { | ||
case "!": // ignore comments | ||
return ""; | ||
case "=": // set new delimiters, rebuild the replace regexp | ||
that.set_delimiters(name); | ||
regex = new_regex(); | ||
return ""; | ||
case ">": // render partial | ||
return that.render_partial(name, context, partials); | ||
case "{": // the triple mustache is unescaped | ||
case "&": // & operator is an alternative unescape method | ||
return that.find(name, context); | ||
default: // escape the value | ||
return escapeHTML(that.find(name, context)); | ||
// Good for debugging. | ||
// console.log(body); | ||
if (returnBody) { | ||
return body; | ||
} | ||
// For great evil! | ||
return new Function("c, r", body); | ||
} | ||
function escapeTags(tags) { | ||
if (tags.length === 2) { | ||
return [ | ||
new RegExp(escapeRe(tags[0]) + "\\s*"), | ||
new RegExp("\\s*" + escapeRe(tags[1])) | ||
]; | ||
} | ||
throw new Error("Invalid tags: " + tags.join(" ")); | ||
} | ||
/** | ||
* Forms the given linear array of `tokens` into a nested tree structure | ||
* where tokens that represent a section have a "tokens" array property | ||
* that contains all tokens that are in that section. | ||
*/ | ||
function nestTokens(tokens) { | ||
var tree = []; | ||
var collector = tree; | ||
var sections = []; | ||
var token, section; | ||
for (var i = 0; i < tokens.length; ++i) { | ||
token = tokens[i]; | ||
switch (token.type) { | ||
case "#": | ||
case "^": | ||
token.tokens = []; | ||
sections.push(token); | ||
collector.push(token); | ||
collector = token.tokens; | ||
break; | ||
case "/": | ||
if (sections.length === 0) { | ||
throw new Error("Unopened section: " + token.value); | ||
} | ||
}; | ||
var lines = template.split("\n"); | ||
for(var i = 0; i < lines.length; i++) { | ||
lines[i] = lines[i].replace(regex, tag_replace_callback, this); | ||
if (!in_recursion) { | ||
this.send(lines[i]); | ||
section = sections.pop(); | ||
if (section.value !== token.value) { | ||
throw new Error("Unclosed section: " + section.value); | ||
} | ||
} | ||
if (in_recursion) { | ||
return lines.join("\n"); | ||
if (sections.length > 0) { | ||
collector = sections[sections.length - 1].tokens; | ||
} else { | ||
collector = tree; | ||
} | ||
break; | ||
default: | ||
collector.push(token); | ||
} | ||
}, | ||
} | ||
set_delimiters: function (delimiters) { | ||
var dels = delimiters.split(" "); | ||
this.otag = this.escape_regex(dels[0]); | ||
this.ctag = this.escape_regex(dels[1]); | ||
}, | ||
// Make sure there were no open sections when we're done. | ||
section = sections.pop(); | ||
escape_regex: function (text) { | ||
// thank you Simon Willison | ||
if (!arguments.callee.sRE) { | ||
var specials = [ | ||
'/', '.', '*', '+', '?', '|', | ||
'(', ')', '[', ']', '{', '}', '\\' | ||
]; | ||
arguments.callee.sRE = new RegExp( | ||
'(\\' + specials.join('|\\') + ')', 'g' | ||
); | ||
} | ||
return text.replace(arguments.callee.sRE, '\\$1'); | ||
}, | ||
if (section) { | ||
throw new Error("Unclosed section: " + section.value); | ||
} | ||
/* | ||
find `name` in current `context`. That is find me a value | ||
from the view object | ||
*/ | ||
find: function (name, context) { | ||
name = trim(name); | ||
return tree; | ||
} | ||
// Checks whether a value is thruthy or false or 0 | ||
function is_kinda_truthy(bool) { | ||
return bool === false || bool === 0 || bool; | ||
/** | ||
* Combines the values of consecutive text tokens in the given `tokens` array | ||
* to a single token. | ||
*/ | ||
function squashTokens(tokens) { | ||
var lastToken; | ||
for (var i = 0; i < tokens.length; ++i) { | ||
var token = tokens[i]; | ||
if (lastToken && lastToken.type === "text" && token.type === "text") { | ||
lastToken.value += token.value; | ||
tokens.splice(i--, 1); // Remove this token from the array. | ||
} else { | ||
lastToken = token; | ||
} | ||
} | ||
} | ||
var value; | ||
/** | ||
* Breaks up the given `template` string into a tree of token objects. If | ||
* `tags` is given here it must be an array with two string values: the | ||
* opening and closing tags used in the template (e.g. ["<%", "%>"]). Of | ||
* course, the default is to use mustaches (i.e. Mustache.tags). | ||
*/ | ||
function parse(template, tags) { | ||
tags = tags || exports.tags; | ||
// check for dot notation eg. foo.bar | ||
if (name.match(/([a-z_]+)\./ig)) { | ||
var childValue = this.walk_context(name, context); | ||
if (is_kinda_truthy(childValue)) { | ||
value = childValue; | ||
var tagRes = escapeTags(tags); | ||
var scanner = new Scanner(template); | ||
var tokens = [], // Buffer to hold the tokens | ||
spaces = [], // Indices of whitespace tokens on the current line | ||
hasTag = false, // Is there a {{tag}} on the current line? | ||
nonSpace = false; // Is there a non-space char on the current line? | ||
// Strips all whitespace tokens array for the current line | ||
// if there was a {{#tag}} on it and otherwise only space. | ||
var stripSpace = function () { | ||
if (hasTag && !nonSpace) { | ||
while (spaces.length) { | ||
tokens.splice(spaces.pop(), 1); | ||
} | ||
} else { | ||
if (is_kinda_truthy(context[name])) { | ||
value = context[name]; | ||
} else if (is_kinda_truthy(this.context[name])) { | ||
value = this.context[name]; | ||
} | ||
spaces = []; | ||
} | ||
if (typeof value == "function") { | ||
return value.apply(context); | ||
} | ||
if (value !== undefined) { | ||
return value; | ||
} | ||
// silently ignore unkown variables | ||
return ""; | ||
}, | ||
hasTag = false; | ||
nonSpace = false; | ||
}; | ||
walk_context: function (name, context) { | ||
var path = name.split('.'); | ||
// if the var doesn't exist in current context, check the top level context | ||
var value_context = (context[path[0]] != undefined) ? context : this.context; | ||
var value = value_context[path.shift()]; | ||
while (value != undefined && path.length > 0) { | ||
value_context = value; | ||
value = value[path.shift()]; | ||
} | ||
// if the value is a function, call it, binding the correct context | ||
if (typeof value == "function") { | ||
return value.apply(value_context); | ||
} | ||
return value; | ||
}, | ||
var type, value, chr; | ||
// Utility methods | ||
while (!scanner.eos()) { | ||
value = scanner.scanUntil(tagRes[0]); | ||
/* includes tag */ | ||
includes: function (needle, haystack) { | ||
return haystack.indexOf(this.otag + needle) != -1; | ||
}, | ||
if (value) { | ||
for (var i = 0, len = value.length; i < len; ++i) { | ||
chr = value.charAt(i); | ||
// by @langalex, support for arrays of strings | ||
create_context: function (_context) { | ||
if (this.is_object(_context)) { | ||
return _context; | ||
} else { | ||
var iterator = "."; | ||
if (this.pragmas["IMPLICIT-ITERATOR"]) { | ||
iterator = this.pragmas["IMPLICIT-ITERATOR"].iterator; | ||
if (isWhitespace(chr)) { | ||
spaces.push(tokens.length); | ||
} else { | ||
nonSpace = true; | ||
} | ||
tokens.push({type: "text", value: chr}); | ||
if (chr === "\n") { | ||
stripSpace(); // Check for whitespace on the current line. | ||
} | ||
} | ||
var ctx = {}; | ||
ctx[iterator] = _context; | ||
return ctx; | ||
} | ||
}, | ||
is_object: function (a) { | ||
return a && typeof a == "object"; | ||
}, | ||
// Match the opening tag. | ||
if (!scanner.scan(tagRes[0])) { | ||
break; | ||
} | ||
/* | ||
Why, why, why? Because IE. Cry, cry cry. | ||
*/ | ||
map: function (array, fn) { | ||
if (typeof array.map == "function") { | ||
return array.map(fn); | ||
hasTag = true; | ||
type = scanner.scan(tagRe) || "name"; | ||
// Skip any whitespace between tag and value. | ||
scanner.scan(whiteRe); | ||
// Extract the tag value. | ||
if (type === "=") { | ||
value = scanner.scanUntil(eqRe); | ||
scanner.scan(eqRe); | ||
scanner.scanUntil(tagRes[1]); | ||
} else if (type === "{") { | ||
var closeRe = new RegExp("\\s*" + escapeRe("}" + tags[1])); | ||
value = scanner.scanUntil(closeRe); | ||
scanner.scan(curlyRe); | ||
scanner.scanUntil(tagRes[1]); | ||
} else { | ||
var r = []; | ||
var l = array.length; | ||
for(var i = 0; i < l; i++) { | ||
r.push(fn(array[i])); | ||
} | ||
return r; | ||
value = scanner.scanUntil(tagRes[1]); | ||
} | ||
}, | ||
getCachedRegex: function (name, generator) { | ||
var byOtag = regexCache[this.otag]; | ||
if (!byOtag) { | ||
byOtag = regexCache[this.otag] = {}; | ||
// Match the closing tag. | ||
if (!scanner.scan(tagRes[1])) { | ||
throw new Error("Unclosed tag at " + scanner.pos); | ||
} | ||
var byCtag = byOtag[this.ctag]; | ||
if (!byCtag) { | ||
byCtag = byOtag[this.ctag] = {}; | ||
tokens.push({type: type, value: value}); | ||
if (type === "name" || type === "{" || type === "&") { | ||
nonSpace = true; | ||
} | ||
var regex = byCtag[name]; | ||
if (!regex) { | ||
regex = byCtag[name] = generator(this.otag, this.ctag); | ||
// Set the tags for the next time around. | ||
if (type === "=") { | ||
tags = value.split(spaceRe); | ||
tagRes = escapeTags(tags); | ||
} | ||
return regex; | ||
} | ||
}; | ||
return({ | ||
name: "mustache.js", | ||
version: "0.4.0", | ||
squashTokens(tokens); | ||
/* | ||
Turns a template and view into HTML | ||
*/ | ||
to_html: function (template, view, partials, send_fun) { | ||
var renderer = new Renderer(); | ||
if (send_fun) { | ||
renderer.send = send_fun; | ||
return nestTokens(tokens); | ||
} | ||
// The high-level clearCache, compile, compilePartial, and render functions | ||
// use this default renderer. | ||
var _renderer = new Renderer(); | ||
/** | ||
* Clears all cached templates and partials. | ||
*/ | ||
function clearCache() { | ||
_renderer.clearCache(); | ||
} | ||
/** | ||
* High-level API for compiling the given `tokens` down to a reusable | ||
* function. If `tokens` is a string it will be parsed using the given `tags` | ||
* before it is compiled. | ||
*/ | ||
function compile(tokens, tags) { | ||
return _renderer.compile(tokens, tags); | ||
} | ||
/** | ||
* High-level API for compiling the `tokens` for the partial with the given | ||
* `name` down to a reusable function. If `tokens` is a string it will be | ||
* parsed using the given `tags` before it is compiled. | ||
*/ | ||
function compilePartial(name, tokens, tags) { | ||
return _renderer.compilePartial(name, tokens, tags); | ||
} | ||
/** | ||
* High-level API for rendering the `template` using the given `view`. The | ||
* optional `partials` object may be given here for convenience, but note that | ||
* it will cause all partials to be re-compiled, thus hurting performance. Of | ||
* course, this only matters if you're going to render the same template more | ||
* than once. If so, it is best to call `compilePartial` before calling this | ||
* function and to leave the `partials` argument blank. | ||
*/ | ||
function render(template, view, partials) { | ||
if (partials) { | ||
for (var name in partials) { | ||
compilePartial(name, partials[name]); | ||
} | ||
renderer.render(template, view || {}, partials); | ||
if (!send_fun) { | ||
return renderer.buffer.join("\n"); | ||
} | ||
} | ||
}); | ||
}(); | ||
if (typeof module !== 'undefined' && module.exports) { | ||
exports.name = Mustache.name; | ||
exports.version = Mustache.version; | ||
exports.to_html = function() { | ||
return Mustache.to_html.apply(this, arguments); | ||
}; | ||
} | ||
return _renderer.render(template, view); | ||
} | ||
return exports; | ||
}()))); |
{ | ||
"name": "mustache", | ||
"author": "Jan Lehnardt <jan@apache.org>", | ||
"description": "Logic-less {{mustache}} templates with JavaScript", | ||
"keywords": ["template", "templates", "mustache"], | ||
"version": "0.4.0", | ||
"main": "./mustache" | ||
"name": "mustache", | ||
"version": "0.5.1-dev", | ||
"description": "Logic-less {{mustache}} templates with JavaScript", | ||
"author": "mustache.js Authors <http://github.com/janl/mustache.js>", | ||
"keywords": ["mustache", "template", "templates", "ejs"], | ||
"main": "./mustache.js", | ||
"devDependencies": { | ||
"vows": "0.6.x", | ||
"uglify-js": "1.3.0" | ||
}, | ||
"scripts": { | ||
"test": "vows --spec" | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No README
QualityPackage does not have a README. This may indicate a failed publish or a low quality package.
Found 1 instance in 1 package
No License Found
License(Experimental) License information could not be found.
Found 1 instance in 1 package
59825
166
0
1056
0
424
2
4
3