Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / static / im / js / mustache.js @ 36dacb92

History | View | Annotate | Download (14.4 kB)

1 4e03ba30 Kostas Papadimitriou
/*!
2 4e03ba30 Kostas Papadimitriou
 * mustache.js - Logic-less {{mustache}} templates with JavaScript
3 4e03ba30 Kostas Papadimitriou
 * http://github.com/janl/mustache.js
4 4e03ba30 Kostas Papadimitriou
 */
5 4e03ba30 Kostas Papadimitriou
6 4e03ba30 Kostas Papadimitriou
/*global define: false*/
7 4e03ba30 Kostas Papadimitriou
8 4e03ba30 Kostas Papadimitriou
(function (root, mustache) {
9 4e03ba30 Kostas Papadimitriou
  if (typeof exports === "object" && exports) {
10 4e03ba30 Kostas Papadimitriou
    module.exports = mustache; // CommonJS
11 4e03ba30 Kostas Papadimitriou
  } else if (typeof define === "function" && define.amd) {
12 4e03ba30 Kostas Papadimitriou
    define(mustache); // AMD
13 4e03ba30 Kostas Papadimitriou
  } else {
14 4e03ba30 Kostas Papadimitriou
    root.Mustache = mustache; // <script>
15 4e03ba30 Kostas Papadimitriou
  }
16 4e03ba30 Kostas Papadimitriou
}(this, (function () {
17 4e03ba30 Kostas Papadimitriou
18 4e03ba30 Kostas Papadimitriou
  var whiteRe = /\s*/;
19 4e03ba30 Kostas Papadimitriou
  var spaceRe = /\s+/;
20 4e03ba30 Kostas Papadimitriou
  var nonSpaceRe = /\S/;
21 4e03ba30 Kostas Papadimitriou
  var eqRe = /\s*=/;
22 4e03ba30 Kostas Papadimitriou
  var curlyRe = /\s*\}/;
23 4e03ba30 Kostas Papadimitriou
  var tagRe = /#|\^|\/|>|\{|&|=|!/;
24 4e03ba30 Kostas Papadimitriou
25 4e03ba30 Kostas Papadimitriou
  var _test = RegExp.prototype.test;
26 4e03ba30 Kostas Papadimitriou
  var _toString = Object.prototype.toString;
27 4e03ba30 Kostas Papadimitriou
28 4e03ba30 Kostas Papadimitriou
  // Workaround for https://issues.apache.org/jira/browse/COUCHDB-577
29 4e03ba30 Kostas Papadimitriou
  // See https://github.com/janl/mustache.js/issues/189
30 4e03ba30 Kostas Papadimitriou
  function testRe(re, string) {
31 4e03ba30 Kostas Papadimitriou
    return _test.call(re, string);
32 4e03ba30 Kostas Papadimitriou
  }
33 4e03ba30 Kostas Papadimitriou
34 4e03ba30 Kostas Papadimitriou
  function isWhitespace(string) {
35 4e03ba30 Kostas Papadimitriou
    return !testRe(nonSpaceRe, string);
36 4e03ba30 Kostas Papadimitriou
  }
37 4e03ba30 Kostas Papadimitriou
38 4e03ba30 Kostas Papadimitriou
  var isArray = Array.isArray || function (obj) {
39 4e03ba30 Kostas Papadimitriou
    return _toString.call(obj) === '[object Array]';
40 4e03ba30 Kostas Papadimitriou
  };
41 4e03ba30 Kostas Papadimitriou
42 4e03ba30 Kostas Papadimitriou
  function escapeRe(string) {
43 4e03ba30 Kostas Papadimitriou
    return string.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
44 4e03ba30 Kostas Papadimitriou
  }
45 4e03ba30 Kostas Papadimitriou
46 4e03ba30 Kostas Papadimitriou
  var entityMap = {
47 4e03ba30 Kostas Papadimitriou
    "&": "&amp;",
48 4e03ba30 Kostas Papadimitriou
    "<": "&lt;",
49 4e03ba30 Kostas Papadimitriou
    ">": "&gt;",
50 4e03ba30 Kostas Papadimitriou
    '"': '&quot;',
51 4e03ba30 Kostas Papadimitriou
    "'": '&#39;',
52 4e03ba30 Kostas Papadimitriou
    "/": '&#x2F;'
53 4e03ba30 Kostas Papadimitriou
  };
54 4e03ba30 Kostas Papadimitriou
55 4e03ba30 Kostas Papadimitriou
  function escapeHtml(string) {
56 4e03ba30 Kostas Papadimitriou
    return String(string).replace(/[&<>"'\/]/g, function (s) {
57 4e03ba30 Kostas Papadimitriou
      return entityMap[s];
58 4e03ba30 Kostas Papadimitriou
    });
59 4e03ba30 Kostas Papadimitriou
  }
60 4e03ba30 Kostas Papadimitriou
61 4e03ba30 Kostas Papadimitriou
  function Scanner(string) {
62 4e03ba30 Kostas Papadimitriou
    this.string = string;
63 4e03ba30 Kostas Papadimitriou
    this.tail = string;
64 4e03ba30 Kostas Papadimitriou
    this.pos = 0;
65 4e03ba30 Kostas Papadimitriou
  }
66 4e03ba30 Kostas Papadimitriou
67 4e03ba30 Kostas Papadimitriou
  /**
68 4e03ba30 Kostas Papadimitriou
   * Returns `true` if the tail is empty (end of string).
69 4e03ba30 Kostas Papadimitriou
   */
70 4e03ba30 Kostas Papadimitriou
  Scanner.prototype.eos = function () {
71 4e03ba30 Kostas Papadimitriou
    return this.tail === "";
72 4e03ba30 Kostas Papadimitriou
  };
73 4e03ba30 Kostas Papadimitriou
74 4e03ba30 Kostas Papadimitriou
  /**
75 4e03ba30 Kostas Papadimitriou
   * Tries to match the given regular expression at the current position.
76 4e03ba30 Kostas Papadimitriou
   * Returns the matched text if it can match, the empty string otherwise.
77 4e03ba30 Kostas Papadimitriou
   */
78 4e03ba30 Kostas Papadimitriou
  Scanner.prototype.scan = function (re) {
79 4e03ba30 Kostas Papadimitriou
    var match = this.tail.match(re);
80 4e03ba30 Kostas Papadimitriou
81 4e03ba30 Kostas Papadimitriou
    if (match && match.index === 0) {
82 4e03ba30 Kostas Papadimitriou
      this.tail = this.tail.substring(match[0].length);
83 4e03ba30 Kostas Papadimitriou
      this.pos += match[0].length;
84 4e03ba30 Kostas Papadimitriou
      return match[0];
85 4e03ba30 Kostas Papadimitriou
    }
86 4e03ba30 Kostas Papadimitriou
87 4e03ba30 Kostas Papadimitriou
    return "";
88 4e03ba30 Kostas Papadimitriou
  };
89 4e03ba30 Kostas Papadimitriou
90 4e03ba30 Kostas Papadimitriou
  /**
91 4e03ba30 Kostas Papadimitriou
   * Skips all text until the given regular expression can be matched. Returns
92 4e03ba30 Kostas Papadimitriou
   * the skipped string, which is the entire tail if no match can be made.
93 4e03ba30 Kostas Papadimitriou
   */
94 4e03ba30 Kostas Papadimitriou
  Scanner.prototype.scanUntil = function (re) {
95 4e03ba30 Kostas Papadimitriou
    var match, pos = this.tail.search(re);
96 4e03ba30 Kostas Papadimitriou
97 4e03ba30 Kostas Papadimitriou
    switch (pos) {
98 4e03ba30 Kostas Papadimitriou
    case -1:
99 4e03ba30 Kostas Papadimitriou
      match = this.tail;
100 4e03ba30 Kostas Papadimitriou
      this.pos += this.tail.length;
101 4e03ba30 Kostas Papadimitriou
      this.tail = "";
102 4e03ba30 Kostas Papadimitriou
      break;
103 4e03ba30 Kostas Papadimitriou
    case 0:
104 4e03ba30 Kostas Papadimitriou
      match = "";
105 4e03ba30 Kostas Papadimitriou
      break;
106 4e03ba30 Kostas Papadimitriou
    default:
107 4e03ba30 Kostas Papadimitriou
      match = this.tail.substring(0, pos);
108 4e03ba30 Kostas Papadimitriou
      this.tail = this.tail.substring(pos);
109 4e03ba30 Kostas Papadimitriou
      this.pos += pos;
110 4e03ba30 Kostas Papadimitriou
    }
111 4e03ba30 Kostas Papadimitriou
112 4e03ba30 Kostas Papadimitriou
    return match;
113 4e03ba30 Kostas Papadimitriou
  };
114 4e03ba30 Kostas Papadimitriou
115 4e03ba30 Kostas Papadimitriou
  function Context(view, parent) {
116 4e03ba30 Kostas Papadimitriou
    this.view = view || {};
117 4e03ba30 Kostas Papadimitriou
    this.parent = parent;
118 4e03ba30 Kostas Papadimitriou
    this._cache = {};
119 4e03ba30 Kostas Papadimitriou
  }
120 4e03ba30 Kostas Papadimitriou
121 4e03ba30 Kostas Papadimitriou
  Context.make = function (view) {
122 4e03ba30 Kostas Papadimitriou
    return (view instanceof Context) ? view : new Context(view);
123 4e03ba30 Kostas Papadimitriou
  };
124 4e03ba30 Kostas Papadimitriou
125 4e03ba30 Kostas Papadimitriou
  Context.prototype.push = function (view) {
126 4e03ba30 Kostas Papadimitriou
    return new Context(view, this);
127 4e03ba30 Kostas Papadimitriou
  };
128 4e03ba30 Kostas Papadimitriou
129 4e03ba30 Kostas Papadimitriou
  Context.prototype.lookup = function (name) {
130 4e03ba30 Kostas Papadimitriou
    var value = this._cache[name];
131 4e03ba30 Kostas Papadimitriou
132 4e03ba30 Kostas Papadimitriou
    if (!value) {
133 4e03ba30 Kostas Papadimitriou
      if (name == '.') {
134 4e03ba30 Kostas Papadimitriou
        value = this.view;
135 4e03ba30 Kostas Papadimitriou
      } else {
136 4e03ba30 Kostas Papadimitriou
        var context = this;
137 4e03ba30 Kostas Papadimitriou
138 4e03ba30 Kostas Papadimitriou
        while (context) {
139 4e03ba30 Kostas Papadimitriou
          if (name.indexOf('.') > 0) {
140 4e03ba30 Kostas Papadimitriou
            value = context.view;
141 4e03ba30 Kostas Papadimitriou
            var names = name.split('.'), i = 0;
142 4e03ba30 Kostas Papadimitriou
            while (value && i < names.length) {
143 4e03ba30 Kostas Papadimitriou
              value = value[names[i++]];
144 4e03ba30 Kostas Papadimitriou
            }
145 4e03ba30 Kostas Papadimitriou
          } else {
146 4e03ba30 Kostas Papadimitriou
            value = context.view[name];
147 4e03ba30 Kostas Papadimitriou
          }
148 4e03ba30 Kostas Papadimitriou
149 4e03ba30 Kostas Papadimitriou
          if (value != null) break;
150 4e03ba30 Kostas Papadimitriou
151 4e03ba30 Kostas Papadimitriou
          context = context.parent;
152 4e03ba30 Kostas Papadimitriou
        }
153 4e03ba30 Kostas Papadimitriou
      }
154 4e03ba30 Kostas Papadimitriou
155 4e03ba30 Kostas Papadimitriou
      this._cache[name] = value;
156 4e03ba30 Kostas Papadimitriou
    }
157 4e03ba30 Kostas Papadimitriou
158 4e03ba30 Kostas Papadimitriou
    if (typeof value === 'function') value = value.call(this.view);
159 4e03ba30 Kostas Papadimitriou
160 4e03ba30 Kostas Papadimitriou
    return value;
161 4e03ba30 Kostas Papadimitriou
  };
162 4e03ba30 Kostas Papadimitriou
163 4e03ba30 Kostas Papadimitriou
  function Writer() {
164 4e03ba30 Kostas Papadimitriou
    this.clearCache();
165 4e03ba30 Kostas Papadimitriou
  }
166 4e03ba30 Kostas Papadimitriou
167 4e03ba30 Kostas Papadimitriou
  Writer.prototype.clearCache = function () {
168 4e03ba30 Kostas Papadimitriou
    this._cache = {};
169 4e03ba30 Kostas Papadimitriou
    this._partialCache = {};
170 4e03ba30 Kostas Papadimitriou
  };
171 4e03ba30 Kostas Papadimitriou
172 4e03ba30 Kostas Papadimitriou
  Writer.prototype.compile = function (template, tags) {
173 4e03ba30 Kostas Papadimitriou
    var fn = this._cache[template];
174 4e03ba30 Kostas Papadimitriou
175 4e03ba30 Kostas Papadimitriou
    if (!fn) {
176 4e03ba30 Kostas Papadimitriou
      var tokens = mustache.parse(template, tags);
177 4e03ba30 Kostas Papadimitriou
      fn = this._cache[template] = this.compileTokens(tokens, template);
178 4e03ba30 Kostas Papadimitriou
    }
179 4e03ba30 Kostas Papadimitriou
180 4e03ba30 Kostas Papadimitriou
    return fn;
181 4e03ba30 Kostas Papadimitriou
  };
182 4e03ba30 Kostas Papadimitriou
183 4e03ba30 Kostas Papadimitriou
  Writer.prototype.compilePartial = function (name, template, tags) {
184 4e03ba30 Kostas Papadimitriou
    var fn = this.compile(template, tags);
185 4e03ba30 Kostas Papadimitriou
    this._partialCache[name] = fn;
186 4e03ba30 Kostas Papadimitriou
    return fn;
187 4e03ba30 Kostas Papadimitriou
  };
188 4e03ba30 Kostas Papadimitriou
189 4e03ba30 Kostas Papadimitriou
  Writer.prototype.getPartial = function (name) {
190 4e03ba30 Kostas Papadimitriou
    if (!(name in this._partialCache) && this._loadPartial) {
191 4e03ba30 Kostas Papadimitriou
      this.compilePartial(name, this._loadPartial(name));
192 4e03ba30 Kostas Papadimitriou
    }
193 4e03ba30 Kostas Papadimitriou
194 4e03ba30 Kostas Papadimitriou
    return this._partialCache[name];
195 4e03ba30 Kostas Papadimitriou
  };
196 4e03ba30 Kostas Papadimitriou
197 4e03ba30 Kostas Papadimitriou
  Writer.prototype.compileTokens = function (tokens, template) {
198 4e03ba30 Kostas Papadimitriou
    var self = this;
199 4e03ba30 Kostas Papadimitriou
    return function (view, partials) {
200 4e03ba30 Kostas Papadimitriou
      if (partials) {
201 4e03ba30 Kostas Papadimitriou
        if (typeof partials === 'function') {
202 4e03ba30 Kostas Papadimitriou
          self._loadPartial = partials;
203 4e03ba30 Kostas Papadimitriou
        } else {
204 4e03ba30 Kostas Papadimitriou
          for (var name in partials) {
205 4e03ba30 Kostas Papadimitriou
            self.compilePartial(name, partials[name]);
206 4e03ba30 Kostas Papadimitriou
          }
207 4e03ba30 Kostas Papadimitriou
        }
208 4e03ba30 Kostas Papadimitriou
      }
209 4e03ba30 Kostas Papadimitriou
210 4e03ba30 Kostas Papadimitriou
      return renderTokens(tokens, self, Context.make(view), template);
211 4e03ba30 Kostas Papadimitriou
    };
212 4e03ba30 Kostas Papadimitriou
  };
213 4e03ba30 Kostas Papadimitriou
214 4e03ba30 Kostas Papadimitriou
  Writer.prototype.render = function (template, view, partials) {
215 4e03ba30 Kostas Papadimitriou
    return this.compile(template)(view, partials);
216 4e03ba30 Kostas Papadimitriou
  };
217 4e03ba30 Kostas Papadimitriou
218 4e03ba30 Kostas Papadimitriou
  /**
219 4e03ba30 Kostas Papadimitriou
   * Low-level function that renders the given `tokens` using the given `writer`
220 4e03ba30 Kostas Papadimitriou
   * and `context`. The `template` string is only needed for templates that use
221 4e03ba30 Kostas Papadimitriou
   * higher-order sections to extract the portion of the original template that
222 4e03ba30 Kostas Papadimitriou
   * was contained in that section.
223 4e03ba30 Kostas Papadimitriou
   */
224 4e03ba30 Kostas Papadimitriou
  function renderTokens(tokens, writer, context, template) {
225 4e03ba30 Kostas Papadimitriou
    var buffer = '';
226 4e03ba30 Kostas Papadimitriou
227 4e03ba30 Kostas Papadimitriou
    var token, tokenValue, value;
228 4e03ba30 Kostas Papadimitriou
    for (var i = 0, len = tokens.length; i < len; ++i) {
229 4e03ba30 Kostas Papadimitriou
      token = tokens[i];
230 4e03ba30 Kostas Papadimitriou
      tokenValue = token[1];
231 4e03ba30 Kostas Papadimitriou
232 4e03ba30 Kostas Papadimitriou
      switch (token[0]) {
233 4e03ba30 Kostas Papadimitriou
      case '#':
234 4e03ba30 Kostas Papadimitriou
        value = context.lookup(tokenValue);
235 4e03ba30 Kostas Papadimitriou
236 4e03ba30 Kostas Papadimitriou
        if (typeof value === 'object') {
237 4e03ba30 Kostas Papadimitriou
          if (isArray(value)) {
238 4e03ba30 Kostas Papadimitriou
            for (var j = 0, jlen = value.length; j < jlen; ++j) {
239 4e03ba30 Kostas Papadimitriou
              buffer += renderTokens(token[4], writer, context.push(value[j]), template);
240 4e03ba30 Kostas Papadimitriou
            }
241 4e03ba30 Kostas Papadimitriou
          } else if (value) {
242 4e03ba30 Kostas Papadimitriou
            buffer += renderTokens(token[4], writer, context.push(value), template);
243 4e03ba30 Kostas Papadimitriou
          }
244 4e03ba30 Kostas Papadimitriou
        } else if (typeof value === 'function') {
245 4e03ba30 Kostas Papadimitriou
          var text = template == null ? null : template.slice(token[3], token[5]);
246 4e03ba30 Kostas Papadimitriou
          value = value.call(context.view, text, function (template) {
247 4e03ba30 Kostas Papadimitriou
            return writer.render(template, context);
248 4e03ba30 Kostas Papadimitriou
          });
249 4e03ba30 Kostas Papadimitriou
          if (value != null) buffer += value;
250 4e03ba30 Kostas Papadimitriou
        } else if (value) {
251 4e03ba30 Kostas Papadimitriou
          buffer += renderTokens(token[4], writer, context, template);
252 4e03ba30 Kostas Papadimitriou
        }
253 4e03ba30 Kostas Papadimitriou
254 4e03ba30 Kostas Papadimitriou
        break;
255 4e03ba30 Kostas Papadimitriou
      case '^':
256 4e03ba30 Kostas Papadimitriou
        value = context.lookup(tokenValue);
257 4e03ba30 Kostas Papadimitriou
258 4e03ba30 Kostas Papadimitriou
        // Use JavaScript's definition of falsy. Include empty arrays.
259 4e03ba30 Kostas Papadimitriou
        // See https://github.com/janl/mustache.js/issues/186
260 4e03ba30 Kostas Papadimitriou
        if (!value || (isArray(value) && value.length === 0)) {
261 4e03ba30 Kostas Papadimitriou
          buffer += renderTokens(token[4], writer, context, template);
262 4e03ba30 Kostas Papadimitriou
        }
263 4e03ba30 Kostas Papadimitriou
264 4e03ba30 Kostas Papadimitriou
        break;
265 4e03ba30 Kostas Papadimitriou
      case '>':
266 4e03ba30 Kostas Papadimitriou
        value = writer.getPartial(tokenValue);
267 4e03ba30 Kostas Papadimitriou
        if (typeof value === 'function') buffer += value(context);
268 4e03ba30 Kostas Papadimitriou
        break;
269 4e03ba30 Kostas Papadimitriou
      case '&':
270 4e03ba30 Kostas Papadimitriou
        value = context.lookup(tokenValue);
271 4e03ba30 Kostas Papadimitriou
        if (value != null) buffer += value;
272 4e03ba30 Kostas Papadimitriou
        break;
273 4e03ba30 Kostas Papadimitriou
      case 'name':
274 4e03ba30 Kostas Papadimitriou
        value = context.lookup(tokenValue);
275 4e03ba30 Kostas Papadimitriou
        if (value != null) buffer += mustache.escape(value);
276 4e03ba30 Kostas Papadimitriou
        break;
277 4e03ba30 Kostas Papadimitriou
      case 'text':
278 4e03ba30 Kostas Papadimitriou
        buffer += tokenValue;
279 4e03ba30 Kostas Papadimitriou
        break;
280 4e03ba30 Kostas Papadimitriou
      }
281 4e03ba30 Kostas Papadimitriou
    }
282 4e03ba30 Kostas Papadimitriou
283 4e03ba30 Kostas Papadimitriou
    return buffer;
284 4e03ba30 Kostas Papadimitriou
  }
285 4e03ba30 Kostas Papadimitriou
286 4e03ba30 Kostas Papadimitriou
  /**
287 4e03ba30 Kostas Papadimitriou
   * Forms the given array of `tokens` into a nested tree structure where
288 4e03ba30 Kostas Papadimitriou
   * tokens that represent a section have two additional items: 1) an array of
289 4e03ba30 Kostas Papadimitriou
   * all tokens that appear in that section and 2) the index in the original
290 4e03ba30 Kostas Papadimitriou
   * template that represents the end of that section.
291 4e03ba30 Kostas Papadimitriou
   */
292 4e03ba30 Kostas Papadimitriou
  function nestTokens(tokens) {
293 4e03ba30 Kostas Papadimitriou
    var tree = [];
294 4e03ba30 Kostas Papadimitriou
    var collector = tree;
295 4e03ba30 Kostas Papadimitriou
    var sections = [];
296 4e03ba30 Kostas Papadimitriou
297 4e03ba30 Kostas Papadimitriou
    var token;
298 4e03ba30 Kostas Papadimitriou
    for (var i = 0, len = tokens.length; i < len; ++i) {
299 4e03ba30 Kostas Papadimitriou
      token = tokens[i];
300 4e03ba30 Kostas Papadimitriou
      switch (token[0]) {
301 4e03ba30 Kostas Papadimitriou
      case '#':
302 4e03ba30 Kostas Papadimitriou
      case '^':
303 4e03ba30 Kostas Papadimitriou
        sections.push(token);
304 4e03ba30 Kostas Papadimitriou
        collector.push(token);
305 4e03ba30 Kostas Papadimitriou
        collector = token[4] = [];
306 4e03ba30 Kostas Papadimitriou
        break;
307 4e03ba30 Kostas Papadimitriou
      case '/':
308 4e03ba30 Kostas Papadimitriou
        var section = sections.pop();
309 4e03ba30 Kostas Papadimitriou
        section[5] = token[2];
310 4e03ba30 Kostas Papadimitriou
        collector = sections.length > 0 ? sections[sections.length - 1][4] : tree;
311 4e03ba30 Kostas Papadimitriou
        break;
312 4e03ba30 Kostas Papadimitriou
      default:
313 4e03ba30 Kostas Papadimitriou
        collector.push(token);
314 4e03ba30 Kostas Papadimitriou
      }
315 4e03ba30 Kostas Papadimitriou
    }
316 4e03ba30 Kostas Papadimitriou
317 4e03ba30 Kostas Papadimitriou
    return tree;
318 4e03ba30 Kostas Papadimitriou
  }
319 4e03ba30 Kostas Papadimitriou
320 4e03ba30 Kostas Papadimitriou
  /**
321 4e03ba30 Kostas Papadimitriou
   * Combines the values of consecutive text tokens in the given `tokens` array
322 4e03ba30 Kostas Papadimitriou
   * to a single token.
323 4e03ba30 Kostas Papadimitriou
   */
324 4e03ba30 Kostas Papadimitriou
  function squashTokens(tokens) {
325 4e03ba30 Kostas Papadimitriou
    var squashedTokens = [];
326 4e03ba30 Kostas Papadimitriou
327 4e03ba30 Kostas Papadimitriou
    var token, lastToken;
328 4e03ba30 Kostas Papadimitriou
    for (var i = 0, len = tokens.length; i < len; ++i) {
329 4e03ba30 Kostas Papadimitriou
      token = tokens[i];
330 4e03ba30 Kostas Papadimitriou
      if (token) {
331 4e03ba30 Kostas Papadimitriou
        if (token[0] === 'text' && lastToken && lastToken[0] === 'text') {
332 4e03ba30 Kostas Papadimitriou
          lastToken[1] += token[1];
333 4e03ba30 Kostas Papadimitriou
          lastToken[3] = token[3];
334 4e03ba30 Kostas Papadimitriou
        } else {
335 4e03ba30 Kostas Papadimitriou
          lastToken = token;
336 4e03ba30 Kostas Papadimitriou
          squashedTokens.push(token);
337 4e03ba30 Kostas Papadimitriou
        }
338 4e03ba30 Kostas Papadimitriou
      }
339 4e03ba30 Kostas Papadimitriou
    }
340 4e03ba30 Kostas Papadimitriou
341 4e03ba30 Kostas Papadimitriou
    return squashedTokens;
342 4e03ba30 Kostas Papadimitriou
  }
343 4e03ba30 Kostas Papadimitriou
344 4e03ba30 Kostas Papadimitriou
  function escapeTags(tags) {
345 4e03ba30 Kostas Papadimitriou
    return [
346 4e03ba30 Kostas Papadimitriou
      new RegExp(escapeRe(tags[0]) + "\\s*"),
347 4e03ba30 Kostas Papadimitriou
      new RegExp("\\s*" + escapeRe(tags[1]))
348 4e03ba30 Kostas Papadimitriou
    ];
349 4e03ba30 Kostas Papadimitriou
  }
350 4e03ba30 Kostas Papadimitriou
351 4e03ba30 Kostas Papadimitriou
  /**
352 4e03ba30 Kostas Papadimitriou
   * Breaks up the given `template` string into a tree of token objects. If
353 4e03ba30 Kostas Papadimitriou
   * `tags` is given here it must be an array with two string values: the
354 4e03ba30 Kostas Papadimitriou
   * opening and closing tags used in the template (e.g. ["<%", "%>"]). Of
355 4e03ba30 Kostas Papadimitriou
   * course, the default is to use mustaches (i.e. Mustache.tags).
356 4e03ba30 Kostas Papadimitriou
   */
357 4e03ba30 Kostas Papadimitriou
  function parseTemplate(template, tags) {
358 4e03ba30 Kostas Papadimitriou
    template = template || '';
359 4e03ba30 Kostas Papadimitriou
    tags = tags || mustache.tags;
360 4e03ba30 Kostas Papadimitriou
361 4e03ba30 Kostas Papadimitriou
    if (typeof tags === 'string') tags = tags.split(spaceRe);
362 4e03ba30 Kostas Papadimitriou
    if (tags.length !== 2) throw new Error('Invalid tags: ' + tags.join(', '));
363 4e03ba30 Kostas Papadimitriou
364 4e03ba30 Kostas Papadimitriou
    var tagRes = escapeTags(tags);
365 4e03ba30 Kostas Papadimitriou
    var scanner = new Scanner(template);
366 4e03ba30 Kostas Papadimitriou
367 4e03ba30 Kostas Papadimitriou
    var sections = [];     // Stack to hold section tokens
368 4e03ba30 Kostas Papadimitriou
    var tokens = [];       // Buffer to hold the tokens
369 4e03ba30 Kostas Papadimitriou
    var spaces = [];       // Indices of whitespace tokens on the current line
370 4e03ba30 Kostas Papadimitriou
    var hasTag = false;    // Is there a {{tag}} on the current line?
371 4e03ba30 Kostas Papadimitriou
    var nonSpace = false;  // Is there a non-space char on the current line?
372 4e03ba30 Kostas Papadimitriou
373 4e03ba30 Kostas Papadimitriou
    // Strips all whitespace tokens array for the current line
374 4e03ba30 Kostas Papadimitriou
    // if there was a {{#tag}} on it and otherwise only space.
375 4e03ba30 Kostas Papadimitriou
    function stripSpace() {
376 4e03ba30 Kostas Papadimitriou
      if (hasTag && !nonSpace) {
377 4e03ba30 Kostas Papadimitriou
        while (spaces.length) {
378 4e03ba30 Kostas Papadimitriou
          delete tokens[spaces.pop()];
379 4e03ba30 Kostas Papadimitriou
        }
380 4e03ba30 Kostas Papadimitriou
      } else {
381 4e03ba30 Kostas Papadimitriou
        spaces = [];
382 4e03ba30 Kostas Papadimitriou
      }
383 4e03ba30 Kostas Papadimitriou
384 4e03ba30 Kostas Papadimitriou
      hasTag = false;
385 4e03ba30 Kostas Papadimitriou
      nonSpace = false;
386 4e03ba30 Kostas Papadimitriou
    }
387 4e03ba30 Kostas Papadimitriou
388 4e03ba30 Kostas Papadimitriou
    var start, type, value, chr, token;
389 4e03ba30 Kostas Papadimitriou
    while (!scanner.eos()) {
390 4e03ba30 Kostas Papadimitriou
      start = scanner.pos;
391 4e03ba30 Kostas Papadimitriou
392 4e03ba30 Kostas Papadimitriou
      // Match any text between tags.
393 4e03ba30 Kostas Papadimitriou
      value = scanner.scanUntil(tagRes[0]);
394 4e03ba30 Kostas Papadimitriou
      if (value) {
395 4e03ba30 Kostas Papadimitriou
        for (var i = 0, len = value.length; i < len; ++i) {
396 4e03ba30 Kostas Papadimitriou
          chr = value.charAt(i);
397 4e03ba30 Kostas Papadimitriou
398 4e03ba30 Kostas Papadimitriou
          if (isWhitespace(chr)) {
399 4e03ba30 Kostas Papadimitriou
            spaces.push(tokens.length);
400 4e03ba30 Kostas Papadimitriou
          } else {
401 4e03ba30 Kostas Papadimitriou
            nonSpace = true;
402 4e03ba30 Kostas Papadimitriou
          }
403 4e03ba30 Kostas Papadimitriou
404 4e03ba30 Kostas Papadimitriou
          tokens.push(['text', chr, start, start + 1]);
405 4e03ba30 Kostas Papadimitriou
          start += 1;
406 4e03ba30 Kostas Papadimitriou
407 4e03ba30 Kostas Papadimitriou
          // Check for whitespace on the current line.
408 4e03ba30 Kostas Papadimitriou
          if (chr == '\n') stripSpace();
409 4e03ba30 Kostas Papadimitriou
        }
410 4e03ba30 Kostas Papadimitriou
      }
411 4e03ba30 Kostas Papadimitriou
412 4e03ba30 Kostas Papadimitriou
      // Match the opening tag.
413 4e03ba30 Kostas Papadimitriou
      if (!scanner.scan(tagRes[0])) break;
414 4e03ba30 Kostas Papadimitriou
      hasTag = true;
415 4e03ba30 Kostas Papadimitriou
416 4e03ba30 Kostas Papadimitriou
      // Get the tag type.
417 4e03ba30 Kostas Papadimitriou
      type = scanner.scan(tagRe) || 'name';
418 4e03ba30 Kostas Papadimitriou
      scanner.scan(whiteRe);
419 4e03ba30 Kostas Papadimitriou
420 4e03ba30 Kostas Papadimitriou
      // Get the tag value.
421 4e03ba30 Kostas Papadimitriou
      if (type === '=') {
422 4e03ba30 Kostas Papadimitriou
        value = scanner.scanUntil(eqRe);
423 4e03ba30 Kostas Papadimitriou
        scanner.scan(eqRe);
424 4e03ba30 Kostas Papadimitriou
        scanner.scanUntil(tagRes[1]);
425 4e03ba30 Kostas Papadimitriou
      } else if (type === '{') {
426 4e03ba30 Kostas Papadimitriou
        value = scanner.scanUntil(new RegExp('\\s*' + escapeRe('}' + tags[1])));
427 4e03ba30 Kostas Papadimitriou
        scanner.scan(curlyRe);
428 4e03ba30 Kostas Papadimitriou
        scanner.scanUntil(tagRes[1]);
429 4e03ba30 Kostas Papadimitriou
        type = '&';
430 4e03ba30 Kostas Papadimitriou
      } else {
431 4e03ba30 Kostas Papadimitriou
        value = scanner.scanUntil(tagRes[1]);
432 4e03ba30 Kostas Papadimitriou
      }
433 4e03ba30 Kostas Papadimitriou
434 4e03ba30 Kostas Papadimitriou
      // Match the closing tag.
435 4e03ba30 Kostas Papadimitriou
      if (!scanner.scan(tagRes[1])) throw new Error('Unclosed tag at ' + scanner.pos);
436 4e03ba30 Kostas Papadimitriou
437 4e03ba30 Kostas Papadimitriou
      token = [type, value, start, scanner.pos];
438 4e03ba30 Kostas Papadimitriou
      tokens.push(token);
439 4e03ba30 Kostas Papadimitriou
440 4e03ba30 Kostas Papadimitriou
      if (type === '#' || type === '^') {
441 4e03ba30 Kostas Papadimitriou
        sections.push(token);
442 4e03ba30 Kostas Papadimitriou
      } else if (type === '/') {
443 4e03ba30 Kostas Papadimitriou
        // Check section nesting.
444 4e03ba30 Kostas Papadimitriou
        if (sections.length === 0) throw new Error('Unopened section "' + value + '" at ' + start);
445 4e03ba30 Kostas Papadimitriou
        var openSection = sections.pop();
446 4e03ba30 Kostas Papadimitriou
        if (openSection[1] !== value) throw new Error('Unclosed section "' + openSection[1] + '" at ' + start);
447 4e03ba30 Kostas Papadimitriou
      } else if (type === 'name' || type === '{' || type === '&') {
448 4e03ba30 Kostas Papadimitriou
        nonSpace = true;
449 4e03ba30 Kostas Papadimitriou
      } else if (type === '=') {
450 4e03ba30 Kostas Papadimitriou
        // Set the tags for the next time around.
451 4e03ba30 Kostas Papadimitriou
        tags = value.split(spaceRe);
452 4e03ba30 Kostas Papadimitriou
        if (tags.length !== 2) throw new Error('Invalid tags at ' + start + ': ' + tags.join(', '));
453 4e03ba30 Kostas Papadimitriou
        tagRes = escapeTags(tags);
454 4e03ba30 Kostas Papadimitriou
      }
455 4e03ba30 Kostas Papadimitriou
    }
456 4e03ba30 Kostas Papadimitriou
457 4e03ba30 Kostas Papadimitriou
    // Make sure there are no open sections when we're done.
458 4e03ba30 Kostas Papadimitriou
    var openSection = sections.pop();
459 4e03ba30 Kostas Papadimitriou
    if (openSection) throw new Error('Unclosed section "' + openSection[1] + '" at ' + scanner.pos);
460 4e03ba30 Kostas Papadimitriou
461 4e03ba30 Kostas Papadimitriou
    tokens = squashTokens(tokens);
462 4e03ba30 Kostas Papadimitriou
463 4e03ba30 Kostas Papadimitriou
    return nestTokens(tokens);
464 4e03ba30 Kostas Papadimitriou
  }
465 4e03ba30 Kostas Papadimitriou
466 4e03ba30 Kostas Papadimitriou
  var mustache = {};
467 4e03ba30 Kostas Papadimitriou
468 4e03ba30 Kostas Papadimitriou
  mustache.name = "mustache.js";
469 4e03ba30 Kostas Papadimitriou
  mustache.version = "0.7.2";
470 4e03ba30 Kostas Papadimitriou
  mustache.tags = ["{{", "}}"];
471 4e03ba30 Kostas Papadimitriou
472 4e03ba30 Kostas Papadimitriou
  mustache.Scanner = Scanner;
473 4e03ba30 Kostas Papadimitriou
  mustache.Context = Context;
474 4e03ba30 Kostas Papadimitriou
  mustache.Writer = Writer;
475 4e03ba30 Kostas Papadimitriou
476 4e03ba30 Kostas Papadimitriou
  mustache.parse = parseTemplate;
477 4e03ba30 Kostas Papadimitriou
478 4e03ba30 Kostas Papadimitriou
  // Export the escaping function so that the user may override it.
479 4e03ba30 Kostas Papadimitriou
  // See https://github.com/janl/mustache.js/issues/244
480 4e03ba30 Kostas Papadimitriou
  mustache.escape = escapeHtml;
481 4e03ba30 Kostas Papadimitriou
482 4e03ba30 Kostas Papadimitriou
  // All Mustache.* functions use this writer.
483 4e03ba30 Kostas Papadimitriou
  var _writer = new Writer();
484 4e03ba30 Kostas Papadimitriou
485 4e03ba30 Kostas Papadimitriou
  /**
486 4e03ba30 Kostas Papadimitriou
   * Clears all cached templates and partials in the default writer.
487 4e03ba30 Kostas Papadimitriou
   */
488 4e03ba30 Kostas Papadimitriou
  mustache.clearCache = function () {
489 4e03ba30 Kostas Papadimitriou
    return _writer.clearCache();
490 4e03ba30 Kostas Papadimitriou
  };
491 4e03ba30 Kostas Papadimitriou
492 4e03ba30 Kostas Papadimitriou
  /**
493 4e03ba30 Kostas Papadimitriou
   * Compiles the given `template` to a reusable function using the default
494 4e03ba30 Kostas Papadimitriou
   * writer.
495 4e03ba30 Kostas Papadimitriou
   */
496 4e03ba30 Kostas Papadimitriou
  mustache.compile = function (template, tags) {
497 4e03ba30 Kostas Papadimitriou
    return _writer.compile(template, tags);
498 4e03ba30 Kostas Papadimitriou
  };
499 4e03ba30 Kostas Papadimitriou
500 4e03ba30 Kostas Papadimitriou
  /**
501 4e03ba30 Kostas Papadimitriou
   * Compiles the partial with the given `name` and `template` to a reusable
502 4e03ba30 Kostas Papadimitriou
   * function using the default writer.
503 4e03ba30 Kostas Papadimitriou
   */
504 4e03ba30 Kostas Papadimitriou
  mustache.compilePartial = function (name, template, tags) {
505 4e03ba30 Kostas Papadimitriou
    return _writer.compilePartial(name, template, tags);
506 4e03ba30 Kostas Papadimitriou
  };
507 4e03ba30 Kostas Papadimitriou
508 4e03ba30 Kostas Papadimitriou
  /**
509 4e03ba30 Kostas Papadimitriou
   * Compiles the given array of tokens (the output of a parse) to a reusable
510 4e03ba30 Kostas Papadimitriou
   * function using the default writer.
511 4e03ba30 Kostas Papadimitriou
   */
512 4e03ba30 Kostas Papadimitriou
  mustache.compileTokens = function (tokens, template) {
513 4e03ba30 Kostas Papadimitriou
    return _writer.compileTokens(tokens, template);
514 4e03ba30 Kostas Papadimitriou
  };
515 4e03ba30 Kostas Papadimitriou
516 4e03ba30 Kostas Papadimitriou
  /**
517 4e03ba30 Kostas Papadimitriou
   * Renders the `template` with the given `view` and `partials` using the
518 4e03ba30 Kostas Papadimitriou
   * default writer.
519 4e03ba30 Kostas Papadimitriou
   */
520 4e03ba30 Kostas Papadimitriou
  mustache.render = function (template, view, partials) {
521 4e03ba30 Kostas Papadimitriou
    return _writer.render(template, view, partials);
522 4e03ba30 Kostas Papadimitriou
  };
523 4e03ba30 Kostas Papadimitriou
524 4e03ba30 Kostas Papadimitriou
  // This is here for backwards compatibility with 0.4.x.
525 4e03ba30 Kostas Papadimitriou
  mustache.to_html = function (template, view, partials, send) {
526 4e03ba30 Kostas Papadimitriou
    var result = mustache.render(template, view, partials);
527 4e03ba30 Kostas Papadimitriou
528 4e03ba30 Kostas Papadimitriou
    if (typeof send === "function") {
529 4e03ba30 Kostas Papadimitriou
      send(result);
530 4e03ba30 Kostas Papadimitriou
    } else {
531 4e03ba30 Kostas Papadimitriou
      return result;
532 4e03ba30 Kostas Papadimitriou
    }
533 4e03ba30 Kostas Papadimitriou
  };
534 4e03ba30 Kostas Papadimitriou
535 4e03ba30 Kostas Papadimitriou
  return mustache;
536 4e03ba30 Kostas Papadimitriou
537 4e03ba30 Kostas Papadimitriou
}())));