зеркало из https://github.com/mozilla/nunjucks.git
add license, lots of tweaks to get ready for release
This commit is contained in:
Родитель
78bf9dc3fe
Коммит
2d41dab9a3
|
@ -0,0 +1,26 @@
|
|||
Copyright (c) 2012, James Long
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -4,6 +4,11 @@ var path = require('path');
|
|||
var util = require('util');
|
||||
var compiler = require('../src/compiler');
|
||||
|
||||
if(process.argv.length < 3) {
|
||||
console.log('Usage: ' + path.basename(process.argv[1]) + ' <folder>');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
var folder = process.argv[2];
|
||||
var templates = [];
|
||||
|
|
@ -79,7 +79,7 @@ exports.isString = function(obj) {
|
|||
|
||||
exports.isObject = function(obj) {
|
||||
return obj === Object(obj);
|
||||
}
|
||||
};
|
||||
|
||||
exports.groupBy = function(obj, val) {
|
||||
var result = {};
|
||||
|
@ -139,7 +139,7 @@ exports.map = function(obj, func) {
|
|||
}
|
||||
|
||||
for(var i=0; i<obj.length; i++) {
|
||||
results[results.length] = func(value, i);
|
||||
results[results.length] = func(obj[i], i);
|
||||
}
|
||||
|
||||
if(obj.length === +obj.length) {
|
||||
|
@ -298,6 +298,7 @@ var TemplateRef = Node.extend("TemplateRef", {
|
|||
|
||||
var Extends = TemplateRef.extend("Extends");
|
||||
var Include = TemplateRef.extend("Include");
|
||||
var Set = Node.extend("Set");
|
||||
|
||||
var Output = Node.extend("Output");
|
||||
var TemplateData = Literal.extend("TemplateData");
|
||||
|
@ -399,6 +400,13 @@ function printNodes(node, indent) {
|
|||
printNodes(node.name, indent+2);
|
||||
printNodes(node.body, indent+2);
|
||||
}
|
||||
else if(node instanceof Set) {
|
||||
print("\n");
|
||||
for(var i=0; i<node.targets.length; i++) {
|
||||
printNodes(node.targets[i], indent+2);
|
||||
}
|
||||
printNodes(node.value, indent+2);
|
||||
}
|
||||
else if(node instanceof LookupVal) {
|
||||
print("\n");
|
||||
printNodes(node.target, indent+2);
|
||||
|
@ -460,6 +468,7 @@ modules['nodes'] = {
|
|||
Block: Block,
|
||||
Extends: Extends,
|
||||
Include: Include,
|
||||
Set: Set,
|
||||
LookupVal: LookupVal,
|
||||
BinOp: BinOp,
|
||||
Or: Or,
|
||||
|
@ -493,8 +502,22 @@ var Frame = Object.extend({
|
|||
this.parent = parent;
|
||||
},
|
||||
|
||||
addVariable: function(name, id) {
|
||||
this.variables[name] = id;
|
||||
set: function(name, val) {
|
||||
// Allow variables with dots by automatically creating the
|
||||
// nested structure
|
||||
var parts = name.split('.');
|
||||
var obj = this.variables;
|
||||
|
||||
for(var i=0; i<parts.length - 1; i++) {
|
||||
var id = parts[i];
|
||||
|
||||
if(!obj[id]) {
|
||||
obj[id] = {};
|
||||
}
|
||||
obj = obj[id];
|
||||
}
|
||||
|
||||
obj[parts[parts.length - 1]] = val;
|
||||
},
|
||||
|
||||
lookup: function(name) {
|
||||
|
@ -894,6 +917,15 @@ modules['lexer'] = {
|
|||
return new Tokenizer(src);
|
||||
},
|
||||
|
||||
setTags: function(tags) {
|
||||
BLOCK_START = tags.blockStart || BLOCK_START;
|
||||
BLOCK_END = tags.blockEnd || BLOCK_END;
|
||||
VARIABLE_START = tags.variableStart || VARIABLE_START;
|
||||
VARIABLE_END = tags.variableEnd || VARIABLE_END;
|
||||
COMMENT_START = tags.commentStart || COMMENT_START;
|
||||
COMMENT_END = tags.commentEnd || COMMENT_END;
|
||||
},
|
||||
|
||||
TOKEN_STRING: TOKEN_STRING,
|
||||
TOKEN_WHITESPACE: TOKEN_WHITESPACE,
|
||||
TOKEN_DATA: TOKEN_DATA,
|
||||
|
@ -1165,6 +1197,34 @@ var Parser = Object.extend({
|
|||
return node;
|
||||
},
|
||||
|
||||
parseSet: function() {
|
||||
var tag = this.peekToken();
|
||||
if(!this.skipSymbol('set')) {
|
||||
this.fail('expected set');
|
||||
}
|
||||
|
||||
var node = new nodes.Set(tag.lineno, tag.colno);
|
||||
node.targets = [];
|
||||
|
||||
var target;
|
||||
while((target = this.parsePrimary())) {
|
||||
node.targets.push(target);
|
||||
|
||||
if(!this.skip(lexer.TOKEN_COMMA)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!this.skipValue(lexer.TOKEN_OPERATOR, '=')) {
|
||||
this.fail('expected = in set tag');
|
||||
}
|
||||
|
||||
node.value = this.parseExpression();
|
||||
this.advanceAfterBlockEnd(tag.value);
|
||||
|
||||
return node;
|
||||
},
|
||||
|
||||
parseStatement: function () {
|
||||
var tok = this.peekToken();
|
||||
var node;
|
||||
|
@ -1185,6 +1245,7 @@ var Parser = Object.extend({
|
|||
case 'block': node = this.parseBlock(); break;
|
||||
case 'extends': node = this.parseExtends(); break;
|
||||
case 'include': node = this.parseInclude(); break;
|
||||
case 'set': node = this.parseSet(); break;
|
||||
default: this.fail('unknown block tag: ' + tok.value);
|
||||
}
|
||||
|
||||
|
@ -1679,16 +1740,15 @@ var Parser = Object.extend({
|
|||
}
|
||||
});
|
||||
|
||||
// var util = modules["util"];
|
||||
var util = modules["util"];
|
||||
|
||||
// console.log('sdfd\\"sdfd');
|
||||
// var l = lexer.lex('1 2 3 {{ "h\\"ello" }} 4');
|
||||
// var l = lexer.lex('{% set x = 3 %}');
|
||||
// var t;
|
||||
// while((t = l.nextToken())) {
|
||||
// console.log(util.inspect(t));
|
||||
// }
|
||||
|
||||
// var p = new Parser(lexer.lex('{% for i, k in items %}{% endfor %}'));
|
||||
// var p = new Parser(lexer.lex('{% set x, y = 3 %}'));
|
||||
// var n = p.parse();
|
||||
// nodes.printNodes(n);
|
||||
|
||||
|
@ -1794,7 +1854,8 @@ var Compiler = Object.extend({
|
|||
nodes.FunCall,
|
||||
nodes.Filter,
|
||||
nodes.LookupVal,
|
||||
nodes.Compare);
|
||||
nodes.Compare,
|
||||
nodes.Not);
|
||||
this.compile(node, frame);
|
||||
},
|
||||
|
||||
|
@ -1944,6 +2005,20 @@ var Compiler = Object.extend({
|
|||
this._compileAggregate(node, frame, '(', ')');
|
||||
},
|
||||
|
||||
compileSet: function(node, frame) {
|
||||
var val = this.tmpid();
|
||||
|
||||
this.emit('var ' + val + ' = ');
|
||||
this._compileExpression(node.value);
|
||||
this.emitLine(';');
|
||||
|
||||
for(var i=0; i<node.targets.length; i++) {
|
||||
var t = node.targets[i];
|
||||
this.emitLine('context.setVariable("' + t.value + '", ' +
|
||||
val + ');');
|
||||
}
|
||||
},
|
||||
|
||||
compileIf: function(node, frame) {
|
||||
this.emit('if(');
|
||||
this._compileExpression(node.cond, frame);
|
||||
|
@ -1976,24 +2051,36 @@ var Compiler = Object.extend({
|
|||
var k = this.tmpid();
|
||||
var v = this.tmpid();
|
||||
|
||||
frame.addVariable(key.value, k);
|
||||
frame.addVariable(val.value, v);
|
||||
frame.set(key.value, k);
|
||||
frame.set(val.value, v);
|
||||
|
||||
this.emitLine('var ' + i + ' = -1;');
|
||||
this.emitLine('for(var ' + k + ' in ' + arr + ') {');
|
||||
this.emitLine(i + '++;');
|
||||
this.emitLine('var ' + v + ' = ' + arr + '[' + k + '];');
|
||||
this.emitLine('frame.addVariable("' + key.value + '", ' + k + ');');
|
||||
this.emitLine('frame.addVariable("' + val.value + '", ' + v + ');');
|
||||
this.emitLine('frame.set("' + key.value + '", ' + k + ');');
|
||||
this.emitLine('frame.set("' + val.value + '", ' + v + ');');
|
||||
this.emitLine('frame.set("loop.index", ' + i + ' + 1);');
|
||||
this.emitLine('frame.set("loop.index0", ' + i + ');');
|
||||
this.emitLine('frame.set("loop.first", ' + i + ' === 0);');
|
||||
}
|
||||
else {
|
||||
var v = this.tmpid();
|
||||
|
||||
frame.addVariable(node.name.value, v);
|
||||
frame.set(node.name.value, v);
|
||||
|
||||
this.emitLine('for(var ' + i + '=0; ' + i + ' < ' + arr + '.length; ' +
|
||||
i + '++) {');
|
||||
this.emitLine('var ' + v + ' = ' + arr + '[' + i + '];');
|
||||
this.emitLine('frame.addVariable("' + node.name.value +
|
||||
this.emitLine('frame.set("' + node.name.value +
|
||||
'", ' + v + ');');
|
||||
this.emitLine('frame.set("loop.index", ' + i + ' + 1);');
|
||||
this.emitLine('frame.set("loop.index0", ' + i + ');');
|
||||
this.emitLine('frame.set("loop.revindex", ' + arr + '.length - ' + i + ');');
|
||||
this.emitLine('frame.set("loop.revindex0", ' + arr + '.length - ' + i + ' - 1);');
|
||||
this.emitLine('frame.set("loop.first", ' + i + ' === 0);');
|
||||
this.emitLine('frame.set("loop.last", ' + i + ' === ' + arr + '.length - 1);');
|
||||
this.emitLine('frame.set("loop.length", ' + arr + '.length);');
|
||||
}
|
||||
|
||||
this.compile(node.body, frame);
|
||||
|
@ -2071,7 +2158,7 @@ var Compiler = Object.extend({
|
|||
'b_' + name + ');');
|
||||
|
||||
var tmpFrame = frame.push();
|
||||
frame.addVariable('super', 'l_super');
|
||||
frame.set('super', 'l_super');
|
||||
this.compile(block.body, tmpFrame);
|
||||
|
||||
this.emitFuncEnd();
|
||||
|
@ -2103,7 +2190,7 @@ var Compiler = Object.extend({
|
|||
|
||||
// var fs = modules["fs"];
|
||||
// var c = new Compiler();
|
||||
// var src = "{% for i in [1,2,3] %}{{ i }}{% endfor %}";
|
||||
// var src = "{% for i in [3,45,1] %}{{ loop.index }}{% endfor %}";
|
||||
|
||||
// var ns = parser.parse(src);
|
||||
// nodes.printNodes(ns);
|
||||
|
@ -2180,10 +2267,6 @@ var filters = {
|
|||
return val ? val : def;
|
||||
},
|
||||
|
||||
dictsort: function(dict, caseSens, by) {
|
||||
by = by || 'key';
|
||||
},
|
||||
|
||||
escape: function(str) {
|
||||
return str.replace(/&/g, '&')
|
||||
.replace(/"/g, '"')
|
||||
|
@ -2192,34 +2275,46 @@ var filters = {
|
|||
.replace(/>/g, '>');
|
||||
},
|
||||
|
||||
filesizeformat: function(val, binary) {
|
||||
},
|
||||
|
||||
first: function(arr) {
|
||||
return arr[0];
|
||||
},
|
||||
|
||||
forceescape: function(val) {
|
||||
|
||||
},
|
||||
|
||||
format: function(str /*, vals */) {
|
||||
var args = lib.toArray(arguments).slice(1);
|
||||
},
|
||||
|
||||
groupby: function(arr, attr) {
|
||||
return lib.groupBy(arr, attr);
|
||||
},
|
||||
|
||||
indent: function(str, width, indentfirst) {
|
||||
width = width || 4;
|
||||
var res = '';
|
||||
var lines = str.split('\n');
|
||||
var sp = lib.repeat(' ', width);
|
||||
|
||||
for(var i=0; i<lines.length; i++) {
|
||||
if(i == 0 && !indentfirst) {
|
||||
res += lines[i] + '\n';
|
||||
}
|
||||
else {
|
||||
res += sp + lines[i] + '\n';
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
},
|
||||
|
||||
join: function(val, d, attr) {
|
||||
d = d || '';
|
||||
join: function(arr, del, attr) {
|
||||
del = del || '';
|
||||
|
||||
if(attr) {
|
||||
arr = lib.map(arr, function(v) {
|
||||
return v[attr];
|
||||
});
|
||||
}
|
||||
|
||||
return arr.join(del);
|
||||
},
|
||||
|
||||
last: function(arr) {
|
||||
return arr[arr.length-1];
|
||||
},
|
||||
|
||||
length: function(arr) {
|
||||
|
@ -2227,80 +2322,183 @@ var filters = {
|
|||
},
|
||||
|
||||
list: function(val) {
|
||||
if(lib.isString(val)) {
|
||||
return val.split('');
|
||||
}
|
||||
else if(lib.isObject(val)) {
|
||||
var keys = [];
|
||||
|
||||
if(Object.keys) {
|
||||
keys = Object.keys(val);
|
||||
}
|
||||
else {
|
||||
for(var k in val) {
|
||||
keys.push(k);
|
||||
}
|
||||
}
|
||||
|
||||
return lib.map(keys, function(k) {
|
||||
return { key: k,
|
||||
value: val[k] };
|
||||
});
|
||||
}
|
||||
else {
|
||||
throw new Error("list: type not iterable");
|
||||
}
|
||||
},
|
||||
|
||||
lower: function(str) {
|
||||
},
|
||||
|
||||
pprint: function(val) {
|
||||
return str.toLowerCase();
|
||||
},
|
||||
|
||||
random: function(arr) {
|
||||
var i = Math.floor(Math.random() * arr.length);
|
||||
if(i == arr.length) {
|
||||
i--;
|
||||
}
|
||||
|
||||
return arr[i];
|
||||
},
|
||||
|
||||
replace: function(str, old, new_, count) {
|
||||
replace: function(str, old, new_, maxCount) {
|
||||
var res = str;
|
||||
var last = res;
|
||||
var count = 1;
|
||||
res = res.replace(old, new_);
|
||||
|
||||
while(last != res) {
|
||||
if(count >= maxCount) {
|
||||
break;
|
||||
}
|
||||
|
||||
last = res;
|
||||
res = res.replace(old, new_);
|
||||
count++;
|
||||
}
|
||||
|
||||
return res;
|
||||
},
|
||||
|
||||
reverse: function(str) {
|
||||
reverse: function(val) {
|
||||
var arr;
|
||||
if(lib.isString(val)) {
|
||||
arr = filters.list(val);
|
||||
}
|
||||
else {
|
||||
// Copy it
|
||||
arr = lib.map(val, function(v) { return v; });
|
||||
}
|
||||
|
||||
arr.reverse();
|
||||
|
||||
if(lib.isString(val)) {
|
||||
return arr.join('');
|
||||
}
|
||||
return arr;
|
||||
},
|
||||
|
||||
round: function(val, precision, method) {
|
||||
method = method || 'common';
|
||||
},
|
||||
precision = precision || 0;
|
||||
var factor = Math.pow(10, precision);
|
||||
var rounder;
|
||||
|
||||
safe: function(str) {
|
||||
if(method == 'ceil') {
|
||||
rounder = Math.ceil;
|
||||
}
|
||||
else if(method == 'floor') {
|
||||
rounder = Math.floor;
|
||||
}
|
||||
else {
|
||||
rounder = Math.round;
|
||||
}
|
||||
|
||||
return rounder(val * factor) / factor;
|
||||
},
|
||||
|
||||
slice: function(arr, slices, fillWith) {
|
||||
var sliceLength = Math.floor(arr.length / slices);
|
||||
var extra = arr.length % slices;
|
||||
var offset = 0;
|
||||
var res = [];
|
||||
|
||||
for(var i=0; i<slices; i++) {
|
||||
var start = offset + i * sliceLength;
|
||||
if(i < extra) {
|
||||
offset++;
|
||||
}
|
||||
var end = offset + (i + 1) * sliceLength;
|
||||
|
||||
var slice = arr.slice(start, end);
|
||||
if(fillWith && i >= extra) {
|
||||
slice.push(fillWith);
|
||||
}
|
||||
res.push(slice);
|
||||
}
|
||||
|
||||
return res;
|
||||
},
|
||||
|
||||
sort: function(arr, reverse, caseSens, attr) {
|
||||
// Copy it
|
||||
arr = lib.map(arr, function(v) { return v; });
|
||||
|
||||
arr.sort(function(a, b) {
|
||||
var x, y;
|
||||
|
||||
if(attr) {
|
||||
x = a[attr];
|
||||
y = b[attr];
|
||||
}
|
||||
else {
|
||||
x = a;
|
||||
y = b;
|
||||
}
|
||||
|
||||
if(!caseSens && lib.isString(x) && lib.isString(y)) {
|
||||
x = x.toLowerCase();
|
||||
y = y.toLowerCase();
|
||||
}
|
||||
|
||||
if(x < y) {
|
||||
return reverse ? 1 : -1;
|
||||
}
|
||||
else if(x > y) {
|
||||
return reverse ? -1: 1;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
return arr;
|
||||
},
|
||||
|
||||
string: function(obj) {
|
||||
return obj.toString();
|
||||
},
|
||||
|
||||
striptags: function(val) {
|
||||
},
|
||||
|
||||
sum: function(arr, attr, start) {
|
||||
},
|
||||
|
||||
title: function(str) {
|
||||
return str.toUpperCase();
|
||||
},
|
||||
|
||||
trim: function(str) {
|
||||
|
||||
},
|
||||
|
||||
truncate: function(str, length, killWords, end) {
|
||||
length = length || 255;
|
||||
end = end || '...';
|
||||
return str.replace(/^\s*|\s*$/g, '');
|
||||
},
|
||||
|
||||
upper: function(str) {
|
||||
return str.toUpperCase();
|
||||
},
|
||||
|
||||
urlize: function(str, trimLimit, noFollow) {
|
||||
},
|
||||
|
||||
wordcount: function(str) {
|
||||
},
|
||||
|
||||
wordwrap: function(str, width, breakWords) {
|
||||
width = width || 79;
|
||||
},
|
||||
|
||||
xmlattr: function(dict, autospace) {
|
||||
return str.match(/\w+/g).length;
|
||||
},
|
||||
|
||||
float: function(val, def) {
|
||||
return parseFloat(val) || def;
|
||||
},
|
||||
|
||||
|
||||
int: function(val, def) {
|
||||
return parseInt(val) || def;
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -2315,22 +2513,24 @@ modules['filters'] = filters;
|
|||
var Object = modules["object"];
|
||||
|
||||
var HttpLoader = Object.extend({
|
||||
init: function(baseURL) {
|
||||
init: function(baseURL, neverUpdate) {
|
||||
console.log("[nunjucks] Warning: only use HttpLoader in " +
|
||||
"development. Otherwise precompile your templates.");
|
||||
this.baseURL = baseURL || '';
|
||||
this.neverUpdate = neverUpdate;
|
||||
},
|
||||
|
||||
getSource: function(name) {
|
||||
var src = this.fetch(this.baseURL + '/' + name);
|
||||
var _this = this;
|
||||
|
||||
if(!src) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
return { src: src,
|
||||
path: name,
|
||||
upToDate: function() { return false; }};
|
||||
upToDate: function() { return _this.neverUpdate; }};
|
||||
},
|
||||
|
||||
fetch: function(url) {
|
||||
|
@ -2369,26 +2569,31 @@ else {
|
|||
|
||||
var lib = modules["lib"];
|
||||
var Object = modules["object"];
|
||||
var lexer = modules["lexer"];
|
||||
var compiler = modules["compiler"];
|
||||
var builtin_filters = modules["filters"];
|
||||
var builtin_loaders = modules["loaders"];
|
||||
var Frame = modules["runtime"].Frame;
|
||||
|
||||
var Environment = Object.extend({
|
||||
init: function(loaders) {
|
||||
init: function(loaders, tags) {
|
||||
if(!loaders) {
|
||||
// The filesystem loader is only available client-side
|
||||
if(builtin_loaders.FileSystemLoader) {
|
||||
this.loaders = [new builtin_loaders.FileSystemLoader()];
|
||||
}
|
||||
else {
|
||||
this.loaders = [new builtin_loaders.HttpLoader()];
|
||||
this.loaders = [new builtin_loaders.HttpLoader('/views')];
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.loaders = lib.isArray(loaders) ? loaders : [loaders];
|
||||
}
|
||||
|
||||
if(tags) {
|
||||
lexer.setTags(tags);
|
||||
}
|
||||
|
||||
this.filters = builtin_filters;
|
||||
this.cache = {};
|
||||
},
|
||||
|
@ -2457,9 +2662,13 @@ var Environment = Object.extend({
|
|||
|
||||
context = lib.extend(context, ctx);
|
||||
|
||||
var res = env.getTemplate(name).render(ctx);
|
||||
var res = env.render(name, ctx);
|
||||
k(null, res);
|
||||
};
|
||||
},
|
||||
|
||||
render: function(name, ctx) {
|
||||
return this.getTemplate(name).render(ctx);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -2477,6 +2686,10 @@ var Context = Object.extend({
|
|||
return this.ctx[name];
|
||||
},
|
||||
|
||||
setVariable: function(name, val) {
|
||||
this.ctx[name] = val;
|
||||
},
|
||||
|
||||
getVariables: function() {
|
||||
return this.ctx;
|
||||
},
|
||||
|
@ -2583,10 +2796,13 @@ var Template = Object.extend({
|
|||
});
|
||||
|
||||
// var fs = modules["fs"];
|
||||
// var env = new Environment();
|
||||
// console.log(compiler.compile(fs.readFileSync('test.html', 'utf-8')));
|
||||
// //var src = fs.readFileSync('test.html', 'utf-8');
|
||||
// var src = "{% for i in [1,2,3] %}{% include 'item.html' %}{% endfor %}";
|
||||
|
||||
// var tmpl = env.getTemplate('test.html');
|
||||
// var env = new Environment();
|
||||
// console.log(compiler.compile(src));
|
||||
|
||||
// var tmpl = new Template(src, env);
|
||||
// console.log("OUTPUT ---");
|
||||
// console.log(tmpl.render({ username: "James" }));
|
||||
|
||||
|
@ -2606,7 +2822,13 @@ window.nunjucks = {};
|
|||
window.nunjucks.Environment = env.Environment;
|
||||
window.nunjucks.Template = env.Template;
|
||||
|
||||
window.nunjucks.loaders = loaders;
|
||||
if(loaders.FileSystemLoader) {
|
||||
window.nunjucks.FileSystemLoader = loaders.FileSystemLoader;
|
||||
}
|
||||
else {
|
||||
window.nunjucks.HttpLoader = loaders.HttpLoader;
|
||||
}
|
||||
|
||||
window.nunjucks.compiler = compiler;
|
||||
window.nunjucks.parser = parser;
|
||||
window.nunjucks.lexer = lexer;
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -79,7 +79,7 @@ exports.isString = function(obj) {
|
|||
|
||||
exports.isObject = function(obj) {
|
||||
return obj === Object(obj);
|
||||
}
|
||||
};
|
||||
|
||||
exports.groupBy = function(obj, val) {
|
||||
var result = {};
|
||||
|
@ -139,7 +139,7 @@ exports.map = function(obj, func) {
|
|||
}
|
||||
|
||||
for(var i=0; i<obj.length; i++) {
|
||||
results[results.length] = func(value, i);
|
||||
results[results.length] = func(obj[i], i);
|
||||
}
|
||||
|
||||
if(obj.length === +obj.length) {
|
||||
|
@ -206,10 +206,6 @@ var filters = {
|
|||
return val ? val : def;
|
||||
},
|
||||
|
||||
dictsort: function(dict, caseSens, by) {
|
||||
by = by || 'key';
|
||||
},
|
||||
|
||||
escape: function(str) {
|
||||
return str.replace(/&/g, '&')
|
||||
.replace(/"/g, '"')
|
||||
|
@ -218,34 +214,46 @@ var filters = {
|
|||
.replace(/>/g, '>');
|
||||
},
|
||||
|
||||
filesizeformat: function(val, binary) {
|
||||
},
|
||||
|
||||
first: function(arr) {
|
||||
return arr[0];
|
||||
},
|
||||
|
||||
forceescape: function(val) {
|
||||
|
||||
},
|
||||
|
||||
format: function(str /*, vals */) {
|
||||
var args = lib.toArray(arguments).slice(1);
|
||||
},
|
||||
|
||||
groupby: function(arr, attr) {
|
||||
return lib.groupBy(arr, attr);
|
||||
},
|
||||
|
||||
indent: function(str, width, indentfirst) {
|
||||
width = width || 4;
|
||||
var res = '';
|
||||
var lines = str.split('\n');
|
||||
var sp = lib.repeat(' ', width);
|
||||
|
||||
for(var i=0; i<lines.length; i++) {
|
||||
if(i == 0 && !indentfirst) {
|
||||
res += lines[i] + '\n';
|
||||
}
|
||||
else {
|
||||
res += sp + lines[i] + '\n';
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
},
|
||||
|
||||
join: function(val, d, attr) {
|
||||
d = d || '';
|
||||
join: function(arr, del, attr) {
|
||||
del = del || '';
|
||||
|
||||
if(attr) {
|
||||
arr = lib.map(arr, function(v) {
|
||||
return v[attr];
|
||||
});
|
||||
}
|
||||
|
||||
return arr.join(del);
|
||||
},
|
||||
|
||||
last: function(arr) {
|
||||
return arr[arr.length-1];
|
||||
},
|
||||
|
||||
length: function(arr) {
|
||||
|
@ -253,80 +261,183 @@ var filters = {
|
|||
},
|
||||
|
||||
list: function(val) {
|
||||
if(lib.isString(val)) {
|
||||
return val.split('');
|
||||
}
|
||||
else if(lib.isObject(val)) {
|
||||
var keys = [];
|
||||
|
||||
if(Object.keys) {
|
||||
keys = Object.keys(val);
|
||||
}
|
||||
else {
|
||||
for(var k in val) {
|
||||
keys.push(k);
|
||||
}
|
||||
}
|
||||
|
||||
return lib.map(keys, function(k) {
|
||||
return { key: k,
|
||||
value: val[k] };
|
||||
});
|
||||
}
|
||||
else {
|
||||
throw new Error("list: type not iterable");
|
||||
}
|
||||
},
|
||||
|
||||
lower: function(str) {
|
||||
},
|
||||
|
||||
pprint: function(val) {
|
||||
return str.toLowerCase();
|
||||
},
|
||||
|
||||
random: function(arr) {
|
||||
var i = Math.floor(Math.random() * arr.length);
|
||||
if(i == arr.length) {
|
||||
i--;
|
||||
}
|
||||
|
||||
return arr[i];
|
||||
},
|
||||
|
||||
replace: function(str, old, new_, count) {
|
||||
replace: function(str, old, new_, maxCount) {
|
||||
var res = str;
|
||||
var last = res;
|
||||
var count = 1;
|
||||
res = res.replace(old, new_);
|
||||
|
||||
while(last != res) {
|
||||
if(count >= maxCount) {
|
||||
break;
|
||||
}
|
||||
|
||||
last = res;
|
||||
res = res.replace(old, new_);
|
||||
count++;
|
||||
}
|
||||
|
||||
return res;
|
||||
},
|
||||
|
||||
reverse: function(str) {
|
||||
reverse: function(val) {
|
||||
var arr;
|
||||
if(lib.isString(val)) {
|
||||
arr = filters.list(val);
|
||||
}
|
||||
else {
|
||||
// Copy it
|
||||
arr = lib.map(val, function(v) { return v; });
|
||||
}
|
||||
|
||||
arr.reverse();
|
||||
|
||||
if(lib.isString(val)) {
|
||||
return arr.join('');
|
||||
}
|
||||
return arr;
|
||||
},
|
||||
|
||||
round: function(val, precision, method) {
|
||||
method = method || 'common';
|
||||
},
|
||||
precision = precision || 0;
|
||||
var factor = Math.pow(10, precision);
|
||||
var rounder;
|
||||
|
||||
safe: function(str) {
|
||||
if(method == 'ceil') {
|
||||
rounder = Math.ceil;
|
||||
}
|
||||
else if(method == 'floor') {
|
||||
rounder = Math.floor;
|
||||
}
|
||||
else {
|
||||
rounder = Math.round;
|
||||
}
|
||||
|
||||
return rounder(val * factor) / factor;
|
||||
},
|
||||
|
||||
slice: function(arr, slices, fillWith) {
|
||||
var sliceLength = Math.floor(arr.length / slices);
|
||||
var extra = arr.length % slices;
|
||||
var offset = 0;
|
||||
var res = [];
|
||||
|
||||
for(var i=0; i<slices; i++) {
|
||||
var start = offset + i * sliceLength;
|
||||
if(i < extra) {
|
||||
offset++;
|
||||
}
|
||||
var end = offset + (i + 1) * sliceLength;
|
||||
|
||||
var slice = arr.slice(start, end);
|
||||
if(fillWith && i >= extra) {
|
||||
slice.push(fillWith);
|
||||
}
|
||||
res.push(slice);
|
||||
}
|
||||
|
||||
return res;
|
||||
},
|
||||
|
||||
sort: function(arr, reverse, caseSens, attr) {
|
||||
// Copy it
|
||||
arr = lib.map(arr, function(v) { return v; });
|
||||
|
||||
arr.sort(function(a, b) {
|
||||
var x, y;
|
||||
|
||||
if(attr) {
|
||||
x = a[attr];
|
||||
y = b[attr];
|
||||
}
|
||||
else {
|
||||
x = a;
|
||||
y = b;
|
||||
}
|
||||
|
||||
if(!caseSens && lib.isString(x) && lib.isString(y)) {
|
||||
x = x.toLowerCase();
|
||||
y = y.toLowerCase();
|
||||
}
|
||||
|
||||
if(x < y) {
|
||||
return reverse ? 1 : -1;
|
||||
}
|
||||
else if(x > y) {
|
||||
return reverse ? -1: 1;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
return arr;
|
||||
},
|
||||
|
||||
string: function(obj) {
|
||||
return obj.toString();
|
||||
},
|
||||
|
||||
striptags: function(val) {
|
||||
},
|
||||
|
||||
sum: function(arr, attr, start) {
|
||||
},
|
||||
|
||||
title: function(str) {
|
||||
return str.toUpperCase();
|
||||
},
|
||||
|
||||
trim: function(str) {
|
||||
|
||||
},
|
||||
|
||||
truncate: function(str, length, killWords, end) {
|
||||
length = length || 255;
|
||||
end = end || '...';
|
||||
return str.replace(/^\s*|\s*$/g, '');
|
||||
},
|
||||
|
||||
upper: function(str) {
|
||||
return str.toUpperCase();
|
||||
},
|
||||
|
||||
urlize: function(str, trimLimit, noFollow) {
|
||||
},
|
||||
|
||||
wordcount: function(str) {
|
||||
},
|
||||
|
||||
wordwrap: function(str, width, breakWords) {
|
||||
width = width || 79;
|
||||
},
|
||||
|
||||
xmlattr: function(dict, autospace) {
|
||||
return str.match(/\w+/g).length;
|
||||
},
|
||||
|
||||
float: function(val, def) {
|
||||
return parseFloat(val) || def;
|
||||
},
|
||||
|
||||
|
||||
int: function(val, def) {
|
||||
return parseInt(val) || def;
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -349,8 +460,22 @@ var Frame = Object.extend({
|
|||
this.parent = parent;
|
||||
},
|
||||
|
||||
addVariable: function(name, id) {
|
||||
this.variables[name] = id;
|
||||
set: function(name, val) {
|
||||
// Allow variables with dots by automatically creating the
|
||||
// nested structure
|
||||
var parts = name.split('.');
|
||||
var obj = this.variables;
|
||||
|
||||
for(var i=0; i<parts.length - 1; i++) {
|
||||
var id = parts[i];
|
||||
|
||||
if(!obj[id]) {
|
||||
obj[id] = {};
|
||||
}
|
||||
obj = obj[id];
|
||||
}
|
||||
|
||||
obj[parts[parts.length - 1]] = val;
|
||||
},
|
||||
|
||||
lookup: function(name) {
|
||||
|
@ -375,26 +500,31 @@ modules['runtime'] = {
|
|||
|
||||
var lib = modules["lib"];
|
||||
var Object = modules["object"];
|
||||
var lexer = modules["lexer"];
|
||||
var compiler = modules["compiler"];
|
||||
var builtin_filters = modules["filters"];
|
||||
var builtin_loaders = modules["loaders"];
|
||||
var Frame = modules["runtime"].Frame;
|
||||
|
||||
var Environment = Object.extend({
|
||||
init: function(loaders) {
|
||||
init: function(loaders, tags) {
|
||||
if(!loaders) {
|
||||
// The filesystem loader is only available client-side
|
||||
if(builtin_loaders.FileSystemLoader) {
|
||||
this.loaders = [new builtin_loaders.FileSystemLoader()];
|
||||
}
|
||||
else {
|
||||
this.loaders = [new builtin_loaders.HttpLoader()];
|
||||
this.loaders = [new builtin_loaders.HttpLoader('/views')];
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.loaders = lib.isArray(loaders) ? loaders : [loaders];
|
||||
}
|
||||
|
||||
if(tags) {
|
||||
lexer.setTags(tags);
|
||||
}
|
||||
|
||||
this.filters = builtin_filters;
|
||||
this.cache = {};
|
||||
},
|
||||
|
@ -463,9 +593,13 @@ var Environment = Object.extend({
|
|||
|
||||
context = lib.extend(context, ctx);
|
||||
|
||||
var res = env.getTemplate(name).render(ctx);
|
||||
var res = env.render(name, ctx);
|
||||
k(null, res);
|
||||
};
|
||||
},
|
||||
|
||||
render: function(name, ctx) {
|
||||
return this.getTemplate(name).render(ctx);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -483,6 +617,10 @@ var Context = Object.extend({
|
|||
return this.ctx[name];
|
||||
},
|
||||
|
||||
setVariable: function(name, val) {
|
||||
this.ctx[name] = val;
|
||||
},
|
||||
|
||||
getVariables: function() {
|
||||
return this.ctx;
|
||||
},
|
||||
|
@ -589,10 +727,13 @@ var Template = Object.extend({
|
|||
});
|
||||
|
||||
// var fs = modules["fs"];
|
||||
// var env = new Environment();
|
||||
// console.log(compiler.compile(fs.readFileSync('test.html', 'utf-8')));
|
||||
// //var src = fs.readFileSync('test.html', 'utf-8');
|
||||
// var src = "{% for i in [1,2,3] %}{% include 'item.html' %}{% endfor %}";
|
||||
|
||||
// var tmpl = env.getTemplate('test.html');
|
||||
// var env = new Environment();
|
||||
// console.log(compiler.compile(src));
|
||||
|
||||
// var tmpl = new Template(src, env);
|
||||
// console.log("OUTPUT ---");
|
||||
// console.log(tmpl.render({ username: "James" }));
|
||||
|
||||
|
@ -612,7 +753,13 @@ window.nunjucks = {};
|
|||
window.nunjucks.Environment = env.Environment;
|
||||
window.nunjucks.Template = env.Template;
|
||||
|
||||
window.nunjucks.loaders = loaders;
|
||||
if(loaders.FileSystemLoader) {
|
||||
window.nunjucks.FileSystemLoader = loaders.FileSystemLoader;
|
||||
}
|
||||
else {
|
||||
window.nunjucks.HttpLoader = loaders.HttpLoader;
|
||||
}
|
||||
|
||||
window.nunjucks.compiler = compiler;
|
||||
window.nunjucks.parser = parser;
|
||||
window.nunjucks.lexer = lexer;
|
||||
|
|
8
index.js
8
index.js
|
@ -9,7 +9,13 @@ module.exports = {};
|
|||
module.exports.Environment = env.Environment;
|
||||
module.exports.Template = env.Template;
|
||||
|
||||
module.exports.loaders = loaders;
|
||||
if(loaders.FileSystemLoader) {
|
||||
module.exports.FileSystemLoader = loaders.FileSystemLoader;
|
||||
}
|
||||
else {
|
||||
module.exports.HttpLoader = loaders.HttpLoader;
|
||||
}
|
||||
|
||||
module.exports.compiler = compiler;
|
||||
module.exports.parser = parser;
|
||||
module.exports.lexer = lexer;
|
|
@ -15,6 +15,9 @@
|
|||
"scripts": {
|
||||
"test": "make test"
|
||||
},
|
||||
"bin": {
|
||||
"nunjucks-precompile": "./bin/precompile"
|
||||
},
|
||||
"main": "index"
|
||||
}
|
||||
|
||||
|
|
|
@ -1,26 +1,31 @@
|
|||
|
||||
var lib = require('./lib');
|
||||
var Object = require('./object');
|
||||
var lexer = require('./lexer');
|
||||
var compiler = require('./compiler');
|
||||
var builtin_filters = require('./filters');
|
||||
var builtin_loaders = require('./loaders');
|
||||
var Frame = require('./runtime').Frame;
|
||||
|
||||
var Environment = Object.extend({
|
||||
init: function(loaders) {
|
||||
init: function(loaders, tags) {
|
||||
if(!loaders) {
|
||||
// The filesystem loader is only available client-side
|
||||
if(builtin_loaders.FileSystemLoader) {
|
||||
this.loaders = [new builtin_loaders.FileSystemLoader()];
|
||||
}
|
||||
else {
|
||||
this.loaders = [new builtin_loaders.HttpLoader()];
|
||||
this.loaders = [new builtin_loaders.HttpLoader('/views')];
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.loaders = lib.isArray(loaders) ? loaders : [loaders];
|
||||
}
|
||||
|
||||
if(tags) {
|
||||
lexer.setTags(tags);
|
||||
}
|
||||
|
||||
this.filters = builtin_filters;
|
||||
this.cache = {};
|
||||
},
|
||||
|
@ -89,9 +94,13 @@ var Environment = Object.extend({
|
|||
|
||||
context = lib.extend(context, ctx);
|
||||
|
||||
var res = env.getTemplate(name).render(ctx);
|
||||
var res = env.render(name, ctx);
|
||||
k(null, res);
|
||||
};
|
||||
},
|
||||
|
||||
render: function(name, ctx) {
|
||||
return this.getTemplate(name).render(ctx);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -220,7 +229,7 @@ var Template = Object.extend({
|
|||
|
||||
// var fs = require('fs');
|
||||
// //var src = fs.readFileSync('test.html', 'utf-8');
|
||||
// var src = "{% for k, v in {one:1, two:2} %}{{ loop.first }}{% endfor %}";
|
||||
// var src = "{% for i in [1,2,3] %}{% include 'item.html' %}{% endfor %}";
|
||||
|
||||
// var env = new Environment();
|
||||
// console.log(compiler.compile(src));
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
{{ i }}
|
|
@ -376,6 +376,15 @@ module.exports = {
|
|||
return new Tokenizer(src);
|
||||
},
|
||||
|
||||
setTags: function(tags) {
|
||||
BLOCK_START = tags.blockStart || BLOCK_START;
|
||||
BLOCK_END = tags.blockEnd || BLOCK_END;
|
||||
VARIABLE_START = tags.variableStart || VARIABLE_START;
|
||||
VARIABLE_END = tags.variableEnd || VARIABLE_END;
|
||||
COMMENT_START = tags.commentStart || COMMENT_START;
|
||||
COMMENT_END = tags.commentEnd || COMMENT_END;
|
||||
},
|
||||
|
||||
TOKEN_STRING: TOKEN_STRING,
|
||||
TOKEN_WHITESPACE: TOKEN_WHITESPACE,
|
||||
TOKEN_DATA: TOKEN_DATA,
|
||||
|
|
|
@ -2,22 +2,24 @@
|
|||
var Object = require('./object');
|
||||
|
||||
var HttpLoader = Object.extend({
|
||||
init: function(baseURL) {
|
||||
init: function(baseURL, neverUpdate) {
|
||||
console.log("[nunjucks] Warning: only use HttpLoader in " +
|
||||
"development. Otherwise precompile your templates.");
|
||||
this.baseURL = baseURL || '';
|
||||
this.neverUpdate = neverUpdate;
|
||||
},
|
||||
|
||||
getSource: function(name) {
|
||||
var src = this.fetch(this.baseURL + '/' + name);
|
||||
var _this = this;
|
||||
|
||||
if(!src) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
return { src: src,
|
||||
path: name,
|
||||
upToDate: function() { return false; }};
|
||||
upToDate: function() { return _this.neverUpdate; }};
|
||||
},
|
||||
|
||||
fetch: function(url) {
|
||||
|
|
|
@ -5,7 +5,7 @@ var express = require('express');
|
|||
|
||||
var app = express();
|
||||
|
||||
var e = new env.Environment(new loaders.FileSystemLoader('templates'));
|
||||
var e = new env.Environment(new loaders.FileSystemLoader('views'));
|
||||
e.express(app);
|
||||
|
||||
app.use(express.static(__dirname));
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -79,11 +79,11 @@ exports.isString = function(obj) {
|
|||
|
||||
exports.isObject = function(obj) {
|
||||
return obj === Object(obj);
|
||||
}
|
||||
};
|
||||
|
||||
exports.groupBy = function(obj, val) {
|
||||
var result = {};
|
||||
var iterator = _.isFunction(val) ? val : function(obj) { return obj[val]; };
|
||||
var iterator = exports.isFunction(val) ? val : function(obj) { return obj[val]; };
|
||||
for(var i=0; i<obj.length; i++) {
|
||||
var value = obj[i];
|
||||
var key = iterator(value, i);
|
||||
|
@ -139,7 +139,7 @@ exports.map = function(obj, func) {
|
|||
}
|
||||
|
||||
for(var i=0; i<obj.length; i++) {
|
||||
results[results.length] = func(value, i);
|
||||
results[results.length] = func(obj[i], i);
|
||||
}
|
||||
|
||||
if(obj.length === +obj.length) {
|
||||
|
@ -206,10 +206,6 @@ var filters = {
|
|||
return val ? val : def;
|
||||
},
|
||||
|
||||
dictsort: function(dict, caseSens, by) {
|
||||
by = by || 'key';
|
||||
},
|
||||
|
||||
escape: function(str) {
|
||||
return str.replace(/&/g, '&')
|
||||
.replace(/"/g, '"')
|
||||
|
@ -218,34 +214,46 @@ var filters = {
|
|||
.replace(/>/g, '>');
|
||||
},
|
||||
|
||||
filesizeformat: function(val, binary) {
|
||||
},
|
||||
|
||||
first: function(arr) {
|
||||
return arr[0];
|
||||
},
|
||||
|
||||
forceescape: function(val) {
|
||||
|
||||
},
|
||||
|
||||
format: function(str /*, vals */) {
|
||||
var args = lib.toArray(arguments).slice(1);
|
||||
},
|
||||
|
||||
groupby: function(arr, attr) {
|
||||
return lib.groupBy(arr, attr);
|
||||
},
|
||||
|
||||
indent: function(str, width, indentfirst) {
|
||||
width = width || 4;
|
||||
var res = '';
|
||||
var lines = str.split('\n');
|
||||
var sp = lib.repeat(' ', width);
|
||||
|
||||
for(var i=0; i<lines.length; i++) {
|
||||
if(i == 0 && !indentfirst) {
|
||||
res += lines[i] + '\n';
|
||||
}
|
||||
else {
|
||||
res += sp + lines[i] + '\n';
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
},
|
||||
|
||||
join: function(val, d, attr) {
|
||||
d = d || '';
|
||||
join: function(arr, del, attr) {
|
||||
del = del || '';
|
||||
|
||||
if(attr) {
|
||||
arr = lib.map(arr, function(v) {
|
||||
return v[attr];
|
||||
});
|
||||
}
|
||||
|
||||
return arr.join(del);
|
||||
},
|
||||
|
||||
last: function(arr) {
|
||||
return arr[arr.length-1];
|
||||
},
|
||||
|
||||
length: function(arr) {
|
||||
|
@ -253,80 +261,183 @@ var filters = {
|
|||
},
|
||||
|
||||
list: function(val) {
|
||||
if(lib.isString(val)) {
|
||||
return val.split('');
|
||||
}
|
||||
else if(lib.isObject(val)) {
|
||||
var keys = [];
|
||||
|
||||
if(Object.keys) {
|
||||
keys = Object.keys(val);
|
||||
}
|
||||
else {
|
||||
for(var k in val) {
|
||||
keys.push(k);
|
||||
}
|
||||
}
|
||||
|
||||
return lib.map(keys, function(k) {
|
||||
return { key: k,
|
||||
value: val[k] };
|
||||
});
|
||||
}
|
||||
else {
|
||||
throw new Error("list: type not iterable");
|
||||
}
|
||||
},
|
||||
|
||||
lower: function(str) {
|
||||
},
|
||||
|
||||
pprint: function(val) {
|
||||
return str.toLowerCase();
|
||||
},
|
||||
|
||||
random: function(arr) {
|
||||
var i = Math.floor(Math.random() * arr.length);
|
||||
if(i == arr.length) {
|
||||
i--;
|
||||
}
|
||||
|
||||
return arr[i];
|
||||
},
|
||||
|
||||
replace: function(str, old, new_, count) {
|
||||
replace: function(str, old, new_, maxCount) {
|
||||
var res = str;
|
||||
var last = res;
|
||||
var count = 1;
|
||||
res = res.replace(old, new_);
|
||||
|
||||
while(last != res) {
|
||||
if(count >= maxCount) {
|
||||
break;
|
||||
}
|
||||
|
||||
last = res;
|
||||
res = res.replace(old, new_);
|
||||
count++;
|
||||
}
|
||||
|
||||
return res;
|
||||
},
|
||||
|
||||
reverse: function(str) {
|
||||
reverse: function(val) {
|
||||
var arr;
|
||||
if(lib.isString(val)) {
|
||||
arr = filters.list(val);
|
||||
}
|
||||
else {
|
||||
// Copy it
|
||||
arr = lib.map(val, function(v) { return v; });
|
||||
}
|
||||
|
||||
arr.reverse();
|
||||
|
||||
if(lib.isString(val)) {
|
||||
return arr.join('');
|
||||
}
|
||||
return arr;
|
||||
},
|
||||
|
||||
round: function(val, precision, method) {
|
||||
method = method || 'common';
|
||||
},
|
||||
precision = precision || 0;
|
||||
var factor = Math.pow(10, precision);
|
||||
var rounder;
|
||||
|
||||
safe: function(str) {
|
||||
if(method == 'ceil') {
|
||||
rounder = Math.ceil;
|
||||
}
|
||||
else if(method == 'floor') {
|
||||
rounder = Math.floor;
|
||||
}
|
||||
else {
|
||||
rounder = Math.round;
|
||||
}
|
||||
|
||||
return rounder(val * factor) / factor;
|
||||
},
|
||||
|
||||
slice: function(arr, slices, fillWith) {
|
||||
var sliceLength = Math.floor(arr.length / slices);
|
||||
var extra = arr.length % slices;
|
||||
var offset = 0;
|
||||
var res = [];
|
||||
|
||||
for(var i=0; i<slices; i++) {
|
||||
var start = offset + i * sliceLength;
|
||||
if(i < extra) {
|
||||
offset++;
|
||||
}
|
||||
var end = offset + (i + 1) * sliceLength;
|
||||
|
||||
var slice = arr.slice(start, end);
|
||||
if(fillWith && i >= extra) {
|
||||
slice.push(fillWith);
|
||||
}
|
||||
res.push(slice);
|
||||
}
|
||||
|
||||
return res;
|
||||
},
|
||||
|
||||
sort: function(arr, reverse, caseSens, attr) {
|
||||
// Copy it
|
||||
arr = lib.map(arr, function(v) { return v; });
|
||||
|
||||
arr.sort(function(a, b) {
|
||||
var x, y;
|
||||
|
||||
if(attr) {
|
||||
x = a[attr];
|
||||
y = b[attr];
|
||||
}
|
||||
else {
|
||||
x = a;
|
||||
y = b;
|
||||
}
|
||||
|
||||
if(!caseSens && lib.isString(x) && lib.isString(y)) {
|
||||
x = x.toLowerCase();
|
||||
y = y.toLowerCase();
|
||||
}
|
||||
|
||||
if(x < y) {
|
||||
return reverse ? 1 : -1;
|
||||
}
|
||||
else if(x > y) {
|
||||
return reverse ? -1: 1;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
return arr;
|
||||
},
|
||||
|
||||
string: function(obj) {
|
||||
return obj.toString();
|
||||
},
|
||||
|
||||
striptags: function(val) {
|
||||
},
|
||||
|
||||
sum: function(arr, attr, start) {
|
||||
},
|
||||
|
||||
title: function(str) {
|
||||
return str.toUpperCase();
|
||||
},
|
||||
|
||||
trim: function(str) {
|
||||
|
||||
},
|
||||
|
||||
truncate: function(str, length, killWords, end) {
|
||||
length = length || 255;
|
||||
end = end || '...';
|
||||
return str.replace(/^\s*|\s*$/g, '');
|
||||
},
|
||||
|
||||
upper: function(str) {
|
||||
return str.toUpperCase();
|
||||
},
|
||||
|
||||
urlize: function(str, trimLimit, noFollow) {
|
||||
},
|
||||
|
||||
wordcount: function(str) {
|
||||
},
|
||||
|
||||
wordwrap: function(str, width, breakWords) {
|
||||
width = width || 79;
|
||||
},
|
||||
|
||||
xmlattr: function(dict, autospace) {
|
||||
return str.match(/\w+/g).length;
|
||||
},
|
||||
|
||||
float: function(val, def) {
|
||||
return parseFloat(val) || def;
|
||||
},
|
||||
|
||||
|
||||
int: function(val, def) {
|
||||
return parseInt(val) || def;
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -338,27 +449,82 @@ modules['filters'] = filters;
|
|||
})();
|
||||
(function() {
|
||||
|
||||
var Object = modules["object"];
|
||||
|
||||
// Frames keep track of scoping both at compile-time and run-time so
|
||||
// we know how to access variables. Block tags can introduce special
|
||||
// variables, for example.
|
||||
var Frame = Object.extend({
|
||||
init: function(parent) {
|
||||
this.variables = {};
|
||||
this.parent = parent;
|
||||
},
|
||||
|
||||
set: function(name, val) {
|
||||
// Allow variables with dots by automatically creating the
|
||||
// nested structure
|
||||
var parts = name.split('.');
|
||||
var obj = this.variables;
|
||||
|
||||
for(var i=0; i<parts.length - 1; i++) {
|
||||
var id = parts[i];
|
||||
|
||||
if(!obj[id]) {
|
||||
obj[id] = {};
|
||||
}
|
||||
obj = obj[id];
|
||||
}
|
||||
|
||||
obj[parts[parts.length - 1]] = val;
|
||||
},
|
||||
|
||||
lookup: function(name) {
|
||||
var p = this.parent;
|
||||
return this.variables[name] || (p && p.lookup(name));
|
||||
},
|
||||
|
||||
push: function() {
|
||||
return new Frame(this);
|
||||
},
|
||||
|
||||
pop: function() {
|
||||
return this.parent;
|
||||
}
|
||||
});
|
||||
|
||||
modules['runtime'] = {
|
||||
Frame: Frame
|
||||
};
|
||||
})();
|
||||
(function() {
|
||||
|
||||
var lib = modules["lib"];
|
||||
var Object = modules["object"];
|
||||
var lexer = modules["lexer"];
|
||||
var compiler = modules["compiler"];
|
||||
var builtin_filters = modules["filters"];
|
||||
var builtin_loaders = modules["loaders"];
|
||||
var Frame = modules["runtime"].Frame;
|
||||
|
||||
var Environment = Object.extend({
|
||||
init: function(loaders) {
|
||||
init: function(loaders, tags) {
|
||||
if(!loaders) {
|
||||
// The filesystem loader is only available client-side
|
||||
if(builtin_loaders.FileSystemLoader) {
|
||||
this.loaders = [new builtin_loaders.FileSystemLoader()];
|
||||
}
|
||||
else {
|
||||
this.loaders = [new builtin_loaders.HttpLoader()];
|
||||
this.loaders = [new builtin_loaders.HttpLoader('/views')];
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.loaders = lib.isArray(loaders) ? loaders : [loaders];
|
||||
}
|
||||
|
||||
if(tags) {
|
||||
lexer.setTags(tags);
|
||||
}
|
||||
|
||||
this.filters = builtin_filters;
|
||||
this.cache = {};
|
||||
},
|
||||
|
@ -427,9 +593,13 @@ var Environment = Object.extend({
|
|||
|
||||
context = lib.extend(context, ctx);
|
||||
|
||||
var res = env.getTemplate(name).render(ctx);
|
||||
var res = env.render(name, ctx);
|
||||
k(null, res);
|
||||
};
|
||||
},
|
||||
|
||||
render: function(name, ctx) {
|
||||
return this.getTemplate(name).render(ctx);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -444,12 +614,13 @@ var Context = Object.extend({
|
|||
},
|
||||
|
||||
lookup: function(name) {
|
||||
if(!(name in this.ctx)) {
|
||||
return '';
|
||||
}
|
||||
return this.ctx[name];
|
||||
},
|
||||
|
||||
setVariable: function(name, val) {
|
||||
this.ctx[name] = val;
|
||||
},
|
||||
|
||||
getVariables: function() {
|
||||
return this.ctx;
|
||||
},
|
||||
|
@ -511,13 +682,15 @@ var Template = Object.extend({
|
|||
}
|
||||
},
|
||||
|
||||
render: function(ctx) {
|
||||
render: function(ctx, frame) {
|
||||
if(!this.compiled) {
|
||||
this._compile();
|
||||
}
|
||||
|
||||
var context = new Context(ctx || {}, this.blocks);
|
||||
return this.rootRenderFunc(this.env, context);
|
||||
return this.rootRenderFunc(this.env,
|
||||
context,
|
||||
frame || new Frame());
|
||||
},
|
||||
|
||||
isUpToDate: function() {
|
||||
|
@ -554,10 +727,13 @@ var Template = Object.extend({
|
|||
});
|
||||
|
||||
// var fs = modules["fs"];
|
||||
// var env = new Environment();
|
||||
// console.log(compiler.compile(fs.readFileSync('test.html', 'utf-8')));
|
||||
// //var src = fs.readFileSync('test.html', 'utf-8');
|
||||
// var src = "{% for i in [1,2,3] %}{% include 'item.html' %}{% endfor %}";
|
||||
|
||||
// var tmpl = env.getTemplate('test.html');
|
||||
// var env = new Environment();
|
||||
// console.log(compiler.compile(src));
|
||||
|
||||
// var tmpl = new Template(src, env);
|
||||
// console.log("OUTPUT ---");
|
||||
// console.log(tmpl.render({ username: "James" }));
|
||||
|
||||
|
@ -577,7 +753,13 @@ window.nunjucks = {};
|
|||
window.nunjucks.Environment = env.Environment;
|
||||
window.nunjucks.Template = env.Template;
|
||||
|
||||
window.nunjucks.loaders = loaders;
|
||||
if(loaders.FileSystemLoader) {
|
||||
window.nunjucks.FileSystemLoader = loaders.FileSystemLoader;
|
||||
}
|
||||
else {
|
||||
window.nunjucks.HttpLoader = loaders.HttpLoader;
|
||||
}
|
||||
|
||||
window.nunjucks.compiler = compiler;
|
||||
window.nunjucks.parser = parser;
|
||||
window.nunjucks.lexer = lexer;
|
||||
|
|
|
@ -14,8 +14,8 @@
|
|||
}
|
||||
</style>
|
||||
|
||||
<script src="/nunjucks.js"></script>
|
||||
<script src="/templates.js"></script>
|
||||
<script src="/nunjucks-dev.js"></script>
|
||||
<!-- <script src="/templates.js"></script> -->
|
||||
</head>
|
||||
<body>
|
||||
{% block content %}{% endblock %}
|
|
@ -467,6 +467,16 @@ describe('compiler', function() {
|
|||
s.should.equal('Hello world, James Long, how are you');
|
||||
});
|
||||
|
||||
it('should compile references', function() {
|
||||
var s = render('{{ foo.bar }}',
|
||||
{ foo: { bar: 'baz' }});
|
||||
s.should.equal('baz');
|
||||
|
||||
s = render('{{ foo["bar"] }}',
|
||||
{ foo: { bar: 'baz' }});
|
||||
s.should.equal('baz');
|
||||
});
|
||||
|
||||
it('should compile if blocks', function() {
|
||||
var tmpl = ('Give me some {% if hungry %}pizza' +
|
||||
'{% else %}water{% endif %}');
|
||||
|
|
Загрузка…
Ссылка в новой задаче