Просмотр исходного кода

Turn interpreter into a recursive descent parser.

tags/0.5.0-vsc
thegrandpoobah 16 лет назад
Родитель
Сommit
8053051cb6
2 измененных файлов: 84 добавлений и 69 удалений
  1. +83
    -68
      mustache.js
  2. +1
    -1
      test/unit.js

+ 83
- 68
mustache.js Просмотреть файл

@@ -13,45 +13,27 @@ var Mustache = function() {
escaped_otag: "\{\{",
escaped_ctag: "\}\}",
pragmas: {},
buffer: [],
pragmas_implemented: {
"IMPLICIT-ITERATOR": true
},
context: {},

render: function(template, context, partials, in_recursion) {
// reset buffer & set context
if(!in_recursion) {
this.context = context;
this.buffer = []; // TODO: make this non-lazy
}
render: function(template, context, partials) {
// reset context
this.context = context;

// fail fast
if(!this.includes("", template)) {
if(in_recursion) {
return template;
} else {
this.send(template);
return;
}
return template;
}

template = this.render_pragmas(template);
template = this.render_delimiter(template, context, partials);

var html = this.render_section(template, context, partials);
if(in_recursion) {
return this.render_tags(html, context, partials, in_recursion);
}

this.render_tags(html, context, partials, in_recursion);
return this.render_recursive(template, context, partials);
},

/*
Sends parsed lines
*/
send: function(line) {
this.buffer.push(line);
render_recursive: function(template, context, partials) {
return this.render_delimiter(template, context, partials);
},

/*
@@ -84,24 +66,38 @@ var Mustache = function() {

render_delimiter: function(template, context, partials) {
if(!this.includes("=", template)) {
return template;
return this.render_section(template, context, partials);
}
var that = this;
var regex = new RegExp(this.escaped_otag + "=(\\S+)\\s+(\\S+)=" + this.escaped_ctag + "\n?([\\s\\S]*)", "mg");
var regex = new RegExp("(" + this.escaped_otag + "=(?:\\S+)\\s+(?:\\S+)=" + this.escaped_ctag + "\\n*)", "mg");
var fragments = template.split(regex);
return template.replace(regex, function(match, newOpen, newClose, content) {
var old_otag = that.otag;
var old_ctag = that.ctag;
that.set_delimiters(newOpen, newClose);
var html = that.render(content, context, partials, true);
that.set_delimiters(old_otag, old_ctag);
return html;
});
var i, n;
var r = new RegExp(this.escaped_otag + "=(\\S+)\\s+(\\S+)=" + this.escaped_ctag);
var matches;
for (i=0, n=fragments.length; i<n; ++i) {
matches = fragments[i].match(r);
if(matches && matches.length===3) {
var old_otag = this.otag;
var old_ctag = this.ctag;
this.set_delimiters(matches[1], matches[2]);
fragments[i] = this.render_delimiter(fragments.slice(i+1).join(""), context, partials);
that.set_delimiters(old_otag, old_ctag);
fragments = fragments.slice(0,i+1);
break;
} else {
fragments[i]=this.render_section(fragments[i], context, partials);
}
}
return fragments.join("");
},
/*
@@ -113,9 +109,9 @@ var Mustache = function() {
throw({message: "unknown_partial '" + name + "'"});
}
if(typeof(context[name]) != "object") {
return this.render(partials[name], context, partials, true);
return this.render_recursive(partials[name], context, partials);
}
return this.render(partials[name], context[name], partials, true);
return this.render_recursive(partials[name], context[name], partials);
},

/*
@@ -123,9 +119,38 @@ var Mustache = function() {
*/
render_section: function(template, context, partials) {
if(!this.includes("#", template) && !this.includes("^", template)) {
return template;
return this.render_tags(template, context, partials);
}

var that = this;
var regex = new RegExp(
"(" + this.escaped_otag + "(?:\\^|\\#)\\s*(.+?)(?:\\(.*\\))?\\s*" + this.escaped_ctag +
"\n*[\\s\\S]+?" + this.escaped_otag + "\\/\\s*\\2\\s*" + this.escaped_ctag + "\\s*)",
"mg");

var i, n;
var lastWasSection = false;
var fragments = template.split(regex);
for (i=0, n=fragments.length; i<n; ++i) {
if(lastWasSection) {
fragments[i] = "";
lastWasSection = false;
continue;
}
if(fragments[i].indexOf(this.otag+"#")===0 || fragments[i].indexOf(this.otag+"^")===0) {
lastWasSection = true;
fragments[i] = this.render_section2(fragments[i], context, partials);
} else {
fragments[i] = this.render_tags(fragments[i], context, partials);
}
}
return fragments.join("");
},
render_section2: function(template, context, partials) {
var that = this;
// CSW - Added "+?" so it finds the tighest bound, not the widest
var regex = new RegExp(this.escaped_otag + "(\\^|\\#)\\s*((.+?)(\\(.*\\))?)\\s*" + this.escaped_ctag +
@@ -133,43 +158,45 @@ var Mustache = function() {
"\\s*", "mg");

// for each {{#foo}}{{/foo}} section do...
return template.replace(regex, function(match, type, name, reserved1, reserved2, content) {
// the reserved variables are not being used
var fragment = template.replace(regex, function(match, type, name, reserved1, reserved2, content) {
// the reserved variables are not being used
var value = that.find(name, context);
if(type == "^") { // inverted section
if(!value || that.is_array(value) && value.length === 0) {
// false or empty list, render it
return that.render(content, context, partials, true);
return that.render_recursive(content, context, partials);
} else {
return "";
}
} else if(type == "#") { // normal section
if(that.is_array(value)) { // Enumerable, Let's loop!
return that.map(value, function(row) {
return that.render(content, that.create_context(row),
return that.render_recursive(content, that.create_context(row),
partials, true);
}).join("");
} else if(that.is_object(value)) { // Object, Use it as subcontext!
return that.render(content, that.create_context(value),
return that.render_recursive(content, that.create_context(value),
partials, true);
} else if(typeof value === "function") {
// higher order section
return value.call(context, content, function(text) {
return that.render(text, context, partials, true);
return that.render_recursive(text, context, partials);
});
} else if(value) { // boolean section
return that.render(content, context, partials, true);
return that.render_recursive(content, context, partials);
} else {
return "";
}
}
});
return fragment;
},

/*
Replace {{foo}} and friends with values from our view
*/
render_tags: function(template, context, partials, in_recursion) {
render_tags: function(template, context, partials) {
// tit for tat
var that = this;

@@ -186,7 +213,7 @@ var Mustache = function() {
case ">": // render partial
return that.render_partial(name, context, partials);
case "{": // the triple mustache is unescaped
case "&": // the ampersand is also unescaped
case "&": // the ampersand is also unescaped
return that.find(name, context);
default: // escape the value
return that.escape(that.find(name, context));
@@ -195,16 +222,10 @@ var Mustache = function() {
var lines = template.split("\n");
var oldContent;
for(var i = 0; i < lines.length; i++) {
oldContent = lines[i];
lines[i] = lines[i].replace(regex, tag_replace_callback, this);
if(!in_recursion && (oldContent === "" || lines[i] !== "")) {
this.send(lines[i]);
}
}

if(in_recursion) {
return lines.join("\n");
}
return lines.join("\n");
},

set_delimiters: function(open, close) {
@@ -351,15 +372,9 @@ var Mustache = function() {
/*
Turns a template and view into HTML
*/
to_html: function(template, view, partials, send_fun) {
to_html: function(template, view, partials) {
var renderer = new Renderer();
if(send_fun) {
renderer.send = send_fun;
}
renderer.render(template, view, partials);
if(!send_fun) {
return renderer.buffer.join("\n");
}
return renderer.render(template, view, partials);
}
});
}();

+ 1
- 1
test/unit.js Просмотреть файл

@@ -550,7 +550,7 @@ test("Regression Suite", function() {
{ enumerate: [ { text: 'A' }, { text: 'B' } ] },
{ partial: '{{=[[ ]]=}}\n{{text}}\n[[={{ }}=]]' }
),
'{{text}}\n{{text}}\n',
'{{text}}\n\n{{text}}\n\n',
'Issue 44'
);


Загрузка…
Отмена
Сохранить