diff --git a/app/index.html b/app/index.html
index 6f52251e..818703a0 100644
--- a/app/index.html
+++ b/app/index.html
@@ -35,7 +35,7 @@
-
+
diff --git a/app/js/app.js b/app/js/app.js
index b3ea3f22..f77375a0 100755
--- a/app/js/app.js
+++ b/app/js/app.js
@@ -102,7 +102,7 @@ var ExplorerApp = Backbone.View.extend({
function makeEmbedLink(state) {
var link = self.makePermaLink(state);
link = link + '&embed=true';
- var out = $.mustache('', {link: link});
+ var out = Mustache.render('', {link: link});
return out;
}
explorer.state.bind('change', function() {
diff --git a/src/view-graph.js b/src/view-graph.js
index 78eb12d8..6b5374c4 100644
--- a/src/view-graph.js
+++ b/src/view-graph.js
@@ -120,7 +120,7 @@ my.Graph = Backbone.View.extend({
render: function() {
var self = this;
var tmplData = this.model.toTemplateJSON();
- var htmls = $.mustache(this.template, tmplData);
+ var htmls = Mustache.render(this.template, tmplData);
$(this.el).html(htmls);
this.$graph = this.el.find('.panel.graph');
@@ -375,7 +375,7 @@ my.Graph = Backbone.View.extend({
seriesName: String.fromCharCode(idx + 64 + 1),
}, this.model.toTemplateJSON());
- var htmls = $.mustache(this.templateSeriesEditor, data);
+ var htmls = Mustache.render(this.templateSeriesEditor, data);
this.el.find('.editor-series-group').append(htmls);
return this;
},
diff --git a/src/view-grid.js b/src/view-grid.js
index ab8a2f39..fc9400b9 100644
--- a/src/view-grid.js
+++ b/src/view-grid.js
@@ -53,7 +53,7 @@ my.Grid = Backbone.View.extend({
{{#columns}} \
Show column: {{.}} \
{{/columns}}';
- var tmp = $.mustache(tmpl, {'columns': this.state.get('hiddenFields')});
+ var tmp = Mustache.render(tmpl, {'columns': this.state.get('hiddenFields')});
this.el.find('.root-header-menu .dropdown-menu').html(tmp);
},
@@ -211,7 +211,7 @@ my.Grid = Backbone.View.extend({
field.set({width: width});
}
});
- var htmls = $.mustache(this.template, this.toTemplateJSON());
+ var htmls = Mustache.render(this.template, this.toTemplateJSON());
this.el.html(htmls);
this.model.currentDocuments.forEach(function(doc) {
var tr = $('
');
@@ -308,7 +308,7 @@ my.GridRow = Backbone.View.extend({
render: function() {
this.el.attr('data-id', this.model.id);
- var html = $.mustache(this.template, this.toTemplateJSON());
+ var html = Mustache.render(this.template, this.toTemplateJSON());
$(this.el).html(html);
return this;
},
@@ -336,7 +336,7 @@ my.GridRow = Backbone.View.extend({
$(e.target).addClass("hidden");
var cell = $(e.target).siblings('.data-table-cell-value');
cell.data("previousContents", cell.text());
- var templated = $.mustache(this.cellEditorTemplate, {value: cell.text()});
+ var templated = Mustache.render(this.cellEditorTemplate, {value: cell.text()});
cell.html(templated);
},
diff --git a/src/view-map.js b/src/view-map.js
index 60be7bc4..412ab2c7 100644
--- a/src/view-map.js
+++ b/src/view-map.js
@@ -161,7 +161,7 @@ my.Map = Backbone.View.extend({
var self = this;
- htmls = $.mustache(this.template, this.model.toTemplateJSON());
+ htmls = Mustache.render(this.template, this.model.toTemplateJSON());
$(this.el).html(htmls);
this.$map = this.el.find('.panel.map');
diff --git a/src/view-timeline.js b/src/view-timeline.js
index 39962807..e3faca04 100644
--- a/src/view-timeline.js
+++ b/src/view-timeline.js
@@ -47,7 +47,7 @@ my.Timeline = Backbone.View.extend({
render: function() {
var tmplData = {};
- var htmls = $.mustache(this.template, tmplData);
+ var htmls = Mustache.render(this.template, tmplData);
this.el.html(htmls);
},
diff --git a/src/view-transform-dialog.js b/src/view-transform-dialog.js
index 6c69766b..e4bee9ae 100644
--- a/src/view-transform-dialog.js
+++ b/src/view-transform-dialog.js
@@ -76,7 +76,7 @@ my.ColumnTransform = Backbone.View.extend({
},
render: function() {
- var htmls = $.mustache(this.template,
+ var htmls = Mustache.render(this.template,
{name: this.state.currentColumn}
);
this.el.html(htmls);
@@ -163,7 +163,7 @@ my.ColumnTransform = Backbone.View.extend({
});
var previewData = costco.previewTransform(docs, editFunc, self.state.currentColumn);
var $el = self.el.find('.expression-preview-container');
- var templated = $.mustache(self.editPreviewTemplate, {rows: previewData.slice(0,4)});
+ var templated = Mustache.render(self.editPreviewTemplate, {rows: previewData.slice(0,4)});
$el.html(templated);
} else {
errors.text(editFunc.errorMessage);
diff --git a/src/view.js b/src/view.js
index 89b74edf..c49b6592 100644
--- a/src/view.js
+++ b/src/view.js
@@ -286,7 +286,7 @@ my.DataExplorer = Backbone.View.extend({
render: function() {
var tmplData = this.model.toTemplateJSON();
tmplData.views = this.pageViews;
- var template = $.mustache(this.template, tmplData);
+ var template = Mustache.render(this.template, tmplData);
$(this.el).html(template);
var $dataViewContainer = this.el.find('.data-view-container');
_.each(this.pageViews, function(view, pageName) {
@@ -431,7 +431,7 @@ my.DataExplorer = Backbone.View.extend({
{{message}} \
';
}
- var _templated = $($.mustache(_template, tmplData));
+ var _templated = $(Mustache.render(_template, tmplData));
_templated = $(_templated).appendTo($('.recline-data-explorer .alert-messages'));
if (!flash.persist) {
setTimeout(function() {
@@ -516,7 +516,7 @@ my.QueryEditor = Backbone.View.extend({
render: function() {
var tmplData = this.model.toJSON();
tmplData.to = this.model.get('from') + this.model.get('size');
- var templated = $.mustache(this.template, tmplData);
+ var templated = Mustache.render(this.template, tmplData);
this.el.html(templated);
}
});
@@ -584,7 +584,7 @@ my.FilterEditor = Backbone.View.extend({
value: filter.term[fieldId]
};
});
- var out = $.mustache(this.template, tmplData);
+ var out = Mustache.render(this.template, tmplData);
this.el.html(out);
// are there actually any facets to show?
if (this.model.get('filters').length > 0) {
@@ -669,7 +669,7 @@ my.FacetViewer = Backbone.View.extend({
}
return facet;
});
- var templated = $.mustache(this.template, tmplData);
+ var templated = Mustache.render(this.template, tmplData);
this.el.html(templated);
// are there actually any facets to show?
if (this.model.facets.length > 0) {
diff --git a/test/index.html b/test/index.html
index 04dd88a4..b5dac4c5 100644
--- a/test/index.html
+++ b/test/index.html
@@ -12,7 +12,7 @@
-
+
diff --git a/vendor/jquery.mustache.js b/vendor/jquery.mustache.js
deleted file mode 100755
index 5aa67def..00000000
--- a/vendor/jquery.mustache.js
+++ /dev/null
@@ -1,346 +0,0 @@
-/*
-Shameless port of a shameless port
-@defunkt => @janl => @aq
-
-See http://github.com/defunkt/mustache for more info.
-*/
-
-;(function($) {
-
-/*
- mustache.js — Logic-less templates in JavaScript
-
- See http://mustache.github.com/ for more info.
-*/
-
-var Mustache = function() {
- var Renderer = function() {};
-
- Renderer.prototype = {
- otag: "{{",
- 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
- }
-
- // fail fast
- if(!this.includes("", template)) {
- if(in_recursion) {
- return template;
- } else {
- this.send(template);
- return;
- }
- }
-
- template = this.render_pragmas(template);
- 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);
- },
-
- /*
- Sends parsed lines
- */
- send: function(line) {
- if(line != "") {
- this.buffer.push(line);
- }
- },
-
- /*
- Looks for %PRAGMAS
- */
- render_pragmas: function(template) {
- // no pragmas
- if(!this.includes("%", template)) {
- return template;
- }
-
- var that = this;
- var regex = new RegExp(this.otag + "%([\\w-]+) ?([\\w]+=[\\w]+)?" +
- this.ctag);
- return template.replace(regex, function(match, pragma, options) {
- if(!that.pragmas_implemented[pragma]) {
- throw({message:
- "This implementation of mustache doesn't understand the '" +
- pragma + "' pragma"});
- }
- that.pragmas[pragma] = {};
- if(options) {
- var opts = options.split("=");
- that.pragmas[pragma][opts[0]] = opts[1];
- }
- return "";
- // ignore unknown pragmas silently
- });
- },
-
- /*
- Tries to find a partial in the curent scope and render it
- */
- render_partial: function(name, context, partials) {
- name = this.trim(name);
- if(!partials || partials[name] === undefined) {
- throw({message: "unknown_partial '" + name + "'"});
- }
- if(typeof(context[name]) != "object") {
- return this.render(partials[name], context, partials, true);
- }
- return this.render(partials[name], context[name], partials, true);
- },
-
- /*
- Renders inverted (^) and normal (#) sections
- */
- render_section: function(template, context, partials) {
- if(!this.includes("#", template) && !this.includes("^", template)) {
- return template;
- }
-
- var that = this;
- // CSW - Added "+?" so it finds the tighest bound, not the widest
- var regex = new RegExp(this.otag + "(\\^|\\#)\\s*(.+)\\s*" + this.ctag +
- "\n*([\\s\\S]+?)" + this.otag + "\\/\\s*\\2\\s*" + this.ctag +
- "\\s*", "mg");
-
- // for each {{#foo}}{{/foo}} section do...
- return template.replace(regex, function(match, type, name, content) {
- 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);
- } 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),
- partials, true);
- }).join("");
- } else if(that.is_object(value)) { // Object, Use it as subcontext!
- return that.render(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);
- });
- } else if(value) { // boolean section
- return that.render(content, context, partials, true);
- } else {
- return "";
- }
- }
- });
- },
-
- /*
- Replace {{foo}} and friends with values from our view
- */
- render_tags: function(template, context, partials, in_recursion) {
- // tit for tat
- var that = this;
-
- var new_regex = function() {
- return new RegExp(that.otag + "(=|!|>|\\{|%)?([^\\/#\\^]+?)\\1?" +
- that.ctag + "+", "g");
- };
-
- var regex = new_regex();
- var tag_replace_callback = function(match, operator, name) {
- switch(operator) {
- case "!": // ignore comments
- return "";
- case "=": // set new delimiters, rebuild the replace regexp
- that.set_delimiters(name);
- regex = new_regex();
- return "";
- case ">": // render partial
- return that.render_partial(name, context, partials);
- case "{": // the triple mustache is unescaped
- return that.find(name, context);
- default: // escape the value
- return that.escape(that.find(name, context));
- }
- };
- var lines = template.split("\n");
- for(var i = 0; i < lines.length; i++) {
- lines[i] = lines[i].replace(regex, tag_replace_callback, this);
- if(!in_recursion) {
- this.send(lines[i]);
- }
- }
-
- if(in_recursion) {
- return lines.join("\n");
- }
- },
-
- 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, context) {
- name = this.trim(name);
-
- // Checks whether a value is thruthy or false or 0
- function is_kinda_truthy(bool) {
- return bool === false || bool === 0 || bool;
- }
-
- var value;
- if(is_kinda_truthy(context[name])) {
- value = context[name];
- } else if(is_kinda_truthy(this.context[name])) {
- value = this.context[name];
- }
-
- if(typeof value === "function") {
- return value.apply(context);
- }
- if(value !== undefined) {
- return value;
- }
- // silently ignore unkown variables
- return "";
- },
-
- // Utility methods
-
- /* includes tag */
- includes: function(needle, haystack) {
- return haystack.indexOf(this.otag + needle) != -1;
- },
-
- /*
- Does away with nasty characters
- */
- escape: function(s) {
- s = String(s === null ? "" : s);
- return s.replace(/&(?!\w+;)|["<>\\]/g, function(s) {
- switch(s) {
- case "&": return "&";
- case "\\": return "\\\\";
- case '"': return '\"';
- case "<": return "<";
- case ">": return ">";
- default: return s;
- }
- });
- },
-
- // by @langalex, support for arrays of strings
- create_context: function(_context) {
- if(this.is_object(_context)) {
- return _context;
- } else {
- var iterator = ".";
- if(this.pragmas["IMPLICIT-ITERATOR"]) {
- iterator = this.pragmas["IMPLICIT-ITERATOR"].iterator;
- }
- var ctx = {};
- ctx[iterator] = _context;
- return ctx;
- }
- },
-
- is_object: function(a) {
- return a && typeof a == "object";
- },
-
- is_array: function(a) {
- return Object.prototype.toString.call(a) === '[object Array]';
- },
-
- /*
- Gets rid of leading and trailing whitespace
- */
- trim: function(s) {
- return s.replace(/^\s*|\s*$/g, "");
- },
-
- /*
- Why, why, why? Because IE. Cry, cry cry.
- */
- map: function(array, fn) {
- if (typeof array.map == "function") {
- return array.map(fn);
- } else {
- var r = [];
- var l = array.length;
- for(var i = 0; i < l; i++) {
- r.push(fn(array[i]));
- }
- return r;
- }
- }
- };
-
- return({
- name: "mustache.js",
- version: "0.3.1-dev",
-
- /*
- Turns a template and view into HTML
- */
- to_html: function(template, view, partials, send_fun) {
- var renderer = new Renderer();
- if(send_fun) {
- renderer.send = send_fun;
- }
- renderer.render(template, view, partials);
- if(!send_fun) {
- return renderer.buffer.join("\n");
- }
- },
- escape : function(text) {
- return new Renderer().escape(text);
- }
- });
-}();
-
- $.mustache = function(template, view, partials) {
- return Mustache.to_html(template, view, partials);
- };
-
- $.mustache.escape = function(text) {
- return Mustache.escape(text);
- };
-
-})(jQuery);
diff --git a/vendor/mustache/0.5.0-dev/mustache.js b/vendor/mustache/0.5.0-dev/mustache.js
new file mode 100644
index 00000000..641cebd5
--- /dev/null
+++ b/vendor/mustache/0.5.0-dev/mustache.js
@@ -0,0 +1,536 @@
+/*!
+ * mustache.js - Logic-less {{mustache}} templates with JavaScript
+ * http://github.com/janl/mustache.js
+ */
+var Mustache = (typeof module !== "undefined" && module.exports) || {};
+
+(function (exports) {
+
+ exports.name = "mustache.js";
+ exports.version = "0.5.0-dev";
+ exports.tags = ["{{", "}}"];
+ exports.parse = parse;
+ exports.compile = compile;
+ exports.render = render;
+ exports.clearCache = clearCache;
+
+ // This is here for backwards compatibility with 0.4.x.
+ exports.to_html = function (template, view, partials, send) {
+ var result = render(template, view, partials);
+
+ if (typeof send === "function") {
+ send(result);
+ } else {
+ return result;
+ }
+ };
+
+ var _toString = Object.prototype.toString;
+ var _isArray = Array.isArray;
+ var _forEach = Array.prototype.forEach;
+ var _trim = String.prototype.trim;
+
+ var isArray;
+ if (_isArray) {
+ isArray = _isArray;
+ } else {
+ isArray = function (obj) {
+ return _toString.call(obj) === "[object Array]";
+ };
+ }
+
+ var forEach;
+ if (_forEach) {
+ forEach = function (obj, callback, scope) {
+ return _forEach.call(obj, callback, scope);
+ };
+ } else {
+ forEach = function (obj, callback, scope) {
+ for (var i = 0, len = obj.length; i < len; ++i) {
+ callback.call(scope, obj[i], i, obj);
+ }
+ };
+ }
+
+ var spaceRe = /^\s*$/;
+
+ function isWhitespace(string) {
+ return spaceRe.test(string);
+ }
+
+ var trim;
+ if (_trim) {
+ trim = function (string) {
+ return string == null ? "" : _trim.call(string);
+ };
+ } else {
+ var trimLeft, trimRight;
+
+ if (isWhitespace("\xA0")) {
+ trimLeft = /^\s+/;
+ trimRight = /\s+$/;
+ } else {
+ // IE doesn't match non-breaking spaces with \s, thanks jQuery.
+ trimLeft = /^[\s\xA0]+/;
+ trimRight = /[\s\xA0]+$/;
+ }
+
+ trim = function (string) {
+ return string == null ? "" :
+ String(string).replace(trimLeft, "").replace(trimRight, "");
+ };
+ }
+
+ var escapeMap = {
+ "&": "&",
+ "<": "<",
+ ">": ">",
+ '"': '"',
+ "'": '''
+ };
+
+ function escapeHTML(string) {
+ return String(string).replace(/&(?!\w+;)|[<>"']/g, function (s) {
+ return escapeMap[s] || s;
+ });
+ }
+
+ /**
+ * Adds the `template`, `line`, and `file` properties to the given error
+ * object and alters the message to provide more useful debugging information.
+ */
+ function debug(e, template, line, file) {
+ file = file || "";
+
+ var lines = template.split("\n"),
+ start = Math.max(line - 3, 0),
+ end = Math.min(lines.length, line + 3),
+ context = lines.slice(start, end);
+
+ var c;
+ for (var i = 0, len = context.length; i < len; ++i) {
+ c = i + start + 1;
+ context[i] = (c === line ? " >> " : " ") + context[i];
+ }
+
+ e.template = template;
+ e.line = line;
+ e.file = file;
+ e.message = [file + ":" + line, context.join("\n"), "", e.message].join("\n");
+
+ return e;
+ }
+
+ /**
+ * Looks up the value of the given `name` in the given context `stack`.
+ */
+ function lookup(name, stack, defaultValue) {
+ if (name === ".") {
+ return stack[stack.length - 1];
+ }
+
+ var names = name.split(".");
+ var lastIndex = names.length - 1;
+ var target = names[lastIndex];
+
+ var value, context, i = stack.length, j, localStack;
+ while (i) {
+ localStack = stack.slice(0);
+ context = stack[--i];
+
+ j = 0;
+ while (j < lastIndex) {
+ context = context[names[j++]];
+
+ if (context == null) {
+ break;
+ }
+
+ localStack.push(context);
+ }
+
+ if (context && typeof context === "object" && target in context) {
+ value = context[target];
+ break;
+ }
+ }
+
+ // If the value is a function, call it in the current context.
+ if (typeof value === "function") {
+ value = value.call(localStack[localStack.length - 1]);
+ }
+
+ if (value == null) {
+ return defaultValue;
+ }
+
+ return value;
+ }
+
+ function renderSection(name, stack, callback, inverted) {
+ var buffer = "";
+ var value = lookup(name, stack);
+
+ if (inverted) {
+ // From the spec: inverted sections may render text once based on the
+ // inverse value of the key. That is, they will be rendered if the key
+ // doesn't exist, is false, or is an empty list.
+ if (value == null || value === false || (isArray(value) && value.length === 0)) {
+ buffer += callback();
+ }
+ } else if (isArray(value)) {
+ forEach(value, function (value) {
+ stack.push(value);
+ buffer += callback();
+ stack.pop();
+ });
+ } else if (typeof value === "object") {
+ stack.push(value);
+ buffer += callback();
+ stack.pop();
+ } else if (typeof value === "function") {
+ var scope = stack[stack.length - 1];
+ var scopedRender = function (template) {
+ return render(template, scope);
+ };
+ buffer += value.call(scope, callback(), scopedRender) || "";
+ } else if (value) {
+ buffer += callback();
+ }
+
+ return buffer;
+ }
+
+ /**
+ * Parses the given `template` and returns the source of a function that,
+ * with the proper arguments, will render the template. Recognized options
+ * include the following:
+ *
+ * - file The name of the file the template comes from (displayed in
+ * error messages)
+ * - tags An array of open and close tags the `template` uses. Defaults
+ * to the value of Mustache.tags
+ * - debug Set `true` to log the body of the generated function to the
+ * console
+ * - space Set `true` to preserve whitespace from lines that otherwise
+ * contain only a {{tag}}. Defaults to `false`
+ */
+ function parse(template, options) {
+ options = options || {};
+
+ var tags = options.tags || exports.tags,
+ openTag = tags[0],
+ closeTag = tags[tags.length - 1];
+
+ var code = [
+ 'var buffer = "";', // output buffer
+ "\nvar line = 1;", // keep track of source line number
+ "\ntry {",
+ '\nbuffer += "'
+ ];
+
+ var spaces = [], // indices of whitespace in code on the current line
+ hasTag = false, // is there a {{tag}} on the current line?
+ nonSpace = false; // is there a non-space char on the current line?
+
+ // Strips all space characters from the code array for the current line
+ // if there was a {{tag}} on it and otherwise only spaces.
+ var stripSpace = function () {
+ if (hasTag && !nonSpace && !options.space) {
+ while (spaces.length) {
+ code.splice(spaces.pop(), 1);
+ }
+ } else {
+ spaces = [];
+ }
+
+ hasTag = false;
+ nonSpace = false;
+ };
+
+ var sectionStack = [], updateLine, nextOpenTag, nextCloseTag;
+
+ var setTags = function (source) {
+ tags = trim(source).split(/\s+/);
+ nextOpenTag = tags[0];
+ nextCloseTag = tags[tags.length - 1];
+ };
+
+ var includePartial = function (source) {
+ code.push(
+ '";',
+ updateLine,
+ '\nvar partial = partials["' + trim(source) + '"];',
+ '\nif (partial) {',
+ '\n buffer += render(partial,stack[stack.length - 1],partials);',
+ '\n}',
+ '\nbuffer += "'
+ );
+ };
+
+ var openSection = function (source, inverted) {
+ var name = trim(source);
+
+ if (name === "") {
+ throw debug(new Error("Section name may not be empty"), template, line, options.file);
+ }
+
+ sectionStack.push({name: name, inverted: inverted});
+
+ code.push(
+ '";',
+ updateLine,
+ '\nvar name = "' + name + '";',
+ '\nvar callback = (function () {',
+ '\n return function () {',
+ '\n var buffer = "";',
+ '\nbuffer += "'
+ );
+ };
+
+ var openInvertedSection = function (source) {
+ openSection(source, true);
+ };
+
+ var closeSection = function (source) {
+ var name = trim(source);
+ var openName = sectionStack.length != 0 && sectionStack[sectionStack.length - 1].name;
+
+ if (!openName || name != openName) {
+ throw debug(new Error('Section named "' + name + '" was never opened'), template, line, options.file);
+ }
+
+ var section = sectionStack.pop();
+
+ code.push(
+ '";',
+ '\n return buffer;',
+ '\n };',
+ '\n})();'
+ );
+
+ if (section.inverted) {
+ code.push("\nbuffer += renderSection(name,stack,callback,true);");
+ } else {
+ code.push("\nbuffer += renderSection(name,stack,callback);");
+ }
+
+ code.push('\nbuffer += "');
+ };
+
+ var sendPlain = function (source) {
+ code.push(
+ '";',
+ updateLine,
+ '\nbuffer += lookup("' + trim(source) + '",stack,"");',
+ '\nbuffer += "'
+ );
+ };
+
+ var sendEscaped = function (source) {
+ code.push(
+ '";',
+ updateLine,
+ '\nbuffer += escapeHTML(lookup("' + trim(source) + '",stack,""));',
+ '\nbuffer += "'
+ );
+ };
+
+ var line = 1, c, callback;
+ for (var i = 0, len = template.length; i < len; ++i) {
+ if (template.slice(i, i + openTag.length) === openTag) {
+ i += openTag.length;
+ c = template.substr(i, 1);
+ updateLine = '\nline = ' + line + ';';
+ nextOpenTag = openTag;
+ nextCloseTag = closeTag;
+ hasTag = true;
+
+ switch (c) {
+ case "!": // comment
+ i++;
+ callback = null;
+ break;
+ case "=": // change open/close tags, e.g. {{=<% %>=}}
+ i++;
+ closeTag = "=" + closeTag;
+ callback = setTags;
+ break;
+ case ">": // include partial
+ i++;
+ callback = includePartial;
+ break;
+ case "#": // start section
+ i++;
+ callback = openSection;
+ break;
+ case "^": // start inverted section
+ i++;
+ callback = openInvertedSection;
+ break;
+ case "/": // end section
+ i++;
+ callback = closeSection;
+ break;
+ case "{": // plain variable
+ closeTag = "}" + closeTag;
+ // fall through
+ case "&": // plain variable
+ i++;
+ nonSpace = true;
+ callback = sendPlain;
+ break;
+ default: // escaped variable
+ nonSpace = true;
+ callback = sendEscaped;
+ }
+
+ var end = template.indexOf(closeTag, i);
+
+ if (end === -1) {
+ throw debug(new Error('Tag "' + openTag + '" was not closed properly'), template, line, options.file);
+ }
+
+ var source = template.substring(i, end);
+
+ if (callback) {
+ callback(source);
+ }
+
+ // Maintain line count for \n in source.
+ var n = 0;
+ while (~(n = source.indexOf("\n", n))) {
+ line++;
+ n++;
+ }
+
+ i = end + closeTag.length - 1;
+ openTag = nextOpenTag;
+ closeTag = nextCloseTag;
+ } else {
+ c = template.substr(i, 1);
+
+ switch (c) {
+ case '"':
+ case "\\":
+ nonSpace = true;
+ code.push("\\" + c);
+ break;
+ case "\r":
+ // Ignore carriage returns.
+ break;
+ case "\n":
+ spaces.push(code.length);
+ code.push("\\n");
+ stripSpace(); // Check for whitespace on the current line.
+ line++;
+ break;
+ default:
+ if (isWhitespace(c)) {
+ spaces.push(code.length);
+ } else {
+ nonSpace = true;
+ }
+
+ code.push(c);
+ }
+ }
+ }
+
+ if (sectionStack.length != 0) {
+ throw debug(new Error('Section "' + sectionStack[sectionStack.length - 1].name + '" was not closed properly'), template, line, options.file);
+ }
+
+ // Clean up any whitespace from a closing {{tag}} that was at the end
+ // of the template without a trailing \n.
+ stripSpace();
+
+ code.push(
+ '";',
+ "\nreturn buffer;",
+ "\n} catch (e) { throw {error: e, line: line}; }"
+ );
+
+ // Ignore `buffer += "";` statements.
+ var body = code.join("").replace(/buffer \+= "";\n/g, "");
+
+ if (options.debug) {
+ if (typeof console != "undefined" && console.log) {
+ console.log(body);
+ } else if (typeof print === "function") {
+ print(body);
+ }
+ }
+
+ return body;
+ }
+
+ /**
+ * Used by `compile` to generate a reusable function for the given `template`.
+ */
+ function _compile(template, options) {
+ var args = "view,partials,stack,lookup,escapeHTML,renderSection,render";
+ var body = parse(template, options);
+ var fn = new Function(args, body);
+
+ // This anonymous function wraps the generated function so we can do
+ // argument coercion, setup some variables, and handle any errors
+ // encountered while executing it.
+ return function (view, partials) {
+ partials = partials || {};
+
+ var stack = [view]; // context stack
+
+ try {
+ return fn(view, partials, stack, lookup, escapeHTML, renderSection, render);
+ } catch (e) {
+ throw debug(e.error, template, e.line, options.file);
+ }
+ };
+ }
+
+ // Cache of pre-compiled templates.
+ var _cache = {};
+
+ /**
+ * Clear the cache of compiled templates.
+ */
+ function clearCache() {
+ _cache = {};
+ }
+
+ /**
+ * Compiles the given `template` into a reusable function using the given
+ * `options`. In addition to the options accepted by Mustache.parse,
+ * recognized options include the following:
+ *
+ * - cache Set `false` to bypass any pre-compiled version of the given
+ * template. Otherwise, a given `template` string will be cached
+ * the first time it is parsed
+ */
+ function compile(template, options) {
+ options = options || {};
+
+ // Use a pre-compiled version from the cache if we have one.
+ if (options.cache !== false) {
+ if (!_cache[template]) {
+ _cache[template] = _compile(template, options);
+ }
+
+ return _cache[template];
+ }
+
+ return _compile(template, options);
+ }
+
+ /**
+ * High-level function that renders the given `template` using the given
+ * `view` and `partials`. If you need to use any of the template options (see
+ * `compile` above), you must compile in a separate step, and then call that
+ * compiled function.
+ */
+ function render(template, view, partials) {
+ return compile(template)(view, partials);
+ }
+
+})(Mustache);