mindreframer-reslike-dash 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/.gitignore +8 -0
  2. data/Gemfile +7 -0
  3. data/Gemfile.lock +52 -0
  4. data/LICENSE +21 -0
  5. data/README.markdown +52 -0
  6. data/Rakefile.rb +11 -0
  7. data/bin/riemann-dash +7 -0
  8. data/example/config.rb +17 -0
  9. data/lib/riemann/dash.rb +5 -0
  10. data/lib/riemann/dash/app.rb +28 -0
  11. data/lib/riemann/dash/config.rb +154 -0
  12. data/lib/riemann/dash/controller/css.rb +5 -0
  13. data/lib/riemann/dash/controller/index.rb +20 -0
  14. data/lib/riemann/dash/public/clock.js +45 -0
  15. data/lib/riemann/dash/public/dash.js +287 -0
  16. data/lib/riemann/dash/public/format.js +24 -0
  17. data/lib/riemann/dash/public/jquery-1.7.2.min.js +4 -0
  18. data/lib/riemann/dash/public/jquery-ui-1.9.0.custom.min.js +6 -0
  19. data/lib/riemann/dash/public/jquery.json-2.2.min.js +31 -0
  20. data/lib/riemann/dash/public/jquery.quickfit.js +144 -0
  21. data/lib/riemann/dash/public/jquery.simplemodal.1.4.3.min.js +26 -0
  22. data/lib/riemann/dash/public/keys.js +46 -0
  23. data/lib/riemann/dash/public/mustache.js +597 -0
  24. data/lib/riemann/dash/public/persistence.js +30 -0
  25. data/lib/riemann/dash/public/profile.js +33 -0
  26. data/lib/riemann/dash/public/subs.js +164 -0
  27. data/lib/riemann/dash/public/toastr.css +174 -0
  28. data/lib/riemann/dash/public/toastr.js +207 -0
  29. data/lib/riemann/dash/public/toolbar.js +217 -0
  30. data/lib/riemann/dash/public/underscore-min.js +5 -0
  31. data/lib/riemann/dash/public/util.js +34 -0
  32. data/lib/riemann/dash/public/vendor/smoothie.js +374 -0
  33. data/lib/riemann/dash/public/view.js +704 -0
  34. data/lib/riemann/dash/public/views/gauge.js +76 -0
  35. data/lib/riemann/dash/public/views/grid.js +279 -0
  36. data/lib/riemann/dash/public/views/help.js +28 -0
  37. data/lib/riemann/dash/public/views/timeseries.js +107 -0
  38. data/lib/riemann/dash/public/views/title.js +35 -0
  39. data/lib/riemann/dash/public/x.png +0 -0
  40. data/lib/riemann/dash/rack/static.rb +16 -0
  41. data/lib/riemann/dash/version.rb +4 -0
  42. data/lib/riemann/dash/views/css.scss +393 -0
  43. data/lib/riemann/dash/views/index.erubis +203 -0
  44. data/lib/riemann/dash/views/layout.erubis +21 -0
  45. data/riemann-dash.gemspec +28 -0
  46. data/sh/c +1 -0
  47. data/sh/env.rb +2 -0
  48. data/sh/test +1 -0
  49. data/test/config_test.rb +106 -0
  50. data/test/fixtures/config/basic_config.rb +2 -0
  51. data/test/fixtures/config/ws_config.rb +1 -0
  52. data/test/fixtures/ws_config/dummy_config.json +1 -0
  53. data/test/fixtures/ws_config/pretty_printed_config.json +6 -0
  54. data/test/test_helper.rb +10 -0
  55. metadata +202 -0
@@ -0,0 +1,26 @@
1
+ /*
2
+ * SimpleModal 1.4.3 - jQuery Plugin
3
+ * http://simplemodal.com/
4
+ * Copyright (c) 2012 Eric Martin
5
+ * Licensed under MIT and GPL
6
+ * Date: Sat, Sep 8 2012 07:52:31 -0700
7
+ */
8
+ (function(b){"function"===typeof define&&define.amd?define(["jquery"],b):b(jQuery)})(function(b){var j=[],l=b(document),m=b.browser.msie&&6===parseInt(b.browser.version)&&"object"!==typeof window.XMLHttpRequest,o=b.browser.msie&&7===parseInt(b.browser.version),n=null,k=b(window),h=[];b.modal=function(a,d){return b.modal.impl.init(a,d)};b.modal.close=function(){b.modal.impl.close()};b.modal.focus=function(a){b.modal.impl.focus(a)};b.modal.setContainerDimensions=function(){b.modal.impl.setContainerDimensions()};
9
+ b.modal.setPosition=function(){b.modal.impl.setPosition()};b.modal.update=function(a,d){b.modal.impl.update(a,d)};b.fn.modal=function(a){return b.modal.impl.init(this,a)};b.modal.defaults={appendTo:"body",focus:!0,opacity:50,overlayId:"simplemodal-overlay",overlayCss:{},containerId:"simplemodal-container",containerCss:{},dataId:"simplemodal-data",dataCss:{},minHeight:null,minWidth:null,maxHeight:null,maxWidth:null,autoResize:!1,autoPosition:!0,zIndex:1E3,close:!0,closeHTML:'<a class="modalCloseImg" title="Close"></a>',
10
+ closeClass:"simplemodal-close",escClose:!0,overlayClose:!1,fixed:!0,position:null,persist:!1,modal:!0,onOpen:null,onShow:null,onClose:null};b.modal.impl={d:{},init:function(a,d){if(this.d.data)return!1;n=b.browser.msie&&!b.support.boxModel;this.o=b.extend({},b.modal.defaults,d);this.zIndex=this.o.zIndex;this.occb=!1;if("object"===typeof a){if(a=a instanceof b?a:b(a),this.d.placeholder=!1,0<a.parent().parent().size()&&(a.before(b("<span></span>").attr("id","simplemodal-placeholder").css({display:"none"})),
11
+ this.d.placeholder=!0,this.display=a.css("display"),!this.o.persist))this.d.orig=a.clone(!0)}else if("string"===typeof a||"number"===typeof a)a=b("<div></div>").html(a);else return alert("SimpleModal Error: Unsupported data type: "+typeof a),this;this.create(a);this.open();b.isFunction(this.o.onShow)&&this.o.onShow.apply(this,[this.d]);return this},create:function(a){this.getDimensions();if(this.o.modal&&m)this.d.iframe=b('<iframe src="javascript:false;"></iframe>').css(b.extend(this.o.iframeCss,
12
+ {display:"none",opacity:0,position:"fixed",height:h[0],width:h[1],zIndex:this.o.zIndex,top:0,left:0})).appendTo(this.o.appendTo);this.d.overlay=b("<div></div>").attr("id",this.o.overlayId).addClass("simplemodal-overlay").css(b.extend(this.o.overlayCss,{display:"none",opacity:this.o.opacity/100,height:this.o.modal?j[0]:0,width:this.o.modal?j[1]:0,position:"fixed",left:0,top:0,zIndex:this.o.zIndex+1})).appendTo(this.o.appendTo);this.d.container=b("<div></div>").attr("id",this.o.containerId).addClass("simplemodal-container").css(b.extend({position:this.o.fixed?
13
+ "fixed":"absolute"},this.o.containerCss,{display:"none",zIndex:this.o.zIndex+2})).append(this.o.close&&this.o.closeHTML?b(this.o.closeHTML).addClass(this.o.closeClass):"").appendTo(this.o.appendTo);this.d.wrap=b("<div></div>").attr("tabIndex",-1).addClass("simplemodal-wrap").css({height:"100%",outline:0,width:"100%"}).appendTo(this.d.container);this.d.data=a.attr("id",a.attr("id")||this.o.dataId).addClass("simplemodal-data").css(b.extend(this.o.dataCss,{display:"none"})).appendTo("body");this.setContainerDimensions();
14
+ this.d.data.appendTo(this.d.wrap);(m||n)&&this.fixIE()},bindEvents:function(){var a=this;b("."+a.o.closeClass).bind("click.simplemodal",function(b){b.preventDefault();a.close()});a.o.modal&&a.o.close&&a.o.overlayClose&&a.d.overlay.bind("click.simplemodal",function(b){b.preventDefault();a.close()});l.bind("keydown.simplemodal",function(b){a.o.modal&&9===b.keyCode?a.watchTab(b):a.o.close&&a.o.escClose&&27===b.keyCode&&(b.preventDefault(),a.close())});k.bind("resize.simplemodal orientationchange.simplemodal",
15
+ function(){a.getDimensions();a.o.autoResize?a.setContainerDimensions():a.o.autoPosition&&a.setPosition();m||n?a.fixIE():a.o.modal&&(a.d.iframe&&a.d.iframe.css({height:h[0],width:h[1]}),a.d.overlay.css({height:j[0],width:j[1]}))})},unbindEvents:function(){b("."+this.o.closeClass).unbind("click.simplemodal");l.unbind("keydown.simplemodal");k.unbind(".simplemodal");this.d.overlay.unbind("click.simplemodal")},fixIE:function(){var a=this.o.position;b.each([this.d.iframe||null,!this.o.modal?null:this.d.overlay,
16
+ "fixed"===this.d.container.css("position")?this.d.container:null],function(b,f){if(f){var g=f[0].style;g.position="absolute";if(2>b)g.removeExpression("height"),g.removeExpression("width"),g.setExpression("height",'document.body.scrollHeight > document.body.clientHeight ? document.body.scrollHeight : document.body.clientHeight + "px"'),g.setExpression("width",'document.body.scrollWidth > document.body.clientWidth ? document.body.scrollWidth : document.body.clientWidth + "px"');else{var c,e;a&&a.constructor===
17
+ Array?(c=a[0]?"number"===typeof a[0]?a[0].toString():a[0].replace(/px/,""):f.css("top").replace(/px/,""),c=-1===c.indexOf("%")?c+' + (t = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px"':parseInt(c.replace(/%/,""))+' * ((document.documentElement.clientHeight || document.body.clientHeight) / 100) + (t = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px"',a[1]&&(e="number"===typeof a[1]?
18
+ a[1].toString():a[1].replace(/px/,""),e=-1===e.indexOf("%")?e+' + (t = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft) + "px"':parseInt(e.replace(/%/,""))+' * ((document.documentElement.clientWidth || document.body.clientWidth) / 100) + (t = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft) + "px"')):(c='(document.documentElement.clientHeight || document.body.clientHeight) / 2 - (this.offsetHeight / 2) + (t = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px"',
19
+ e='(document.documentElement.clientWidth || document.body.clientWidth) / 2 - (this.offsetWidth / 2) + (t = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft) + "px"');g.removeExpression("top");g.removeExpression("left");g.setExpression("top",c);g.setExpression("left",e)}}})},focus:function(a){var d=this,a=a&&-1!==b.inArray(a,["first","last"])?a:"first",f=b(":input:enabled:visible:"+a,d.d.wrap);setTimeout(function(){0<f.length?f.focus():d.d.wrap.focus()},
20
+ 10)},getDimensions:function(){var a="undefined"===typeof window.innerHeight?k.height():window.innerHeight;j=[l.height(),l.width()];h=[a,k.width()]},getVal:function(a,b){return a?"number"===typeof a?a:"auto"===a?0:0<a.indexOf("%")?parseInt(a.replace(/%/,""))/100*("h"===b?h[0]:h[1]):parseInt(a.replace(/px/,"")):null},update:function(a,b){if(!this.d.data)return!1;this.d.origHeight=this.getVal(a,"h");this.d.origWidth=this.getVal(b,"w");this.d.data.hide();a&&this.d.container.css("height",a);b&&this.d.container.css("width",
21
+ b);this.setContainerDimensions();this.d.data.show();this.o.focus&&this.focus();this.unbindEvents();this.bindEvents()},setContainerDimensions:function(){var a=m||o,d=this.d.origHeight?this.d.origHeight:b.browser.opera?this.d.container.height():this.getVal(a?this.d.container[0].currentStyle.height:this.d.container.css("height"),"h"),a=this.d.origWidth?this.d.origWidth:b.browser.opera?this.d.container.width():this.getVal(a?this.d.container[0].currentStyle.width:this.d.container.css("width"),"w"),f=this.d.data.outerHeight(!0),
22
+ g=this.d.data.outerWidth(!0);this.d.origHeight=this.d.origHeight||d;this.d.origWidth=this.d.origWidth||a;var c=this.o.maxHeight?this.getVal(this.o.maxHeight,"h"):null,e=this.o.maxWidth?this.getVal(this.o.maxWidth,"w"):null,c=c&&c<h[0]?c:h[0],e=e&&e<h[1]?e:h[1],i=this.o.minHeight?this.getVal(this.o.minHeight,"h"):"auto",d=d?this.o.autoResize&&d>c?c:d<i?i:d:f?f>c?c:this.o.minHeight&&"auto"!==i&&f<i?i:f:i,c=this.o.minWidth?this.getVal(this.o.minWidth,"w"):"auto",a=a?this.o.autoResize&&a>e?e:a<c?c:a:
23
+ g?g>e?e:this.o.minWidth&&"auto"!==c&&g<c?c:g:c;this.d.container.css({height:d,width:a});this.d.wrap.css({overflow:f>d||g>a?"auto":"visible"});this.o.autoPosition&&this.setPosition()},setPosition:function(){var a,b;a=h[0]/2-this.d.container.outerHeight(!0)/2;b=h[1]/2-this.d.container.outerWidth(!0)/2;var f="fixed"!==this.d.container.css("position")?k.scrollTop():0;this.o.position&&"[object Array]"===Object.prototype.toString.call(this.o.position)?(a=f+(this.o.position[0]||a),b=this.o.position[1]||
24
+ b):a=f+a;this.d.container.css({left:b,top:a})},watchTab:function(a){if(0<b(a.target).parents(".simplemodal-container").length){if(this.inputs=b(":input:enabled:visible:first, :input:enabled:visible:last",this.d.data[0]),!a.shiftKey&&a.target===this.inputs[this.inputs.length-1]||a.shiftKey&&a.target===this.inputs[0]||0===this.inputs.length)a.preventDefault(),this.focus(a.shiftKey?"last":"first")}else a.preventDefault(),this.focus()},open:function(){this.d.iframe&&this.d.iframe.show();b.isFunction(this.o.onOpen)?
25
+ this.o.onOpen.apply(this,[this.d]):(this.d.overlay.show(),this.d.container.show(),this.d.data.show());this.o.focus&&this.focus();this.bindEvents()},close:function(){if(!this.d.data)return!1;this.unbindEvents();if(b.isFunction(this.o.onClose)&&!this.occb)this.occb=!0,this.o.onClose.apply(this,[this.d]);else{if(this.d.placeholder){var a=b("#simplemodal-placeholder");this.o.persist?a.replaceWith(this.d.data.removeClass("simplemodal-data").css("display",this.display)):(this.d.data.hide().remove(),a.replaceWith(this.d.orig))}else this.d.data.hide().remove();
26
+ this.d.container.hide().remove();this.d.overlay.hide();this.d.iframe&&this.d.iframe.hide().remove();this.d.overlay.remove();this.d={}}}}});
@@ -0,0 +1,46 @@
1
+ var keys = (function() {
2
+ var active = true;
3
+
4
+ var bindings = {};
5
+
6
+ // Disable bindings.
7
+ var disable = function() {
8
+ active = false;
9
+ }
10
+
11
+ // Enable bindings.
12
+ var enable = function() {
13
+ active = true;
14
+ }
15
+
16
+ // Bind a key.
17
+ var bind = function(code, fn) {
18
+ if (bindings[code] === undefined) {
19
+ bindings[code] = [];
20
+ }
21
+ bindings[code].push(fn);
22
+ }
23
+
24
+ // React to key presses.
25
+ $(document).bind('keydown', function(ev) {
26
+ if (active === false) {
27
+ return;
28
+ }
29
+
30
+ console.log(ev.which);
31
+
32
+ var fns = bindings[ev.which];
33
+ if (fns !== undefined) {
34
+ fns.forEach(function(fn) { fn(ev); });
35
+ // ev.preventDefault();
36
+ }
37
+ });
38
+
39
+ return {
40
+ active: function() { return active; },
41
+ bindings: function() { return bindings; },
42
+ bind: bind,
43
+ enable: enable,
44
+ disable: disable
45
+ }
46
+ })();
@@ -0,0 +1,597 @@
1
+ /*!
2
+ * mustache.js - Logic-less {{mustache}} templates with JavaScript
3
+ * http://github.com/janl/mustache.js
4
+ */
5
+ var Mustache = (typeof module !== "undefined" && module.exports) || {};
6
+
7
+ (function (exports) {
8
+
9
+ exports.name = "mustache.js";
10
+ exports.version = "0.5.1-dev";
11
+ exports.tags = ["{{", "}}"];
12
+
13
+ exports.parse = parse;
14
+ exports.clearCache = clearCache;
15
+ exports.compile = compile;
16
+ exports.compilePartial = compilePartial;
17
+ exports.render = render;
18
+
19
+ exports.Scanner = Scanner;
20
+ exports.Context = Context;
21
+ exports.Renderer = Renderer;
22
+
23
+ // This is here for backwards compatibility with 0.4.x.
24
+ exports.to_html = function (template, view, partials, send) {
25
+ var result = render(template, view, partials);
26
+
27
+ if (typeof send === "function") {
28
+ send(result);
29
+ } else {
30
+ return result;
31
+ }
32
+ };
33
+
34
+ var whiteRe = /\s*/;
35
+ var spaceRe = /\s+/;
36
+ var nonSpaceRe = /\S/;
37
+ var eqRe = /\s*=/;
38
+ var curlyRe = /\s*\}/;
39
+ var tagRe = /#|\^|\/|>|\{|&|=|!/;
40
+
41
+ // Workaround for https://issues.apache.org/jira/browse/COUCHDB-577
42
+ // See https://github.com/janl/mustache.js/issues/189
43
+ function testRe(re, string) {
44
+ return RegExp.prototype.test.call(re, string);
45
+ }
46
+
47
+ function isWhitespace(string) {
48
+ return !testRe(nonSpaceRe, string);
49
+ }
50
+
51
+ var isArray = Array.isArray || function (obj) {
52
+ return Object.prototype.toString.call(obj) === "[object Array]";
53
+ };
54
+
55
+ // OSWASP Guidlines: escape all non alphanumeric characters in ASCII space.
56
+ var jsCharsRe = /[\x00-\x2F\x3A-\x40\x5B-\x60\x7B-\xFF\u2028\u2029]/gm;
57
+
58
+ function quote(text) {
59
+ var escaped = text.replace(jsCharsRe, function (c) {
60
+ return "\\u" + ('0000' + c.charCodeAt(0).toString(16)).slice(-4);
61
+ });
62
+
63
+ return '"' + escaped + '"';
64
+ }
65
+
66
+ function escapeRe(string) {
67
+ return string.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
68
+ }
69
+
70
+ var entityMap = {
71
+ "&": "&amp;",
72
+ "<": "&lt;",
73
+ ">": "&gt;",
74
+ '"': '&quot;',
75
+ "'": '&#39;',
76
+ "/": '&#x2F;'
77
+ };
78
+
79
+ function escapeHtml(string) {
80
+ return String(string).replace(/[&<>"'\/]/g, function (s) {
81
+ return entityMap[s];
82
+ });
83
+ }
84
+
85
+ // Export these utility functions.
86
+ exports.isWhitespace = isWhitespace;
87
+ exports.isArray = isArray;
88
+ exports.quote = quote;
89
+ exports.escapeRe = escapeRe;
90
+ exports.escapeHtml = escapeHtml;
91
+
92
+ function Scanner(string) {
93
+ this.string = string;
94
+ this.tail = string;
95
+ this.pos = 0;
96
+ }
97
+
98
+ /**
99
+ * Returns `true` if the tail is empty (end of string).
100
+ */
101
+ Scanner.prototype.eos = function () {
102
+ return this.tail === "";
103
+ };
104
+
105
+ /**
106
+ * Tries to match the given regular expression at the current position.
107
+ * Returns the matched text if it can match, `null` otherwise.
108
+ */
109
+ Scanner.prototype.scan = function (re) {
110
+ var match = this.tail.match(re);
111
+
112
+ if (match && match.index === 0) {
113
+ this.tail = this.tail.substring(match[0].length);
114
+ this.pos += match[0].length;
115
+ return match[0];
116
+ }
117
+
118
+ return null;
119
+ };
120
+
121
+ /**
122
+ * Skips all text until the given regular expression can be matched. Returns
123
+ * the skipped string, which is the entire tail of this scanner if no match
124
+ * can be made.
125
+ */
126
+ Scanner.prototype.scanUntil = function (re) {
127
+ var match, pos = this.tail.search(re);
128
+
129
+ switch (pos) {
130
+ case -1:
131
+ match = this.tail;
132
+ this.pos += this.tail.length;
133
+ this.tail = "";
134
+ break;
135
+ case 0:
136
+ match = null;
137
+ break;
138
+ default:
139
+ match = this.tail.substring(0, pos);
140
+ this.tail = this.tail.substring(pos);
141
+ this.pos += pos;
142
+ }
143
+
144
+ return match;
145
+ };
146
+
147
+ function Context(view, parent) {
148
+ this.view = view;
149
+ this.parent = parent;
150
+ this.clearCache();
151
+ }
152
+
153
+ Context.make = function (view) {
154
+ return (view instanceof Context) ? view : new Context(view);
155
+ };
156
+
157
+ Context.prototype.clearCache = function () {
158
+ this._cache = {};
159
+ };
160
+
161
+ Context.prototype.push = function (view) {
162
+ return new Context(view, this);
163
+ };
164
+
165
+ Context.prototype.lookup = function (name) {
166
+ var value = this._cache[name];
167
+
168
+ if (!value) {
169
+ if (name === ".") {
170
+ value = this.view;
171
+ } else {
172
+ var context = this;
173
+
174
+ while (context) {
175
+ if (name.indexOf(".") > 0) {
176
+ var names = name.split("."), i = 0;
177
+
178
+ value = context.view;
179
+
180
+ while (value && i < names.length) {
181
+ value = value[names[i++]];
182
+ }
183
+ } else {
184
+ value = context.view[name];
185
+ }
186
+
187
+ if (value != null) {
188
+ break;
189
+ }
190
+
191
+ context = context.parent;
192
+ }
193
+ }
194
+
195
+ this._cache[name] = value;
196
+ }
197
+
198
+ if (typeof value === "function") {
199
+ value = value.call(this.view);
200
+ }
201
+
202
+ return value;
203
+ };
204
+
205
+ function Renderer() {
206
+ this.clearCache();
207
+ }
208
+
209
+ Renderer.prototype.clearCache = function () {
210
+ this._cache = {};
211
+ this._partialCache = {};
212
+ };
213
+
214
+ Renderer.prototype.compile = function (tokens, tags) {
215
+ var fn = compileTokens(tokens),
216
+ self = this;
217
+
218
+ return function (view) {
219
+ return fn(Context.make(view), self);
220
+ };
221
+ };
222
+
223
+ Renderer.prototype.compilePartial = function (name, tokens, tags) {
224
+ this._partialCache[name] = this.compile(tokens, tags);
225
+ return this._partialCache[name];
226
+ };
227
+
228
+ Renderer.prototype.render = function (template, view) {
229
+ var fn = this._cache[template];
230
+
231
+ if (!fn) {
232
+ fn = this.compile(template);
233
+ this._cache[template] = fn;
234
+ }
235
+
236
+ return fn(view);
237
+ };
238
+
239
+ Renderer.prototype._section = function (name, context, callback) {
240
+ var value = context.lookup(name);
241
+
242
+ switch (typeof value) {
243
+ case "object":
244
+ if (isArray(value)) {
245
+ var buffer = "";
246
+ for (var i = 0, len = value.length; i < len; ++i) {
247
+ buffer += callback(context.push(value[i]), this);
248
+ }
249
+ return buffer;
250
+ } else {
251
+ return callback(context.push(value), this);
252
+ }
253
+ break;
254
+ case "function":
255
+ var sectionText = callback(context, this), self = this;
256
+ var scopedRender = function (template) {
257
+ return self.render(template, context);
258
+ };
259
+ return value.call(context.view, sectionText, scopedRender) || "";
260
+ break;
261
+ default:
262
+ if (value) {
263
+ return callback(context, this);
264
+ }
265
+ }
266
+
267
+ return "";
268
+ };
269
+
270
+ Renderer.prototype._inverted = function (name, context, callback) {
271
+ var value = context.lookup(name);
272
+
273
+ // From the spec: inverted sections may render text once based on the
274
+ // inverse value of the key. That is, they will be rendered if the key
275
+ // doesn't exist, is false, or is an empty list.
276
+ if (value == null || value === false || (isArray(value) && value.length === 0)) {
277
+ return callback(context, this);
278
+ }
279
+
280
+ return "";
281
+ };
282
+
283
+ Renderer.prototype._partial = function (name, context) {
284
+ var fn = this._partialCache[name];
285
+
286
+ if (fn) {
287
+ return fn(context, this);
288
+ }
289
+
290
+ return "";
291
+ };
292
+
293
+ Renderer.prototype._name = function (name, context, escape) {
294
+ var value = context.lookup(name);
295
+
296
+ if (typeof value === "function") {
297
+ value = value.call(context.view);
298
+ }
299
+
300
+ var string = (value == null) ? "" : String(value);
301
+
302
+ if (escape) {
303
+ return escapeHtml(string);
304
+ }
305
+
306
+ return string;
307
+ };
308
+
309
+ /**
310
+ * Low-level function that compiles the given `tokens` into a
311
+ * function that accepts two arguments: a Context and a
312
+ * Renderer. Returns the body of the function as a string if
313
+ * `returnBody` is true.
314
+ */
315
+ function compileTokens(tokens, returnBody) {
316
+ if (typeof tokens === "string") {
317
+ tokens = parse(tokens);
318
+ }
319
+
320
+ var body = ['""'];
321
+ var token, method, escape;
322
+
323
+ for (var i = 0, len = tokens.length; i < len; ++i) {
324
+ token = tokens[i];
325
+
326
+ switch (token.type) {
327
+ case "#":
328
+ case "^":
329
+ method = (token.type === "#") ? "_section" : "_inverted";
330
+ body.push("r." + method + "(" + quote(token.value) + ", c, function (c, r) {\n" +
331
+ " " + compileTokens(token.tokens, true) + "\n" +
332
+ "})");
333
+ break;
334
+ case "{":
335
+ case "&":
336
+ case "name":
337
+ escape = token.type === "name" ? "true" : "false";
338
+ body.push("r._name(" + quote(token.value) + ", c, " + escape + ")");
339
+ break;
340
+ case ">":
341
+ body.push("r._partial(" + quote(token.value) + ", c)");
342
+ break;
343
+ case "text":
344
+ body.push(quote(token.value));
345
+ break;
346
+ }
347
+ }
348
+
349
+ // Convert to a string body.
350
+ body = "return " + body.join(" + ") + ";";
351
+
352
+ // Good for debugging.
353
+ // console.log(body);
354
+
355
+ if (returnBody) {
356
+ return body;
357
+ }
358
+
359
+ // For great evil!
360
+ return new Function("c, r", body);
361
+ }
362
+
363
+ function escapeTags(tags) {
364
+ if (tags.length === 2) {
365
+ return [
366
+ new RegExp(escapeRe(tags[0]) + "\\s*"),
367
+ new RegExp("\\s*" + escapeRe(tags[1]))
368
+ ];
369
+ }
370
+
371
+ throw new Error("Invalid tags: " + tags.join(" "));
372
+ }
373
+
374
+ /**
375
+ * Forms the given linear array of `tokens` into a nested tree structure
376
+ * where tokens that represent a section have a "tokens" array property
377
+ * that contains all tokens that are in that section.
378
+ */
379
+ function nestTokens(tokens) {
380
+ var tree = [];
381
+ var collector = tree;
382
+ var sections = [];
383
+ var token, section;
384
+
385
+ for (var i = 0; i < tokens.length; ++i) {
386
+ token = tokens[i];
387
+
388
+ switch (token.type) {
389
+ case "#":
390
+ case "^":
391
+ token.tokens = [];
392
+ sections.push(token);
393
+ collector.push(token);
394
+ collector = token.tokens;
395
+ break;
396
+ case "/":
397
+ if (sections.length === 0) {
398
+ throw new Error("Unopened section: " + token.value);
399
+ }
400
+
401
+ section = sections.pop();
402
+
403
+ if (section.value !== token.value) {
404
+ throw new Error("Unclosed section: " + section.value);
405
+ }
406
+
407
+ if (sections.length > 0) {
408
+ collector = sections[sections.length - 1].tokens;
409
+ } else {
410
+ collector = tree;
411
+ }
412
+ break;
413
+ default:
414
+ collector.push(token);
415
+ }
416
+ }
417
+
418
+ // Make sure there were no open sections when we're done.
419
+ section = sections.pop();
420
+
421
+ if (section) {
422
+ throw new Error("Unclosed section: " + section.value);
423
+ }
424
+
425
+ return tree;
426
+ }
427
+
428
+ /**
429
+ * Combines the values of consecutive text tokens in the given `tokens` array
430
+ * to a single token.
431
+ */
432
+ function squashTokens(tokens) {
433
+ var lastToken;
434
+
435
+ for (var i = 0; i < tokens.length; ++i) {
436
+ token = tokens[i];
437
+
438
+ if (lastToken && lastToken.type === "text" && token.type === "text") {
439
+ lastToken.value += token.value;
440
+ tokens.splice(i--, 1); // Remove this token from the array.
441
+ } else {
442
+ lastToken = token;
443
+ }
444
+ }
445
+ }
446
+
447
+ /**
448
+ * Breaks up the given `template` string into a tree of token objects. If
449
+ * `tags` is given here it must be an array with two string values: the
450
+ * opening and closing tags used in the template (e.g. ["<%", "%>"]). Of
451
+ * course, the default is to use mustaches (i.e. Mustache.tags).
452
+ */
453
+ function parse(template, tags) {
454
+ tags = tags || exports.tags;
455
+ tagRes = escapeTags(tags);
456
+
457
+ var scanner = new Scanner(template);
458
+
459
+ var tokens = [], // Buffer to hold the tokens
460
+ spaces = [], // Indices of whitespace tokens on the current line
461
+ hasTag = false, // Is there a {{tag}} on the current line?
462
+ nonSpace = false; // Is there a non-space char on the current line?
463
+
464
+ // Strips all whitespace tokens array for the current line
465
+ // if there was a {{#tag}} on it and otherwise only space.
466
+ var stripSpace = function () {
467
+ if (hasTag && !nonSpace) {
468
+ while (spaces.length) {
469
+ tokens.splice(spaces.pop(), 1);
470
+ }
471
+ } else {
472
+ spaces = [];
473
+ }
474
+
475
+ hasTag = false;
476
+ nonSpace = false;
477
+ };
478
+
479
+ var type, value, chr;
480
+
481
+ while (!scanner.eos()) {
482
+ value = scanner.scanUntil(tagRes[0]);
483
+
484
+ if (value) {
485
+ for (var i = 0, len = value.length; i < len; ++i) {
486
+ chr = value[i];
487
+
488
+ if (isWhitespace(chr)) {
489
+ spaces.push(tokens.length);
490
+ } else {
491
+ nonSpace = true;
492
+ }
493
+
494
+ tokens.push({type: "text", value: chr});
495
+
496
+ if (chr === "\n") {
497
+ stripSpace(); // Check for whitespace on the current line.
498
+ }
499
+ }
500
+ }
501
+
502
+ // Match the opening tag.
503
+ if (!scanner.scan(tagRes[0])) {
504
+ break;
505
+ }
506
+
507
+ hasTag = true;
508
+ type = scanner.scan(tagRe) || "name";
509
+
510
+ // Skip any whitespace between tag and value.
511
+ scanner.scan(whiteRe);
512
+
513
+ // Extract the tag value.
514
+ if (type === "=") {
515
+ value = scanner.scanUntil(eqRe);
516
+ scanner.scan(eqRe);
517
+ scanner.scanUntil(tagRes[1]);
518
+ } else if (type === "{") {
519
+ var closeRe = new RegExp("\\s*" + escapeRe("}" + tags[1]));
520
+ value = scanner.scanUntil(closeRe);
521
+ scanner.scan(curlyRe);
522
+ scanner.scanUntil(tagRes[1]);
523
+ } else {
524
+ value = scanner.scanUntil(tagRes[1]);
525
+ }
526
+
527
+ // Match the closing tag.
528
+ if (!scanner.scan(tagRes[1])) {
529
+ throw new Error("Unclosed tag at " + scanner.pos);
530
+ }
531
+
532
+ tokens.push({type: type, value: value});
533
+
534
+ if (type === "name" || type === "{" || type === "&") {
535
+ nonSpace = true;
536
+ }
537
+
538
+ // Set the tags for the next time around.
539
+ if (type === "=") {
540
+ tags = value.split(spaceRe);
541
+ tagRes = escapeTags(tags);
542
+ }
543
+ }
544
+
545
+ squashTokens(tokens);
546
+
547
+ return nestTokens(tokens);
548
+ }
549
+
550
+ // The high-level clearCache, compile, compilePartial, and render functions
551
+ // use this default renderer.
552
+ var _renderer = new Renderer;
553
+
554
+ /**
555
+ * Clears all cached templates and partials.
556
+ */
557
+ function clearCache() {
558
+ _renderer.clearCache();
559
+ }
560
+
561
+ /**
562
+ * High-level API for compiling the given `tokens` down to a reusable
563
+ * function. If `tokens` is a string it will be parsed using the given `tags`
564
+ * before it is compiled.
565
+ */
566
+ function compile(tokens, tags) {
567
+ return _renderer.compile(tokens, tags);
568
+ }
569
+
570
+ /**
571
+ * High-level API for compiling the `tokens` for the partial with the given
572
+ * `name` down to a reusable function. If `tokens` is a string it will be
573
+ * parsed using the given `tags` before it is compiled.
574
+ */
575
+ function compilePartial(name, tokens, tags) {
576
+ return _renderer.compilePartial(name, tokens, tags);
577
+ }
578
+
579
+ /**
580
+ * High-level API for rendering the `template` using the given `view`. The
581
+ * optional `partials` object may be given here for convenience, but note that
582
+ * it will cause all partials to be re-compiled, thus hurting performance. Of
583
+ * course, this only matters if you're going to render the same template more
584
+ * than once. If so, it is best to call `compilePartial` before calling this
585
+ * function and to leave the `partials` argument blank.
586
+ */
587
+ function render(template, view, partials) {
588
+ if (partials) {
589
+ for (var name in partials) {
590
+ compilePartial(name, partials[name]);
591
+ }
592
+ }
593
+
594
+ return _renderer.render(template, view);
595
+ }
596
+
597
+ })(Mustache);