Comparing version 0.7.2 to 0.7.3
449
mustache.js
@@ -10,20 +10,14 @@ /*! | ||
if (typeof exports === "object" && exports) { | ||
module.exports = factory; // CommonJS | ||
} else if (typeof define === "function" && define.amd) { | ||
define(factory); // AMD | ||
factory(exports); // CommonJS | ||
} else { | ||
root.Mustache = factory; // <script> | ||
var mustache = {}; | ||
factory(mustache); | ||
if (typeof define === "function" && define.amd) { | ||
define(mustache); // AMD | ||
} else { | ||
root.Mustache = mustache; // <script> | ||
} | ||
} | ||
}(this, (function () { | ||
}(this, function (mustache) { | ||
var exports = {}; | ||
exports.name = "mustache.js"; | ||
exports.version = "0.7.2"; | ||
exports.tags = ["{{", "}}"]; | ||
exports.Scanner = Scanner; | ||
exports.Context = Context; | ||
exports.Writer = Writer; | ||
var whiteRe = /\s*/; | ||
@@ -38,15 +32,21 @@ var spaceRe = /\s+/; | ||
// See https://github.com/janl/mustache.js/issues/189 | ||
function testRe(re, string) { | ||
return RegExp.prototype.test.call(re, string); | ||
var RegExp_test = RegExp.prototype.test; | ||
function testRegExp(re, string) { | ||
return RegExp_test.call(re, string); | ||
} | ||
function isWhitespace(string) { | ||
return !testRe(nonSpaceRe, string); | ||
return !testRegExp(nonSpaceRe, string); | ||
} | ||
var isArray = Array.isArray || function (obj) { | ||
return Object.prototype.toString.call(obj) === "[object Array]"; | ||
var Object_toString = Object.prototype.toString; | ||
var isArray = Array.isArray || function (object) { | ||
return Object_toString.call(object) === '[object Array]'; | ||
}; | ||
function escapeRe(string) { | ||
function isFunction(object) { | ||
return typeof object === 'function'; | ||
} | ||
function escapeRegExp(string) { | ||
return string.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&"); | ||
@@ -70,6 +70,2 @@ } | ||
// Export the escaping function so that the user may override it. | ||
// See https://github.com/janl/mustache.js/issues/244 | ||
exports.escape = escapeHtml; | ||
function Scanner(string) { | ||
@@ -96,5 +92,6 @@ this.string = string; | ||
if (match && match.index === 0) { | ||
this.tail = this.tail.substring(match[0].length); | ||
this.pos += match[0].length; | ||
return match[0]; | ||
var string = match[0]; | ||
this.tail = this.tail.substring(string.length); | ||
this.pos += string.length; | ||
return string; | ||
} | ||
@@ -110,8 +107,7 @@ | ||
Scanner.prototype.scanUntil = function (re) { | ||
var match, pos = this.tail.search(re); | ||
var index = this.tail.search(re), match; | ||
switch (pos) { | ||
switch (index) { | ||
case -1: | ||
match = this.tail; | ||
this.pos += this.tail.length; | ||
this.tail = ""; | ||
@@ -123,7 +119,8 @@ break; | ||
default: | ||
match = this.tail.substring(0, pos); | ||
this.tail = this.tail.substring(pos); | ||
this.pos += pos; | ||
match = this.tail.substring(0, index); | ||
this.tail = this.tail.substring(index); | ||
} | ||
this.pos += match.length; | ||
return match; | ||
@@ -133,5 +130,5 @@ }; | ||
function Context(view, parent) { | ||
this.view = view; | ||
this.view = view == null ? {} : view; | ||
this.parent = parent; | ||
this.clearCache(); | ||
this._cache = { '.': this.view }; | ||
} | ||
@@ -143,6 +140,2 @@ | ||
Context.prototype.clearCache = function () { | ||
this._cache = {}; | ||
}; | ||
Context.prototype.push = function (view) { | ||
@@ -153,29 +146,23 @@ return new Context(view, this); | ||
Context.prototype.lookup = function (name) { | ||
var value = this._cache[name]; | ||
var value; | ||
if (name in this._cache) { | ||
value = this._cache[name]; | ||
} else { | ||
var context = this; | ||
if (!value) { | ||
if (name === ".") { | ||
value = this.view; | ||
} else { | ||
var context = this; | ||
while (context) { | ||
if (name.indexOf('.') > 0) { | ||
value = context.view; | ||
while (context) { | ||
if (name.indexOf(".") > 0) { | ||
var names = name.split("."), i = 0; | ||
value = context.view; | ||
while (value && i < names.length) { | ||
value = value[names[i++]]; | ||
} | ||
} else { | ||
value = context.view[name]; | ||
var names = name.split('.'), i = 0; | ||
while (value != null && i < names.length) { | ||
value = value[names[i++]]; | ||
} | ||
} else { | ||
value = context.view[name]; | ||
} | ||
if (value != null) { | ||
break; | ||
} | ||
if (value != null) break; | ||
context = context.parent; | ||
} | ||
context = context.parent; | ||
} | ||
@@ -186,3 +173,3 @@ | ||
if (typeof value === "function") { | ||
if (isFunction(value)) { | ||
value = value.call(this.view); | ||
@@ -207,3 +194,3 @@ } | ||
if (!fn) { | ||
var tokens = exports.parse(template, tags); | ||
var tokens = mustache.parse(template, tags); | ||
fn = this._cache[template] = this.compileTokens(tokens, template); | ||
@@ -221,9 +208,15 @@ } | ||
Writer.prototype.getPartial = function (name) { | ||
if (!(name in this._partialCache) && this._loadPartial) { | ||
this.compilePartial(name, this._loadPartial(name)); | ||
} | ||
return this._partialCache[name]; | ||
}; | ||
Writer.prototype.compileTokens = function (tokens, template) { | ||
var fn = compileTokens(tokens); | ||
var self = this; | ||
return function (view, partials) { | ||
if (partials) { | ||
if (typeof partials === "function") { | ||
if (isFunction(partials)) { | ||
self._loadPartial = partials; | ||
@@ -237,3 +230,3 @@ } else { | ||
return fn(self, Context.make(view), template); | ||
return renderTokens(tokens, self, Context.make(view), template); | ||
}; | ||
@@ -246,121 +239,72 @@ }; | ||
Writer.prototype._section = function (name, context, text, callback) { | ||
var value = context.lookup(name); | ||
/** | ||
* Low-level function that renders the given `tokens` using the given `writer` | ||
* and `context`. The `template` string is only needed for templates that use | ||
* higher-order sections to extract the portion of the original template that | ||
* was contained in that section. | ||
*/ | ||
function renderTokens(tokens, writer, context, template) { | ||
var buffer = ''; | ||
switch (typeof value) { | ||
case "object": | ||
if (isArray(value)) { | ||
var buffer = ""; | ||
for (var i = 0, len = value.length; i < len; ++i) { | ||
buffer += callback(this, context.push(value[i])); | ||
} | ||
return buffer; | ||
} | ||
return value ? callback(this, context.push(value)) : ""; | ||
case "function": | ||
var self = this; | ||
var scopedRender = function (template) { | ||
return self.render(template, context); | ||
}; | ||
var result = value.call(context.view, text, scopedRender); | ||
return result != null ? result : ""; | ||
default: | ||
if (value) { | ||
return callback(this, context); | ||
} | ||
// This function is used to render an artbitrary template | ||
// in the current context by higher-order functions. | ||
function subRender(template) { | ||
return writer.render(template, context); | ||
} | ||
return ""; | ||
}; | ||
var token, tokenValue, value; | ||
for (var i = 0, len = tokens.length; i < len; ++i) { | ||
token = tokens[i]; | ||
tokenValue = token[1]; | ||
Writer.prototype._inverted = function (name, context, callback) { | ||
var value = context.lookup(name); | ||
switch (token[0]) { | ||
case '#': | ||
value = context.lookup(tokenValue); | ||
// Use JavaScript's definition of falsy. Include empty arrays. | ||
// See https://github.com/janl/mustache.js/issues/186 | ||
if (!value || (isArray(value) && value.length === 0)) { | ||
return callback(this, context); | ||
} | ||
if (typeof value === 'object' || typeof value === 'string') { | ||
if (isArray(value)) { | ||
for (var j = 0, jlen = value.length; j < jlen; ++j) { | ||
buffer += renderTokens(token[4], writer, context.push(value[j]), template); | ||
} | ||
} else if (value) { | ||
buffer += renderTokens(token[4], writer, context.push(value), template); | ||
} | ||
} else if (isFunction(value)) { | ||
var text = template == null ? null : template.slice(token[3], token[5]); | ||
value = value.call(context.view, text, subRender); | ||
if (value != null) buffer += value; | ||
} else if (value) { | ||
buffer += renderTokens(token[4], writer, context, template); | ||
} | ||
return ""; | ||
}; | ||
break; | ||
case '^': | ||
value = context.lookup(tokenValue); | ||
Writer.prototype._partial = function (name, context) { | ||
if (!(name in this._partialCache) && this._loadPartial) { | ||
this.compilePartial(name, this._loadPartial(name)); | ||
} | ||
// Use JavaScript's definition of falsy. Include empty arrays. | ||
// See https://github.com/janl/mustache.js/issues/186 | ||
if (!value || (isArray(value) && value.length === 0)) { | ||
buffer += renderTokens(token[4], writer, context, template); | ||
} | ||
var fn = this._partialCache[name]; | ||
return fn ? fn(context) : ""; | ||
}; | ||
Writer.prototype._name = function (name, context) { | ||
var value = context.lookup(name); | ||
if (typeof value === "function") { | ||
value = value.call(context.view); | ||
} | ||
return (value == null) ? "" : String(value); | ||
}; | ||
Writer.prototype._escaped = function (name, context) { | ||
return exports.escape(this._name(name, context)); | ||
}; | ||
/** | ||
* Low-level function that compiles the given `tokens` into a function | ||
* that accepts three arguments: a Writer, a Context, and the template. | ||
*/ | ||
function compileTokens(tokens) { | ||
var subRenders = {}; | ||
function subRender(i, tokens, template) { | ||
if (!subRenders[i]) { | ||
var fn = compileTokens(tokens); | ||
subRenders[i] = function (writer, context) { | ||
return fn(writer, context, template); | ||
}; | ||
break; | ||
case '>': | ||
value = writer.getPartial(tokenValue); | ||
if (isFunction(value)) buffer += value(context); | ||
break; | ||
case '&': | ||
value = context.lookup(tokenValue); | ||
if (value != null) buffer += value; | ||
break; | ||
case 'name': | ||
value = context.lookup(tokenValue); | ||
if (value != null) buffer += mustache.escape(value); | ||
break; | ||
case 'text': | ||
buffer += tokenValue; | ||
break; | ||
} | ||
return subRenders[i]; | ||
} | ||
return function (writer, context, template) { | ||
var buffer = ""; | ||
var token, sectionText; | ||
for (var i = 0, len = tokens.length; i < len; ++i) { | ||
token = tokens[i]; | ||
switch (token[0]) { | ||
case "#": | ||
sectionText = template.slice(token[3], token[5]); | ||
buffer += writer._section(token[1], context, sectionText, subRender(i, token[4], template)); | ||
break; | ||
case "^": | ||
buffer += writer._inverted(token[1], context, subRender(i, token[4], template)); | ||
break; | ||
case ">": | ||
buffer += writer._partial(token[1], context); | ||
break; | ||
case "&": | ||
buffer += writer._name(token[1], context); | ||
break; | ||
case "name": | ||
buffer += writer._escaped(token[1], context); | ||
break; | ||
case "text": | ||
buffer += token[1]; | ||
break; | ||
} | ||
} | ||
return buffer; | ||
}; | ||
return buffer; | ||
} | ||
@@ -412,8 +356,10 @@ | ||
token = tokens[i]; | ||
if (token[0] === 'text' && lastToken && lastToken[0] === 'text') { | ||
lastToken[1] += token[1]; | ||
lastToken[3] = token[3]; | ||
} else { | ||
lastToken = token; | ||
squashedTokens.push(token); | ||
if (token) { | ||
if (token[0] === 'text' && lastToken && lastToken[0] === 'text') { | ||
lastToken[1] += token[1]; | ||
lastToken[3] = token[3]; | ||
} else { | ||
lastToken = token; | ||
squashedTokens.push(token); | ||
} | ||
} | ||
@@ -427,4 +373,4 @@ } | ||
return [ | ||
new RegExp(escapeRe(tags[0]) + "\\s*"), | ||
new RegExp("\\s*" + escapeRe(tags[1])) | ||
new RegExp(escapeRegExp(tags[0]) + "\\s*"), | ||
new RegExp("\\s*" + escapeRegExp(tags[1])) | ||
]; | ||
@@ -439,10 +385,8 @@ } | ||
*/ | ||
exports.parse = function (template, tags) { | ||
function parseTemplate(template, tags) { | ||
template = template || ''; | ||
tags = tags || exports.tags; | ||
tags = tags || mustache.tags; | ||
if (typeof tags === 'string') tags = tags.split(spaceRe); | ||
if (tags.length !== 2) { | ||
throw new Error('Invalid tags: ' + tags.join(', ')); | ||
} | ||
if (tags.length !== 2) throw new Error('Invalid tags: ' + tags.join(', ')); | ||
@@ -463,3 +407,3 @@ var tagRes = escapeTags(tags); | ||
while (spaces.length) { | ||
tokens.splice(spaces.pop(), 1); | ||
delete tokens[spaces.pop()]; | ||
} | ||
@@ -474,7 +418,8 @@ } else { | ||
var start, type, value, chr; | ||
var start, type, value, chr, token, openSection; | ||
while (!scanner.eos()) { | ||
start = scanner.pos; | ||
// Match any text between tags. | ||
value = scanner.scanUntil(tagRes[0]); | ||
if (value) { | ||
@@ -490,35 +435,28 @@ for (var i = 0, len = value.length; i < len; ++i) { | ||
tokens.push(["text", chr, start, start + 1]); | ||
tokens.push(['text', chr, start, start + 1]); | ||
start += 1; | ||
if (chr === "\n") { | ||
stripSpace(); // Check for whitespace on the current line. | ||
} | ||
// Check for whitespace on the current line. | ||
if (chr == '\n') stripSpace(); | ||
} | ||
} | ||
start = scanner.pos; | ||
// Match the opening tag. | ||
if (!scanner.scan(tagRes[0])) { | ||
break; | ||
} | ||
if (!scanner.scan(tagRes[0])) break; | ||
hasTag = true; | ||
type = scanner.scan(tagRe) || "name"; | ||
// Skip any whitespace between tag and value. | ||
// Get the tag type. | ||
type = scanner.scan(tagRe) || 'name'; | ||
scanner.scan(whiteRe); | ||
// Extract the tag value. | ||
if (type === "=") { | ||
// Get 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); | ||
} else if (type === '{') { | ||
value = scanner.scanUntil(new RegExp('\\s*' + escapeRegExp('}' + tags[1]))); | ||
scanner.scan(curlyRe); | ||
scanner.scanUntil(tagRes[1]); | ||
type = "&"; | ||
type = '&'; | ||
} else { | ||
@@ -529,20 +467,5 @@ value = scanner.scanUntil(tagRes[1]); | ||
// Match the closing tag. | ||
if (!scanner.scan(tagRes[1])) { | ||
throw new Error('Unclosed tag at ' + scanner.pos); | ||
} | ||
if (!scanner.scan(tagRes[1])) throw new Error('Unclosed tag at ' + scanner.pos); | ||
// Check section nesting. | ||
if (type === '/') { | ||
if (sections.length === 0) { | ||
throw new Error('Unopened section "' + value + '" at ' + start); | ||
} | ||
var section = sections.pop(); | ||
if (section[1] !== value) { | ||
throw new Error('Unclosed section "' + section[1] + '" at ' + start); | ||
} | ||
} | ||
var token = [type, value, start, scanner.pos]; | ||
token = [type, value, start, scanner.pos]; | ||
tokens.push(token); | ||
@@ -552,12 +475,19 @@ | ||
sections.push(token); | ||
} else if (type === "name" || type === "{" || type === "&") { | ||
} else if (type === '/') { | ||
// Check section nesting. | ||
openSection = sections.pop(); | ||
if (!openSection) { | ||
throw new Error('Unopened section "' + value + '" at ' + start); | ||
} | ||
if (openSection[1] !== value) { | ||
throw new Error('Unclosed section "' + openSection[1] + '" at ' + start); | ||
} | ||
} else if (type === 'name' || type === '{' || type === '&') { | ||
nonSpace = true; | ||
} else if (type === "=") { | ||
} else if (type === '=') { | ||
// Set the tags for the next time around. | ||
tags = value.split(spaceRe); | ||
if (tags.length !== 2) { | ||
throw new Error('Invalid tags at ' + start + ': ' + tags.join(', ')); | ||
} | ||
tagRes = escapeTags(tags); | ||
@@ -568,19 +498,32 @@ } | ||
// Make sure there are no open sections when we're done. | ||
var section = sections.pop(); | ||
if (section) { | ||
throw new Error('Unclosed section "' + section[1] + '" at ' + scanner.pos); | ||
openSection = sections.pop(); | ||
if (openSection) { | ||
throw new Error('Unclosed section "' + openSection[1] + '" at ' + scanner.pos); | ||
} | ||
return nestTokens(squashTokens(tokens)); | ||
}; | ||
} | ||
// The high-level clearCache, compile, compilePartial, and render functions | ||
// use this default writer. | ||
var _writer = new Writer(); | ||
mustache.name = "mustache.js"; | ||
mustache.version = "0.7.3"; | ||
mustache.tags = ["{{", "}}"]; | ||
mustache.Scanner = Scanner; | ||
mustache.Context = Context; | ||
mustache.Writer = Writer; | ||
mustache.parse = parseTemplate; | ||
// Export the escaping function so that the user may override it. | ||
// See https://github.com/janl/mustache.js/issues/244 | ||
mustache.escape = escapeHtml; | ||
// All Mustache.* functions use this writer. | ||
var defaultWriter = new Writer(); | ||
/** | ||
* Clears all cached templates and partials in the default writer. | ||
*/ | ||
exports.clearCache = function () { | ||
return _writer.clearCache(); | ||
mustache.clearCache = function () { | ||
return defaultWriter.clearCache(); | ||
}; | ||
@@ -592,4 +535,4 @@ | ||
*/ | ||
exports.compile = function (template, tags) { | ||
return _writer.compile(template, tags); | ||
mustache.compile = function (template, tags) { | ||
return defaultWriter.compile(template, tags); | ||
}; | ||
@@ -601,4 +544,4 @@ | ||
*/ | ||
exports.compilePartial = function (name, template, tags) { | ||
return _writer.compilePartial(name, template, tags); | ||
mustache.compilePartial = function (name, template, tags) { | ||
return defaultWriter.compilePartial(name, template, tags); | ||
}; | ||
@@ -610,4 +553,4 @@ | ||
*/ | ||
exports.compileTokens = function (tokens, template) { | ||
return _writer.compileTokens(tokens, template); | ||
mustache.compileTokens = function (tokens, template) { | ||
return defaultWriter.compileTokens(tokens, template); | ||
}; | ||
@@ -619,11 +562,11 @@ | ||
*/ | ||
exports.render = function (template, view, partials) { | ||
return _writer.render(template, view, partials); | ||
mustache.render = function (template, view, partials) { | ||
return defaultWriter.render(template, view, partials); | ||
}; | ||
// This is here for backwards compatibility with 0.4.x. | ||
exports.to_html = function (template, view, partials, send) { | ||
var result = exports.render(template, view, partials); | ||
mustache.to_html = function (template, view, partials, send) { | ||
var result = mustache.render(template, view, partials); | ||
if (typeof send === "function") { | ||
if (isFunction(send)) { | ||
send(result); | ||
@@ -635,4 +578,2 @@ } else { | ||
return exports; | ||
}()))); | ||
})); |
{ | ||
"name": "mustache", | ||
"version": "0.7.2", | ||
"version": "0.7.3", | ||
"description": "Logic-less {{mustache}} templates with JavaScript", | ||
"author": "mustache.js Authors <http://github.com/janl/mustache.js>", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/janl/mustache.js.git" | ||
}, | ||
"keywords": ["mustache", "template", "templates", "ejs"], | ||
@@ -12,3 +16,3 @@ "main": "./mustache.js", | ||
"volo": { | ||
"url": "https://raw.github.com/janl/mustache.js/0.7.2/mustache.js" | ||
"url": "https://raw.github.com/janl/mustache.js/0.7.3/mustache.js" | ||
}, | ||
@@ -15,0 +19,0 @@ "scripts": { |
@@ -105,3 +105,3 @@ # mustache.js - Logic-less {{mustache}} templates with JavaScript | ||
If the `person` key does not exist, or exists and has a value of `null`, `undefined`, or `false`, or is an empty list, the block will not be rendered. | ||
If the `person` key does not exist, or exists and has a value of `null`, `undefined`, `false`, `0`, or `NaN`, or is an empty string or an empty list, the block will not be rendered. | ||
@@ -352,3 +352,3 @@ View: | ||
$ rake dojo | ||
$ rake yui | ||
$ rake yui3 | ||
$ rake qooxdoo | ||
@@ -358,9 +358,14 @@ | ||
The mustache.js test suite uses the [vows](http://vowsjs.org/) testing framework. In order to run the tests you'll need to install [node](http://nodejs.org/). Once that's done you can install vows using [npm](http://npmjs.org/). | ||
The mustache.js test suite uses the [mocha](http://visionmedia.github.com/mocha/) testing framework. In order to run the tests you'll need to install [node](http://nodejs.org/). Once that's done you can install mocha using [npm](http://npmjs.org/). | ||
$ npm install -g vows | ||
$ npm install -g mocha | ||
You also need to install the sub module containing [Mustache specifications](http://github.com/mustache/spec) in the project root. | ||
$ git submodule init | ||
$ git submodule update | ||
Then run the tests. | ||
$ vows --spec | ||
$ mocha test | ||
@@ -379,3 +384,3 @@ The test suite consists of both unit and integration tests. If a template isn't rendering correctly for you, you can make a test for it by doing the following: | ||
$ TEST=mytest vows test/render_test.js | ||
$ TEST=mytest mocha test/render-test.js | ||
@@ -382,0 +387,0 @@ ## Thanks |
<ul> | ||
<li><a href="#aa/a">a</a></li> | ||
<li><a href="#aa/b">b</a></li> | ||
<li><a href="#aa/c">c</a></li> | ||
<li>aa/a</li> | ||
<li>aa/b</li> | ||
<li>aa/c</li> | ||
</ul> |
@@ -46,2 +46,19 @@ require('./helper'); | ||
describe('A Mustache.Context', function () { | ||
var context; | ||
describe('with an empty string in the lookup chain', function () { | ||
var view, context; | ||
beforeEach(function () { | ||
view = { a: '' }; | ||
view.a.b = 'value'; | ||
context = new Context(view); | ||
}); | ||
it('is able to lookup a nested property', function () { | ||
assert.equal(context.lookup('a.b'), view.a.b); | ||
}); | ||
}); | ||
}); | ||
describe('Mustache.Context.make', function () { | ||
@@ -48,0 +65,0 @@ it('returns the same object when given a Context', function () { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
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
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
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 repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
149338
207
1168
406
5
2