Add scripts to create myops-getqueryview:
[myops.git] / web / query / vendor / couchapp / _attachments / jquery.mustache.js
1 /*
2 Shameless port of a shameless port
3 @defunkt => @janl => @aq
4  
5 See http://github.com/defunkt/mustache for more info.
6 */
7  
8 ;(function($) {
9
10 /*
11   mustache.js — Logic-less templates in JavaScript
12
13   See http://mustache.github.com/ for more info.
14 */
15
16 var Mustache = function() {
17   var Renderer = function() {};
18
19   Renderer.prototype = {
20     otag: "{{",
21     ctag: "}}",
22     pragmas: {},
23     buffer: [],
24     pragmas_implemented: {
25       "IMPLICIT-ITERATOR": true
26     },
27     context: {},
28
29     render: function(template, context, partials, in_recursion) {
30       // reset buffer & set context
31       if(!in_recursion) {
32         this.context = context;
33         this.buffer = []; // TODO: make this non-lazy
34       }
35
36       // fail fast
37       if(!this.includes("", template)) {
38         if(in_recursion) {
39           return template;
40         } else {
41           this.send(template);
42           return;
43         }
44       }
45
46       template = this.render_pragmas(template);
47       var html = this.render_section(template, context, partials);
48       if(in_recursion) {
49         return this.render_tags(html, context, partials, in_recursion);
50       }
51
52       this.render_tags(html, context, partials, in_recursion);
53     },
54
55     /*
56       Sends parsed lines
57     */
58     send: function(line) {
59       if(line != "") {
60         this.buffer.push(line);
61       }
62     },
63
64     /*
65       Looks for %PRAGMAS
66     */
67     render_pragmas: function(template) {
68       // no pragmas
69       if(!this.includes("%", template)) {
70         return template;
71       }
72
73       var that = this;
74       var regex = new RegExp(this.otag + "%([\\w-]+) ?([\\w]+=[\\w]+)?" +
75             this.ctag);
76       return template.replace(regex, function(match, pragma, options) {
77         if(!that.pragmas_implemented[pragma]) {
78           throw({message: 
79             "This implementation of mustache doesn't understand the '" +
80             pragma + "' pragma"});
81         }
82         that.pragmas[pragma] = {};
83         if(options) {
84           var opts = options.split("=");
85           that.pragmas[pragma][opts[0]] = opts[1];
86         }
87         return "";
88         // ignore unknown pragmas silently
89       });
90     },
91
92     /*
93       Tries to find a partial in the curent scope and render it
94     */
95     render_partial: function(name, context, partials) {
96       name = this.trim(name);
97       if(!partials || partials[name] === undefined) {
98         throw({message: "unknown_partial '" + name + "'"});
99       }
100       if(typeof(context[name]) != "object") {
101         return this.render(partials[name], context, partials, true);
102       }
103       return this.render(partials[name], context[name], partials, true);
104     },
105
106     /*
107       Renders inverted (^) and normal (#) sections
108     */
109     render_section: function(template, context, partials) {
110       if(!this.includes("#", template) && !this.includes("^", template)) {
111         return template;
112       }
113
114       var that = this;
115       // CSW - Added "+?" so it finds the tighest bound, not the widest
116       var regex = new RegExp(this.otag + "(\\^|\\#)\\s*(.+)\\s*" + this.ctag +
117               "\n*([\\s\\S]+?)" + this.otag + "\\/\\s*\\2\\s*" + this.ctag +
118               "\\s*", "mg");
119
120       // for each {{#foo}}{{/foo}} section do...
121       return template.replace(regex, function(match, type, name, content) {
122         var value = that.find(name, context);
123         if(type == "^") { // inverted section
124           if(!value || that.is_array(value) && value.length === 0) {
125             // false or empty list, render it
126             return that.render(content, context, partials, true);
127           } else {
128             return "";
129           }
130         } else if(type == "#") { // normal section
131           if(that.is_array(value)) { // Enumerable, Let's loop!
132             return that.map(value, function(row) {
133               return that.render(content, that.create_context(row),
134                 partials, true);
135             }).join("");
136           } else if(that.is_object(value)) { // Object, Use it as subcontext!
137             return that.render(content, that.create_context(value),
138               partials, true);
139           } else if(typeof value === "function") {
140             // higher order section
141             return value.call(context, content, function(text) {
142               return that.render(text, context, partials, true);
143             });
144           } else if(value) { // boolean section
145             return that.render(content, context, partials, true);
146           } else {
147             return "";
148           }
149         }
150       });
151     },
152
153     /*
154       Replace {{foo}} and friends with values from our view
155     */
156     render_tags: function(template, context, partials, in_recursion) {
157       // tit for tat
158       var that = this;
159
160       var new_regex = function() {
161         return new RegExp(that.otag + "(=|!|>|\\{|%)?([^\\/#\\^]+?)\\1?" +
162           that.ctag + "+", "g");
163       };
164
165       var regex = new_regex();
166       var tag_replace_callback = function(match, operator, name) {
167         switch(operator) {
168         case "!": // ignore comments
169           return "";
170         case "=": // set new delimiters, rebuild the replace regexp
171           that.set_delimiters(name);
172           regex = new_regex();
173           return "";
174         case ">": // render partial
175           return that.render_partial(name, context, partials);
176         case "{": // the triple mustache is unescaped
177           return that.find(name, context);
178         default: // escape the value
179           return that.escape(that.find(name, context));
180         }
181       };
182       var lines = template.split("\n");
183       for(var i = 0; i < lines.length; i++) {
184         lines[i] = lines[i].replace(regex, tag_replace_callback, this);
185         if(!in_recursion) {
186           this.send(lines[i]);
187         }
188       }
189
190       if(in_recursion) {
191         return lines.join("\n");
192       }
193     },
194
195     set_delimiters: function(delimiters) {
196       var dels = delimiters.split(" ");
197       this.otag = this.escape_regex(dels[0]);
198       this.ctag = this.escape_regex(dels[1]);
199     },
200
201     escape_regex: function(text) {
202       // thank you Simon Willison
203       if(!arguments.callee.sRE) {
204         var specials = [
205           '/', '.', '*', '+', '?', '|',
206           '(', ')', '[', ']', '{', '}', '\\'
207         ];
208         arguments.callee.sRE = new RegExp(
209           '(\\' + specials.join('|\\') + ')', 'g'
210         );
211       }
212       return text.replace(arguments.callee.sRE, '\\$1');
213     },
214
215     /*
216       find `name` in current `context`. That is find me a value
217       from the view object
218     */
219     find: function(name, context) {
220       name = this.trim(name);
221
222       // Checks whether a value is thruthy or false or 0
223       function is_kinda_truthy(bool) {
224         return bool === false || bool === 0 || bool;
225       }
226
227       var value;
228       if(is_kinda_truthy(context[name])) {
229         value = context[name];
230       } else if(is_kinda_truthy(this.context[name])) {
231         value = this.context[name];
232       }
233
234       if(typeof value === "function") {
235         return value.apply(context);
236       }
237       if(value !== undefined) {
238         return value;
239       }
240       // silently ignore unkown variables
241       return "";
242     },
243
244     // Utility methods
245
246     /* includes tag */
247     includes: function(needle, haystack) {
248       return haystack.indexOf(this.otag + needle) != -1;
249     },
250
251     /*
252       Does away with nasty characters
253     */
254     escape: function(s) {
255       s = String(s === null ? "" : s);
256       return s.replace(/&(?!\w+;)|["<>\\]/g, function(s) {
257         switch(s) {
258         case "&": return "&amp;";
259         case "\\": return "\\\\";
260         case '"': return '\"';
261         case "<": return "&lt;";
262         case ">": return "&gt;";
263         default: return s;
264         }
265       });
266     },
267
268     // by @langalex, support for arrays of strings
269     create_context: function(_context) {
270       if(this.is_object(_context)) {
271         return _context;
272       } else {
273         var iterator = ".";
274         if(this.pragmas["IMPLICIT-ITERATOR"]) {
275           iterator = this.pragmas["IMPLICIT-ITERATOR"].iterator;
276         }
277         var ctx = {};
278         ctx[iterator] = _context;
279         return ctx;
280       }
281     },
282
283     is_object: function(a) {
284       return a && typeof a == "object";
285     },
286
287     is_array: function(a) {
288       return Object.prototype.toString.call(a) === '[object Array]';
289     },
290
291     /*
292       Gets rid of leading and trailing whitespace
293     */
294     trim: function(s) {
295       return s.replace(/^\s*|\s*$/g, "");
296     },
297
298     /*
299       Why, why, why? Because IE. Cry, cry cry.
300     */
301     map: function(array, fn) {
302       if (typeof array.map == "function") {
303         return array.map(fn);
304       } else {
305         var r = [];
306         var l = array.length;
307         for(var i = 0; i < l; i++) {
308           r.push(fn(array[i]));
309         }
310         return r;
311       }
312     }
313   };
314
315   return({
316     name: "mustache.js",
317     version: "0.3.1-dev",
318
319     /*
320       Turns a template and view into HTML
321     */
322     to_html: function(template, view, partials, send_fun) {
323       var renderer = new Renderer();
324       if(send_fun) {
325         renderer.send = send_fun;
326       }
327       renderer.render(template, view, partials);
328       if(!send_fun) {
329         return renderer.buffer.join("\n");
330       }
331     },
332     escape : function(text) {
333       return new Renderer().escape(text);
334     }
335   });
336 }();
337
338   $.mustache = function(template, view, partials) {
339     return Mustache.to_html(template, view, partials);
340   };
341
342   $.mustache.escape = function(text) {
343     return Mustache.escape(text);
344   };
345
346 })(jQuery);