hogan.js
Advanced tools
Comparing version 2.0.0 to 3.0.1
@@ -23,8 +23,10 @@ /* | ||
rCr = /\r/g, | ||
rSlash = /\\/g, | ||
tagTypes = { | ||
'#': 1, '^': 2, '/': 3, '!': 4, '>': 5, | ||
'<': 6, '=': 7, '_v': 8, '{': 9, '&': 10 | ||
}; | ||
rSlash = /\\/g; | ||
Hogan.tags = { | ||
'#': 1, '^': 2, '<': 3, '$': 4, | ||
'/': 5, '!': 6, '>': 7, '=': 8, '_v': 9, | ||
'{': 10, '&': 11, '_t': 12 | ||
}; | ||
Hogan.scan = function scan(text, delimiters) { | ||
@@ -48,3 +50,3 @@ var len = text.length, | ||
if (buf.length > 0) { | ||
tokens.push(new String(buf)); | ||
tokens.push({tag: '_t', text: new String(buf)}); | ||
buf = ''; | ||
@@ -58,4 +60,4 @@ } | ||
isAllWhitespace = | ||
(tokens[j].tag && tagTypes[tokens[j].tag] < tagTypes['_v']) || | ||
(!tokens[j].tag && tokens[j].match(rIsWhitespace) === null); | ||
(Hogan.tags[tokens[j].tag] < Hogan.tags['_v']) || | ||
(tokens[j].tag == '_t' && tokens[j].text.match(rIsWhitespace) === null); | ||
if (!isAllWhitespace) { | ||
@@ -74,6 +76,6 @@ return false; | ||
for (var j = lineStart, next; j < tokens.length; j++) { | ||
if (!tokens[j].tag) { | ||
if (tokens[j].text) { | ||
if ((next = tokens[j+1]) && next.tag == '>') { | ||
// set indent to token value | ||
next.indent = tokens[j].toString() | ||
next.indent = tokens[j].text.toString() | ||
} | ||
@@ -99,3 +101,3 @@ tokens.splice(j, 1); | ||
otag = delimiters[0]; | ||
ctag = delimiters[1]; | ||
ctag = delimiters[delimiters.length - 1]; | ||
@@ -126,3 +128,3 @@ return closeIndex + close.length - 1; | ||
i += otag.length - 1; | ||
tag = tagTypes[text.charAt(i + 1)]; | ||
tag = Hogan.tags[text.charAt(i + 1)]; | ||
tagType = tag ? text.charAt(i + 1) : '_v'; | ||
@@ -142,3 +144,3 @@ if (tagType == '=') { | ||
tokens.push({tag: tagType, n: trim(buf), otag: otag, ctag: ctag, | ||
i: (tagType == '/') ? seenTag - ctag.length : i + otag.length}); | ||
i: (tagType == '/') ? seenTag - otag.length : i + ctag.length}); | ||
buf = ''; | ||
@@ -193,13 +195,23 @@ i += ctag.length - 1; | ||
// the tags allowed inside super templates | ||
var allowedInSuper = {'_t': true, '\n': true, '$': true, '/': true}; | ||
function buildTree(tokens, kind, stack, customTags) { | ||
var instructions = [], | ||
opener = null, | ||
tail = null, | ||
token = null; | ||
tail = stack[stack.length - 1]; | ||
while (tokens.length > 0) { | ||
token = tokens.shift(); | ||
if (token.tag == '#' || token.tag == '^' || isOpener(token, customTags)) { | ||
if (tail && tail.tag == '<' && !(token.tag in allowedInSuper)) { | ||
throw new Error('Illegal content in < super tag.'); | ||
} | ||
if (Hogan.tags[token.tag] <= Hogan.tags['$'] || isOpener(token, customTags)) { | ||
stack.push(token); | ||
token.nodes = buildTree(tokens, token.tag, stack, customTags); | ||
instructions.push(token); | ||
} else if (token.tag == '/') { | ||
@@ -215,5 +227,7 @@ if (stack.length === 0) { | ||
return instructions; | ||
} else { | ||
instructions.push(token); | ||
} else if (token.tag == '\n') { | ||
token.last = (tokens.length == 0) || (tokens[0].tag == '\n'); | ||
} | ||
instructions.push(token); | ||
} | ||
@@ -245,11 +259,58 @@ | ||
Hogan.generate = function (tree, text, options) { | ||
var code = 'var _=this;_.b(i=i||"");' + walk(tree) + 'return _.fl();'; | ||
function stringifySubstitutions(obj) { | ||
var items = []; | ||
for (var key in obj) { | ||
items.push('"' + esc(key) + '": function(c,p,t,i) {' + obj[key] + '}'); | ||
} | ||
return "{ " + items.join(",") + " }"; | ||
} | ||
function stringifyPartials(codeObj) { | ||
var partials = []; | ||
for (var key in codeObj.partials) { | ||
partials.push('"' + esc(key) + '":{name:"' + esc(codeObj.partials[key].name) + '", ' + stringifyPartials(codeObj.partials[key]) + "}"); | ||
} | ||
return "partials: {" + partials.join(",") + "}, subs: " + stringifySubstitutions(codeObj.subs); | ||
} | ||
Hogan.stringify = function(codeObj, text, options) { | ||
return "{code: function (c,p,i) { " + Hogan.wrapMain(codeObj.code) + " }," + stringifyPartials(codeObj) + "}"; | ||
} | ||
var serialNo = 0; | ||
Hogan.generate = function(tree, text, options) { | ||
serialNo = 0; | ||
var context = { code: '', subs: {}, partials: {} }; | ||
Hogan.walk(tree, context); | ||
if (options.asString) { | ||
return 'function(c,p,i){' + code + ';}'; | ||
return this.stringify(context, text, options); | ||
} | ||
return new Hogan.Template(new Function('c', 'p', 'i', code), text, Hogan, options); | ||
return this.makeTemplate(context, text, options); | ||
} | ||
Hogan.wrapMain = function(code) { | ||
return 'var t=this;t.b(i=i||"");' + code + 'return t.fl();'; | ||
} | ||
Hogan.template = Hogan.Template; | ||
Hogan.makeTemplate = function(codeObj, text, options) { | ||
var template = this.makePartials(codeObj); | ||
template.code = new Function('c', 'p', 'i', this.wrapMain(codeObj.code)); | ||
return new this.template(template, text, this, options); | ||
} | ||
Hogan.makePartials = function(codeObj) { | ||
var key, template = {subs: {}, partials: codeObj.partials, name: codeObj.name}; | ||
for (key in template.partials) { | ||
template.partials[key] = this.makePartials(template.partials[key]); | ||
} | ||
for (key in codeObj.subs) { | ||
template.subs[key] = new Function('c', 'p', 't', 'i', codeObj.subs[key]); | ||
} | ||
return template; | ||
} | ||
function esc(s) { | ||
@@ -266,56 +327,75 @@ return s.replace(rSlash, '\\\\') | ||
function walk(tree) { | ||
var code = ''; | ||
for (var i = 0, l = tree.length; i < l; i++) { | ||
var tag = tree[i].tag; | ||
if (tag == '#') { | ||
code += section(tree[i].nodes, tree[i].n, chooseMethod(tree[i].n), | ||
tree[i].i, tree[i].end, tree[i].otag + " " + tree[i].ctag); | ||
} else if (tag == '^') { | ||
code += invertedSection(tree[i].nodes, tree[i].n, | ||
chooseMethod(tree[i].n)); | ||
} else if (tag == '<' || tag == '>') { | ||
code += partial(tree[i]); | ||
} else if (tag == '{' || tag == '&') { | ||
code += tripleStache(tree[i].n, chooseMethod(tree[i].n)); | ||
} else if (tag == '\n') { | ||
code += text('"\\n"' + (tree.length-1 == i ? '' : ' + i')); | ||
} else if (tag == '_v') { | ||
code += variable(tree[i].n, chooseMethod(tree[i].n)); | ||
} else if (tag === undefined) { | ||
code += text('"' + esc(tree[i]) + '"'); | ||
} | ||
} | ||
return code; | ||
function createPartial(node, context) { | ||
var prefix = "<" + (context.prefix || ""); | ||
var sym = prefix + node.n + serialNo++; | ||
context.partials[sym] = {name: node.n, partials: {}}; | ||
context.code += 't.b(t.rp("' + esc(sym) + '",c,p,"' + (node.indent || '') + '"));'; | ||
return sym; | ||
} | ||
function section(nodes, id, method, start, end, tags) { | ||
return 'if(_.s(_.' + method + '("' + esc(id) + '",c,p,1),' + | ||
'c,p,0,' + start + ',' + end + ',"' + tags + '")){' + | ||
'_.rs(c,p,' + | ||
'function(c,p,_){' + | ||
walk(nodes) + | ||
'});c.pop();}'; | ||
} | ||
Hogan.codegen = { | ||
'#': function(node, context) { | ||
context.code += 'if(t.s(t.' + chooseMethod(node.n) + '("' + esc(node.n) + '",c,p,1),' + | ||
'c,p,0,' + node.i + ',' + node.end + ',"' + node.otag + " " + node.ctag + '")){' + | ||
't.rs(c,p,' + 'function(c,p,t){'; | ||
Hogan.walk(node.nodes, context); | ||
context.code += '});c.pop();}'; | ||
}, | ||
function invertedSection(nodes, id, method) { | ||
return 'if(!_.s(_.' + method + '("' + esc(id) + '",c,p,1),c,p,1,0,0,"")){' + | ||
walk(nodes) + | ||
'};'; | ||
} | ||
'^': function(node, context) { | ||
context.code += 'if(!t.s(t.' + chooseMethod(node.n) + '("' + esc(node.n) + '",c,p,1),c,p,1,0,0,"")){'; | ||
Hogan.walk(node.nodes, context); | ||
context.code += '};'; | ||
}, | ||
function partial(tok) { | ||
return '_.b(_.rp("' + esc(tok.n) + '",c,p,"' + (tok.indent || '') + '"));'; | ||
'>': createPartial, | ||
'<': function(node, context) { | ||
var ctx = {partials: {}, code: '', subs: {}, inPartial: true}; | ||
Hogan.walk(node.nodes, ctx); | ||
var template = context.partials[createPartial(node, context)]; | ||
template.subs = ctx.subs; | ||
template.partials = ctx.partials; | ||
}, | ||
'$': function(node, context) { | ||
var ctx = {subs: {}, code: '', partials: context.partials, prefix: node.n}; | ||
Hogan.walk(node.nodes, ctx); | ||
context.subs[node.n] = ctx.code; | ||
if (!context.inPartial) { | ||
context.code += 't.sub("' + esc(node.n) + '",c,p,i);'; | ||
} | ||
}, | ||
'\n': function(node, context) { | ||
context.code += write('"\\n"' + (node.last ? '' : ' + i')); | ||
}, | ||
'_v': function(node, context) { | ||
context.code += 't.b(t.v(t.' + chooseMethod(node.n) + '("' + esc(node.n) + '",c,p,0)));'; | ||
}, | ||
'_t': function(node, context) { | ||
context.code += write('"' + esc(node.text) + '"'); | ||
}, | ||
'{': tripleStache, | ||
'&': tripleStache | ||
} | ||
function tripleStache(id, method) { | ||
return '_.b(_.t(_.' + method + '("' + esc(id) + '",c,p,0)));'; | ||
function tripleStache(node, context) { | ||
context.code += 't.b(t.t(t.' + chooseMethod(node.n) + '("' + esc(node.n) + '",c,p,0)));'; | ||
} | ||
function variable(id, method) { | ||
return '_.b(_.v(_.' + method + '("' + esc(id) + '",c,p,0)));'; | ||
function write(s) { | ||
return 't.b(' + s + ');'; | ||
} | ||
function text(id) { | ||
return '_.b(' + id + ');'; | ||
Hogan.walk = function(nodelist, context) { | ||
var func; | ||
for (var i = 0, l = nodelist.length; i < l; i++) { | ||
func = Hogan.codegen[nodelist[i].tag]; | ||
func && func(nodelist[i], context); | ||
} | ||
return context; | ||
} | ||
@@ -326,31 +406,26 @@ | ||
return buildTree(tokens, '', [], options.sectionTags || []); | ||
}, | ||
} | ||
Hogan.cache = {}; | ||
Hogan.cacheKey = function(text, options) { | ||
return [text, !!options.asString, !!options.disableLambda, options.delimiters, !!options.modelGet].join('||'); | ||
} | ||
Hogan.compile = function(text, options) { | ||
// options | ||
// | ||
// asString: false (default) | ||
// | ||
// sectionTags: [{o: '_foo', c: 'foo'}] | ||
// An array of object with o and c fields that indicate names for custom | ||
// section tags. The example above allows parsing of {{_foo}}{{/foo}}. | ||
// | ||
// delimiters: A string that overrides the default delimiters. | ||
// Example: "<% %>" | ||
// | ||
options = options || {}; | ||
var key = Hogan.cacheKey(text, options); | ||
var template = this.cache[key]; | ||
var key = text + '||' + !!options.asString; | ||
var t = this.cache[key]; | ||
if (t) { | ||
return t; | ||
if (template) { | ||
var partials = template.partials; | ||
for (var name in partials) { | ||
delete partials[name].instance; | ||
} | ||
return template; | ||
} | ||
t = this.generate(this.parse(this.scan(text, options.delimiters), text, options), text, options); | ||
return this.cache[key] = t; | ||
}; | ||
template = this.generate(this.parse(this.scan(text, options.delimiters), text, options), text, options); | ||
return this.cache[key] = template; | ||
} | ||
})(typeof exports !== 'undefined' ? exports : Hogan); |
@@ -20,2 +20,3 @@ /* | ||
Hogan.Template = require('./template').Template; | ||
module.exports = Hogan; | ||
Hogan.template = Hogan.Template; | ||
module.exports = Hogan; |
@@ -18,9 +18,12 @@ /* | ||
(function (Hogan, useArrayBuffer) { | ||
Hogan.Template = function (renderFunc, text, compiler, options) { | ||
this.r = renderFunc || this.r; | ||
(function (Hogan) { | ||
Hogan.Template = function (codeObj, text, compiler, options) { | ||
codeObj = codeObj || {}; | ||
this.r = codeObj.code || this.r; | ||
this.c = compiler; | ||
this.options = options; | ||
this.options = options || {}; | ||
this.text = text || ''; | ||
this.buf = (useArrayBuffer) ? [] : ''; | ||
this.partials = codeObj.partials || {}; | ||
this.subs = codeObj.subs || {}; | ||
this.buf = ''; | ||
} | ||
@@ -47,6 +50,45 @@ | ||
// tries to find a partial in the curent scope and render it | ||
rp: function(name, context, partials, indent) { | ||
var partial = partials[name]; | ||
// ensurePartial | ||
ep: function(symbol, partials) { | ||
var partial = this.partials[symbol]; | ||
// check to see that if we've instantiated this partial before | ||
var template = partials[partial.name]; | ||
if (partial.instance && partial.base == template) { | ||
return partial.instance; | ||
} | ||
if (typeof template == 'string') { | ||
if (!this.c) { | ||
throw new Error("No compiler available."); | ||
} | ||
template = this.c.compile(template, this.options); | ||
} | ||
if (!template) { | ||
return null; | ||
} | ||
// We use this to check whether the partials dictionary has changed | ||
this.partials[symbol].base = template; | ||
if (partial.subs) { | ||
// Make sure we consider parent template now | ||
if (!partials.stackText) partials.stackText = {}; | ||
for (key in partial.subs) { | ||
if (!partials.stackText[key]) { | ||
partials.stackText[key] = (this.activeSub !== undefined && partials.stackText[this.activeSub]) ? partials.stackText[this.activeSub] : this.text; | ||
} | ||
} | ||
template = createSpecializedPartial(template, partial.subs, partial.partials, | ||
this.stackSubs, this.stackPartials, partials.stackText); | ||
} | ||
this.partials[symbol].instance = template; | ||
return template; | ||
}, | ||
// tries to find a partial in the current scope and render it | ||
rp: function(symbol, context, partials, indent) { | ||
var partial = this.ep(symbol, partials); | ||
if (!partial) { | ||
@@ -56,6 +98,2 @@ return ''; | ||
if (this.c && typeof partial == 'string') { | ||
partial = this.c.compile(partial, this.options); | ||
} | ||
return partial.ri(context, partials, indent); | ||
@@ -89,6 +127,6 @@ }, | ||
if (typeof val == 'function') { | ||
val = this.ls(val, ctx, partials, inverted, start, end, tags); | ||
val = this.ms(val, ctx, partials, inverted, start, end, tags); | ||
} | ||
pass = (val === '') || !!val; | ||
pass = !!val; | ||
@@ -104,16 +142,19 @@ if (!inverted && pass && ctx) { | ||
d: function(key, ctx, partials, returnFound) { | ||
var names = key.split('.'), | ||
var found, | ||
names = key.split('.'), | ||
val = this.f(names[0], ctx, partials, returnFound), | ||
doModelGet = this.options.modelGet, | ||
cx = null; | ||
if (key === '.' && isArray(ctx[ctx.length - 2])) { | ||
return ctx[ctx.length - 1]; | ||
} | ||
for (var i = 1; i < names.length; i++) { | ||
if (val && typeof val == 'object' && names[i] in val) { | ||
cx = val; | ||
val = val[names[i]]; | ||
} else { | ||
val = ''; | ||
val = ctx[ctx.length - 1]; | ||
} else { | ||
for (var i = 1; i < names.length; i++) { | ||
found = findInScope(names[i], val, doModelGet); | ||
if (found != null) { | ||
cx = val; | ||
val = found; | ||
} else { | ||
val = ''; | ||
} | ||
} | ||
@@ -128,3 +169,3 @@ } | ||
ctx.push(cx); | ||
val = this.lv(val, ctx, partials); | ||
val = this.mv(val, ctx, partials); | ||
ctx.pop(); | ||
@@ -140,8 +181,9 @@ } | ||
v = null, | ||
found = false; | ||
found = false, | ||
doModelGet = this.options.modelGet; | ||
for (var i = ctx.length - 1; i >= 0; i--) { | ||
v = ctx[i]; | ||
if (v && typeof v == 'object' && key in v) { | ||
val = v[key]; | ||
val = findInScope(key, v, doModelGet); | ||
if (val != null) { | ||
found = true; | ||
@@ -157,3 +199,3 @@ break; | ||
if (!returnFound && typeof val == 'function') { | ||
val = this.lv(val, ctx, partials); | ||
val = this.mv(val, ctx, partials); | ||
} | ||
@@ -165,53 +207,62 @@ | ||
// higher order templates | ||
ho: function(val, cx, partials, text, tags) { | ||
var compiler = this.c; | ||
var options = this.options; | ||
options.delimiters = tags; | ||
var text = val.call(cx, text); | ||
text = (text == null) ? String(text) : text.toString(); | ||
this.b(compiler.compile(text, options).render(cx, partials)); | ||
ls: function(func, cx, partials, text, tags) { | ||
var oldTags = this.options.delimiters; | ||
this.options.delimiters = tags; | ||
this.b(this.ct(coerceToString(func.call(cx, text)), cx, partials)); | ||
this.options.delimiters = oldTags; | ||
return false; | ||
}, | ||
// compile text | ||
ct: function(text, cx, partials) { | ||
if (this.options.disableLambda) { | ||
throw new Error('Lambda features disabled.'); | ||
} | ||
return this.c.compile(text, this.options).render(cx, partials); | ||
}, | ||
// template result buffering | ||
b: (useArrayBuffer) ? function(s) { this.buf.push(s); } : | ||
function(s) { this.buf += s; }, | ||
fl: (useArrayBuffer) ? function() { var r = this.buf.join(''); this.buf = []; return r; } : | ||
function() { var r = this.buf; this.buf = ''; return r; }, | ||
b: function(s) { this.buf += s; }, | ||
// lambda replace section | ||
ls: function(val, ctx, partials, inverted, start, end, tags) { | ||
var cx = ctx[ctx.length - 1], | ||
t = null; | ||
fl: function() { var r = this.buf; this.buf = ''; return r; }, | ||
if (!inverted && this.c && val.length > 0) { | ||
return this.ho(val, cx, partials, this.text.substring(start, end), tags); | ||
} | ||
// method replace section | ||
ms: function(func, ctx, partials, inverted, start, end, tags) { | ||
var textSource, | ||
cx = ctx[ctx.length - 1], | ||
result = func.call(cx); | ||
t = val.call(cx); | ||
if (typeof t == 'function') { | ||
if (typeof result == 'function') { | ||
if (inverted) { | ||
return true; | ||
} else if (this.c) { | ||
return this.ho(t, cx, partials, this.text.substring(start, end), tags); | ||
} else { | ||
textSource = (this.activeSub && this.subsText && this.subsText[this.activeSub]) ? this.subsText[this.activeSub] : this.text; | ||
return this.ls(result, cx, partials, textSource.substring(start, end), tags); | ||
} | ||
} | ||
return t; | ||
return result; | ||
}, | ||
// lambda replace variable | ||
lv: function(val, ctx, partials) { | ||
// method replace variable | ||
mv: function(func, ctx, partials) { | ||
var cx = ctx[ctx.length - 1]; | ||
var result = val.call(cx); | ||
var result = func.call(cx); | ||
if (typeof result == 'function') { | ||
result = coerceToString(result.call(cx)); | ||
if (this.c && ~result.indexOf("{\u007B")) { | ||
return this.c.compile(result, this.options).render(cx, partials); | ||
} | ||
return this.ct(coerceToString(result.call(cx)), cx, partials); | ||
} | ||
return coerceToString(result); | ||
return result; | ||
}, | ||
sub: function(name, context, partials, indent) { | ||
var f = this.subs[name]; | ||
if (f) { | ||
this.activeSub = name; | ||
f(context, partials, this, indent); | ||
this.activeSub = false; | ||
} | ||
} | ||
@@ -221,10 +272,60 @@ | ||
//Find a key in an object | ||
function findInScope(key, scope, doModelGet) { | ||
var val, checkVal; | ||
if (scope && typeof scope == 'object') { | ||
if (scope[key] != null) { | ||
val = scope[key]; | ||
// try lookup with get for backbone or similar model data | ||
} else if (doModelGet && scope.get && typeof scope.get == 'function') { | ||
val = scope.get(key); | ||
} | ||
} | ||
return val; | ||
} | ||
function createSpecializedPartial(instance, subs, partials, stackSubs, stackPartials, stackText) { | ||
function PartialTemplate() {}; | ||
PartialTemplate.prototype = instance; | ||
function Substitutions() {}; | ||
Substitutions.prototype = instance.subs; | ||
var key; | ||
var partial = new PartialTemplate(); | ||
partial.subs = new Substitutions(); | ||
partial.subsText = {}; //hehe. substext. | ||
partial.buf = ''; | ||
stackSubs = stackSubs || {}; | ||
partial.stackSubs = stackSubs; | ||
partial.subsText = stackText; | ||
for (key in subs) { | ||
if (!stackSubs[key]) stackSubs[key] = subs[key]; | ||
} | ||
for (key in stackSubs) { | ||
partial.subs[key] = stackSubs[key]; | ||
} | ||
stackPartials = stackPartials || {}; | ||
partial.stackPartials = stackPartials; | ||
for (key in partials) { | ||
if (!stackPartials[key]) stackPartials[key] = partials[key]; | ||
} | ||
for (key in stackPartials) { | ||
partial.partials[key] = stackPartials[key]; | ||
} | ||
return partial; | ||
} | ||
var rAmp = /&/g, | ||
rLt = /</g, | ||
rGt = />/g, | ||
rApos =/\'/g, | ||
rApos = /\'/g, | ||
rQuot = /\"/g, | ||
hChars =/[&<>\"\']/; | ||
hChars = /[&<>\"\']/; | ||
function coerceToString(val) { | ||
@@ -238,6 +339,6 @@ return String((val === null || val === undefined) ? '' : val); | ||
str | ||
.replace(rAmp,'&') | ||
.replace(rLt,'<') | ||
.replace(rGt,'>') | ||
.replace(rApos,''') | ||
.replace(rAmp, '&') | ||
.replace(rLt, '<') | ||
.replace(rGt, '>') | ||
.replace(rApos, ''') | ||
.replace(rQuot, '"') : | ||
@@ -252,2 +353,1 @@ str; | ||
})(typeof exports !== 'undefined' ? exports : Hogan); | ||
{ | ||
"name": "hogan.js" | ||
, "description": "A mustache compiler." | ||
, "version": "2.0.0" | ||
, "version": "3.0.1" | ||
, "keywords": ["mustache", "template"] | ||
@@ -18,8 +18,13 @@ , "main": "./lib/hogan.js" | ||
] | ||
, "dependencies" : { | ||
"nopt" : "1.0.10" | ||
, "mkdirp": "0.3.0" | ||
} | ||
, "devDependencies": { | ||
"uglify-js": "*" | ||
, "jsdom": "0.2.10" | ||
"uglify-js": "2.x" | ||
, "jsdom": "0.3.4" | ||
, "step": "0.0.5" | ||
, "rimraf": "2.0.1" | ||
} | ||
, "bin" : { "hulk" : "./bin/hulk" } | ||
} |
@@ -1,5 +0,5 @@ | ||
## Hogan.js - A mustache compiler. | ||
## Hogan.js - A mustache compiler. [![Build Status](https://secure.travis-ci.org/twitter/hogan.js.png)](http://travis-ci.org/twitter/hogan.js) | ||
[Hogan.js](http://twitter.github.com/hogan.js/) is a compiler for the | ||
[Mustache](http://mustache.github.com/) templating language. For information | ||
[Hogan.js](http://twitter.github.io/hogan.js/) is a compiler for the | ||
[Mustache](http://mustache.github.io/) templating language. For information | ||
on Mustache, see the [manpage](http://mustache.github.com/mustache.5.html) and | ||
@@ -50,4 +50,8 @@ the [spec](https://github.com/mustache/spec). | ||
avoid shipping the compiler. However, the optional lambda features from the | ||
Mustache spec do require the compiler to be present. | ||
Mustache spec require the compiler and the original template source to be present. | ||
Hogan also supports [template inheritance](https://github.com/mustache/spec/pull/75), | ||
and maintains compatibility with other implementations like [mustache.java](https://github.com/spullara/mustache.java), | ||
[mustache.php](https://github.com/bobthecow/mustache.php), and [GRMustache](https://github.com/groue/GRMustache) | ||
## Why Hogan.js? | ||
@@ -60,2 +64,45 @@ | ||
## Install | ||
# Node.js | ||
``` | ||
npm install hogan.js | ||
``` | ||
# component | ||
``` | ||
component install twitter/hogan.js | ||
``` | ||
## Compilation options | ||
The second argument to Hogan.compile is an options hash. | ||
```js | ||
var text = "my <%example%> template." | ||
Hogan.compile(text, {delimiters: '<% %>'}); | ||
``` | ||
There are currently four valid options. | ||
asString: return the compiled template as a string. This feature is used | ||
by hulk to produce strings containing pre-compiled templates. | ||
sectionTags: allow custom tags that require opening and closing tags, and | ||
treat them as though they were section tags. | ||
```js | ||
var text = "my {{_foo}}example{{/foo}} template." | ||
Hogan.compile(text, { sectionTags: [{o: '_foo', c: 'foo'}]}); | ||
``` | ||
The value is an array of object with o and c fields that indicate names | ||
for custom section tags. The example above allows parsing of {{_foo}}{{/foo}}. | ||
delimiters: A string that overrides the default delimiters. Example: "<% %>". | ||
disableLambda: disables the higher-order sections / lambda-replace features of Mustache. | ||
## Issues | ||
@@ -81,2 +128,17 @@ | ||
## Testing | ||
To run the tests you first need to update all git submodules. | ||
$ git submodule init | ||
$ git submodule update | ||
Unit tests are written using [QUnit](http://qunitjs.com/). To run them, open `test/index.html` | ||
in a browser. | ||
Use [node](http://nodejs.org/) to run all tests from the | ||
[mustache spec](https://github.com/mustache/spec). | ||
$ node test/spec.js | ||
## Authors | ||
@@ -96,2 +158,2 @@ | ||
Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 | ||
Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
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
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 eval() which is a dangerous function. This prevents 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
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
156
0
1
43289
2
4
11
682
+ Addedmkdirp@0.3.0
+ Addednopt@1.0.10
+ Addedabbrev@1.1.1(transitive)
+ Addedmkdirp@0.3.0(transitive)
+ Addednopt@1.0.10(transitive)