diff --git a/examples/delimiters.html b/examples/delimiters.html
new file mode 100644
index 0000000..92bea6d
--- /dev/null
+++ b/examples/delimiters.html
@@ -0,0 +1,6 @@
+{{=<% %>=}}
+* <% first %>
+<%=| |=%>
+* | second |
+|={{ }}=|
+* {{ third }}
diff --git a/examples/delimiters.js b/examples/delimiters.js
new file mode 100644
index 0000000..8d56dcd
--- /dev/null
+++ b/examples/delimiters.js
@@ -0,0 +1,5 @@
+var delimiters = {
+ first: "It worked the first time.",
+ second: "And it worked the second time.",
+ third: "Then, surprisingly, it worked the third time."
+}
diff --git a/examples/delimiters.txt b/examples/delimiters.txt
new file mode 100644
index 0000000..e95cd11
--- /dev/null
+++ b/examples/delimiters.txt
@@ -0,0 +1,7 @@
+
+* It worked the first time.
+
+* And it worked the second time.
+
+* Then, surprisingly, it worked the third time.
+
diff --git a/mustache.js b/mustache.js
index 3dab570..ded994a 100644
--- a/mustache.js
+++ b/mustache.js
@@ -11,6 +11,8 @@ var Mustache = {
name: "mustache.js",
version: "0.1",
context: {},
+ otag: "{{",
+ ctag: "}}",
/*
Public method. Turns a template and view into HTML
@@ -22,21 +24,32 @@ var Mustache = {
// Private Methods
render: function(template, view) {
// fail fast
- if(template.indexOf("{{") == -1) {
+ if(template.indexOf(this.otag) == -1) {
return template;
}
// keep context around for recursive calls
this.context = context = this.merge((this.context || {}), view);
- // first, render all sections
+ // first, render all sections
var html = this.render_section(template);
// restore context, recursion might have messed it up
this.context = context;
-
+
// finally, render tags
- return this.render_tags(html);
+
+ // render until all is rendered
+ var new_result = "";
+ var result = this.render_tags(html);
+ // print(result);
+ // while(result != new_result) {
+ // print(1);
+ // result = new_result;
+ // new_result = this.render_tags(result);
+ // print(new_result);
+ // }
+ return result;
},
/*
@@ -60,13 +73,13 @@ var Mustache = {
Renders boolean and enumerable sections
*/
render_section: function(template) {
- if(template.indexOf("{{#") == -1) {
+ if(template.indexOf(this.otag + "#") == -1) {
return template;
}
var that = this;
+ var regex = new RegExp(this.otag + "\\#(.+)" + this.ctag + "\\s*([\\s\\S]+)" + this.otag + "\\/\\1" + this.ctag + "\\s*", "mg");
// for each {{#foo}}{{/foo}} section do...
- return template.replace(/\{\{\#(.+)\}\}\s*([\s\S]+)\{\{\/\1\}\}\s*/mg,
- function(match, name, content) {
+ return template.replace(regex, function(match, name, content) {
var value = that.find(name);
if(that.is_array(value)) { // Enumerable, Let's loop!
return value.map(function(row) {
@@ -88,27 +101,78 @@ var Mustache = {
// tit for tat
var that = this;
// for each {{(!<{)?foo}} tag, do...
- return template.replace(/\{\{(!|<|\{)?([^\/#]+?)\1?\}\}+/mg,
- function (match, operator, name) {
+ regex = this.new_regex();
+ var lines = template;
+ while(regex.test(lines)) {
+ template = template.replace(regex, function (match, operator, name) {
switch(operator) {
case "!": // ignore comments
return match;
+ case "=": // set new delimiters
+ that.set_delimiters(name);
+ return "";
case "<": // render partial
return that.render_partial(name);
- case '{': // the triple mustache is unescaped
+ case "{": // the triple mustache is unescaped
return that.find(name);
default: // escape the value
+ // print("render name: '" +name+ "'");
return that.escape(that.find(name));
}
}, this);
+ regex = this.new_regex();
+ lines = this.pop_first(lines);
+ }
+ return template;
},
+ pop_first: function(lines) {
+ var lines_array = lines.split("\n");
+ lines_array.shift();
+ return lines_array.join("\n");
+ },
+
+ new_regex: function() {
+ return new RegExp(this.otag + "(=|!|<|\\{)?([^\/#]+?)\\1?" + this.ctag + "+", "m");
+ },
+/*
+{{=<% %>=}}
+* <% first %>
+<%=| |=%>
+* | second |
+|={{ }}=|
+* {{ third }}
+*/
+
+ set_delimiters: function(delimiters) {
+ var dels = delimiters.split(" ");
+ this.otag = this.escape_regex(dels[0]);
+ this.ctag = this.escape_regex(dels[1]);
+ },
+
+ escape_regex: function(text) {
+ // thank you Simon Willison
+ if(!arguments.callee.sRE) {
+ var specials = [
+ '/', '.', '*', '+', '?', '|',
+ '(', ')', '[', ']', '{', '}', '\\'
+ ];
+ arguments.callee.sRE = new RegExp(
+ '(\\' + specials.join('|\\') + ')', 'g'
+ );
+ }
+ return text.replace(arguments.callee.sRE, '\\$1');
+ },
+
/*
find `name` in current `context`. That is find me a value
from the view object
*/
find: function(name) {
name = this.trim(name);
+ if(name === "") {
+ // return "";
+ }
var context = this.context;
if(typeof context[name] === "function") {
return context[name].apply(context);