~ubuntu-branches/ubuntu/raring/horizon/raring

« back to all changes in this revision

Viewing changes to horizon/static/horizon/lib/hogan-2.0.0.js

  • Committer: Package Import Robot
  • Author(s): James Page, James Page, Adam Gandelman
  • Date: 2013-03-20 11:20:17 UTC
  • mfrom: (1.1.25)
  • Revision ID: package-import@ubuntu.com-20130320112017-15h2zdnqy20lc089
Tags: 1:2013.1~rc1-0ubuntu1
[ James Page ]
* New upstream release candidate for Grizzly:
  - Recompress static JS and CSS and generate new manifest.json for
    offline compression.
* d/watch: Update uversionmangle to deal with upstream versioning
  changes, remove tarballs.openstack.org.
* d/control: Version python-compressor >= 1.2 (LP: #1130610).

[ Adam Gandelman ]
* debian/patches/ubuntu_local_settings.py: Refresh, specify memcache
  location as part of CACHES, as per upstream changes to example config.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *  Copyright 2011 Twitter, Inc.
 
3
 *  Licensed under the Apache License, Version 2.0 (the "License");
 
4
 *  you may not use this file except in compliance with the License.
 
5
 *  You may obtain a copy of the License at
 
6
 *
 
7
 *  http://www.apache.org/licenses/LICENSE-2.0
 
8
 *
 
9
 *  Unless required by applicable law or agreed to in writing, software
 
10
 *  distributed under the License is distributed on an "AS IS" BASIS,
 
11
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
12
 *  See the License for the specific language governing permissions and
 
13
 *  limitations under the License.
 
14
 */
 
15
 
 
16
 
 
17
 
 
18
var Hogan = {};
 
19
 
 
20
(function (Hogan, useArrayBuffer) {
 
21
  Hogan.Template = function (renderFunc, text, compiler, options) {
 
22
    this.r = renderFunc || this.r;
 
23
    this.c = compiler;
 
24
    this.options = options;
 
25
    this.text = text || '';
 
26
    this.buf = (useArrayBuffer) ? [] : '';
 
27
  }
 
28
 
 
29
  Hogan.Template.prototype = {
 
30
    // render: replaced by generated code.
 
31
    r: function (context, partials, indent) { return ''; },
 
32
 
 
33
    // variable escaping
 
34
    v: hoganEscape,
 
35
 
 
36
    // triple stache
 
37
    t: coerceToString,
 
38
 
 
39
    render: function render(context, partials, indent) {
 
40
      return this.ri([context], partials || {}, indent);
 
41
    },
 
42
 
 
43
    // render internal -- a hook for overrides that catches partials too
 
44
    ri: function (context, partials, indent) {
 
45
      return this.r(context, partials, indent);
 
46
    },
 
47
 
 
48
    // tries to find a partial in the curent scope and render it
 
49
    rp: function(name, context, partials, indent) {
 
50
      var partial = partials[name];
 
51
 
 
52
      if (!partial) {
 
53
        return '';
 
54
      }
 
55
 
 
56
      if (this.c && typeof partial == 'string') {
 
57
        partial = this.c.compile(partial, this.options);
 
58
      }
 
59
 
 
60
      return partial.ri(context, partials, indent);
 
61
    },
 
62
 
 
63
    // render a section
 
64
    rs: function(context, partials, section) {
 
65
      var tail = context[context.length - 1];
 
66
 
 
67
      if (!isArray(tail)) {
 
68
        section(context, partials, this);
 
69
        return;
 
70
      }
 
71
 
 
72
      for (var i = 0; i < tail.length; i++) {
 
73
        context.push(tail[i]);
 
74
        section(context, partials, this);
 
75
        context.pop();
 
76
      }
 
77
    },
 
78
 
 
79
    // maybe start a section
 
80
    s: function(val, ctx, partials, inverted, start, end, tags) {
 
81
      var pass;
 
82
 
 
83
      if (isArray(val) && val.length === 0) {
 
84
        return false;
 
85
      }
 
86
 
 
87
      if (typeof val == 'function') {
 
88
        val = this.ls(val, ctx, partials, inverted, start, end, tags);
 
89
      }
 
90
 
 
91
      pass = (val === '') || !!val;
 
92
 
 
93
      if (!inverted && pass && ctx) {
 
94
        ctx.push((typeof val == 'object') ? val : ctx[ctx.length - 1]);
 
95
      }
 
96
 
 
97
      return pass;
 
98
    },
 
99
 
 
100
    // find values with dotted names
 
101
    d: function(key, ctx, partials, returnFound) {
 
102
      var names = key.split('.'),
 
103
          val = this.f(names[0], ctx, partials, returnFound),
 
104
          cx = null;
 
105
 
 
106
      if (key === '.' && isArray(ctx[ctx.length - 2])) {
 
107
        return ctx[ctx.length - 1];
 
108
      }
 
109
 
 
110
      for (var i = 1; i < names.length; i++) {
 
111
        if (val && typeof val == 'object' && names[i] in val) {
 
112
          cx = val;
 
113
          val = val[names[i]];
 
114
        } else {
 
115
          val = '';
 
116
        }
 
117
      }
 
118
 
 
119
      if (returnFound && !val) {
 
120
        return false;
 
121
      }
 
122
 
 
123
      if (!returnFound && typeof val == 'function') {
 
124
        ctx.push(cx);
 
125
        val = this.lv(val, ctx, partials);
 
126
        ctx.pop();
 
127
      }
 
128
 
 
129
      return val;
 
130
    },
 
131
 
 
132
    // find values with normal names
 
133
    f: function(key, ctx, partials, returnFound) {
 
134
      var val = false,
 
135
          v = null,
 
136
          found = false;
 
137
 
 
138
      for (var i = ctx.length - 1; i >= 0; i--) {
 
139
        v = ctx[i];
 
140
        if (v && typeof v == 'object' && key in v) {
 
141
          val = v[key];
 
142
          found = true;
 
143
          break;
 
144
        }
 
145
      }
 
146
 
 
147
      if (!found) {
 
148
        return (returnFound) ? false : "";
 
149
      }
 
150
 
 
151
      if (!returnFound && typeof val == 'function') {
 
152
        val = this.lv(val, ctx, partials);
 
153
      }
 
154
 
 
155
      return val;
 
156
    },
 
157
 
 
158
    // higher order templates
 
159
    ho: function(val, cx, partials, text, tags) {
 
160
      var compiler = this.c;
 
161
      var options = this.options;
 
162
      options.delimiters = tags;
 
163
      var text = val.call(cx, text);
 
164
      text = (text == null) ? String(text) : text.toString();
 
165
      this.b(compiler.compile(text, options).render(cx, partials));
 
166
      return false;
 
167
    },
 
168
 
 
169
    // template result buffering
 
170
    b: (useArrayBuffer) ? function(s) { this.buf.push(s); } :
 
171
                          function(s) { this.buf += s; },
 
172
    fl: (useArrayBuffer) ? function() { var r = this.buf.join(''); this.buf = []; return r; } :
 
173
                           function() { var r = this.buf; this.buf = ''; return r; },
 
174
 
 
175
    // lambda replace section
 
176
    ls: function(val, ctx, partials, inverted, start, end, tags) {
 
177
      var cx = ctx[ctx.length - 1],
 
178
          t = null;
 
179
 
 
180
      if (!inverted && this.c && val.length > 0) {
 
181
        return this.ho(val, cx, partials, this.text.substring(start, end), tags);
 
182
      }
 
183
 
 
184
      t = val.call(cx);
 
185
 
 
186
      if (typeof t == 'function') {
 
187
        if (inverted) {
 
188
          return true;
 
189
        } else if (this.c) {
 
190
          return this.ho(t, cx, partials, this.text.substring(start, end), tags);
 
191
        }
 
192
      }
 
193
 
 
194
      return t;
 
195
    },
 
196
 
 
197
    // lambda replace variable
 
198
    lv: function(val, ctx, partials) {
 
199
      var cx = ctx[ctx.length - 1];
 
200
      var result = val.call(cx);
 
201
 
 
202
      if (typeof result == 'function') {
 
203
        result = coerceToString(result.call(cx));
 
204
        if (this.c && ~result.indexOf("{\u007B")) {
 
205
          return this.c.compile(result, this.options).render(cx, partials);
 
206
        }
 
207
      }
 
208
 
 
209
      return coerceToString(result);
 
210
    }
 
211
 
 
212
  };
 
213
 
 
214
  var rAmp = /&/g,
 
215
      rLt = /</g,
 
216
      rGt = />/g,
 
217
      rApos =/\'/g,
 
218
      rQuot = /\"/g,
 
219
      hChars =/[&<>\"\']/;
 
220
 
 
221
 
 
222
  function coerceToString(val) {
 
223
    return String((val === null || val === undefined) ? '' : val);
 
224
  }
 
225
 
 
226
  function hoganEscape(str) {
 
227
    str = coerceToString(str);
 
228
    return hChars.test(str) ?
 
229
      str
 
230
        .replace(rAmp,'&amp;')
 
231
        .replace(rLt,'&lt;')
 
232
        .replace(rGt,'&gt;')
 
233
        .replace(rApos,'&#39;')
 
234
        .replace(rQuot, '&quot;') :
 
235
      str;
 
236
  }
 
237
 
 
238
  var isArray = Array.isArray || function(a) {
 
239
    return Object.prototype.toString.call(a) === '[object Array]';
 
240
  };
 
241
 
 
242
})(typeof exports !== 'undefined' ? exports : Hogan);
 
243
 
 
244
 
 
245
 
 
246
 
 
247
(function (Hogan) {
 
248
  // Setup regex  assignments
 
249
  // remove whitespace according to Mustache spec
 
250
  var rIsWhitespace = /\S/,
 
251
      rQuot = /\"/g,
 
252
      rNewline =  /\n/g,
 
253
      rCr = /\r/g,
 
254
      rSlash = /\\/g,
 
255
      tagTypes = {
 
256
        '#': 1, '^': 2, '/': 3,  '!': 4, '>': 5,
 
257
        '<': 6, '=': 7, '_v': 8, '{': 9, '&': 10
 
258
      };
 
259
 
 
260
  Hogan.scan = function scan(text, delimiters) {
 
261
    var len = text.length,
 
262
        IN_TEXT = 0,
 
263
        IN_TAG_TYPE = 1,
 
264
        IN_TAG = 2,
 
265
        state = IN_TEXT,
 
266
        tagType = null,
 
267
        tag = null,
 
268
        buf = '',
 
269
        tokens = [],
 
270
        seenTag = false,
 
271
        i = 0,
 
272
        lineStart = 0,
 
273
        otag = '{{',
 
274
        ctag = '}}';
 
275
 
 
276
    function addBuf() {
 
277
      if (buf.length > 0) {
 
278
        tokens.push(new String(buf));
 
279
        buf = '';
 
280
      }
 
281
    }
 
282
 
 
283
    function lineIsWhitespace() {
 
284
      var isAllWhitespace = true;
 
285
      for (var j = lineStart; j < tokens.length; j++) {
 
286
        isAllWhitespace =
 
287
          (tokens[j].tag && tagTypes[tokens[j].tag] < tagTypes['_v']) ||
 
288
          (!tokens[j].tag && tokens[j].match(rIsWhitespace) === null);
 
289
        if (!isAllWhitespace) {
 
290
          return false;
 
291
        }
 
292
      }
 
293
 
 
294
      return isAllWhitespace;
 
295
    }
 
296
 
 
297
    function filterLine(haveSeenTag, noNewLine) {
 
298
      addBuf();
 
299
 
 
300
      if (haveSeenTag && lineIsWhitespace()) {
 
301
        for (var j = lineStart, next; j < tokens.length; j++) {
 
302
          if (!tokens[j].tag) {
 
303
            if ((next = tokens[j+1]) && next.tag == '>') {
 
304
              // set indent to token value
 
305
              next.indent = tokens[j].toString()
 
306
            }
 
307
            tokens.splice(j, 1);
 
308
          }
 
309
        }
 
310
      } else if (!noNewLine) {
 
311
        tokens.push({tag:'\n'});
 
312
      }
 
313
 
 
314
      seenTag = false;
 
315
      lineStart = tokens.length;
 
316
    }
 
317
 
 
318
    function changeDelimiters(text, index) {
 
319
      var close = '=' + ctag,
 
320
          closeIndex = text.indexOf(close, index),
 
321
          delimiters = trim(
 
322
            text.substring(text.indexOf('=', index) + 1, closeIndex)
 
323
          ).split(' ');
 
324
 
 
325
      otag = delimiters[0];
 
326
      ctag = delimiters[1];
 
327
 
 
328
      return closeIndex + close.length - 1;
 
329
    }
 
330
 
 
331
    if (delimiters) {
 
332
      delimiters = delimiters.split(' ');
 
333
      otag = delimiters[0];
 
334
      ctag = delimiters[1];
 
335
    }
 
336
 
 
337
    for (i = 0; i < len; i++) {
 
338
      if (state == IN_TEXT) {
 
339
        if (tagChange(otag, text, i)) {
 
340
          --i;
 
341
          addBuf();
 
342
          state = IN_TAG_TYPE;
 
343
        } else {
 
344
          if (text.charAt(i) == '\n') {
 
345
            filterLine(seenTag);
 
346
          } else {
 
347
            buf += text.charAt(i);
 
348
          }
 
349
        }
 
350
      } else if (state == IN_TAG_TYPE) {
 
351
        i += otag.length - 1;
 
352
        tag = tagTypes[text.charAt(i + 1)];
 
353
        tagType = tag ? text.charAt(i + 1) : '_v';
 
354
        if (tagType == '=') {
 
355
          i = changeDelimiters(text, i);
 
356
          state = IN_TEXT;
 
357
        } else {
 
358
          if (tag) {
 
359
            i++;
 
360
          }
 
361
          state = IN_TAG;
 
362
        }
 
363
        seenTag = i;
 
364
      } else {
 
365
        if (tagChange(ctag, text, i)) {
 
366
          tokens.push({tag: tagType, n: trim(buf), otag: otag, ctag: ctag,
 
367
                       i: (tagType == '/') ? seenTag - ctag.length : i + otag.length});
 
368
          buf = '';
 
369
          i += ctag.length - 1;
 
370
          state = IN_TEXT;
 
371
          if (tagType == '{') {
 
372
            if (ctag == '}}') {
 
373
              i++;
 
374
            } else {
 
375
              cleanTripleStache(tokens[tokens.length - 1]);
 
376
            }
 
377
          }
 
378
        } else {
 
379
          buf += text.charAt(i);
 
380
        }
 
381
      }
 
382
    }
 
383
 
 
384
    filterLine(seenTag, true);
 
385
 
 
386
    return tokens;
 
387
  }
 
388
 
 
389
  function cleanTripleStache(token) {
 
390
    if (token.n.substr(token.n.length - 1) === '}') {
 
391
      token.n = token.n.substring(0, token.n.length - 1);
 
392
    }
 
393
  }
 
394
 
 
395
  function trim(s) {
 
396
    if (s.trim) {
 
397
      return s.trim();
 
398
    }
 
399
 
 
400
    return s.replace(/^\s*|\s*$/g, '');
 
401
  }
 
402
 
 
403
  function tagChange(tag, text, index) {
 
404
    if (text.charAt(index) != tag.charAt(0)) {
 
405
      return false;
 
406
    }
 
407
 
 
408
    for (var i = 1, l = tag.length; i < l; i++) {
 
409
      if (text.charAt(index + i) != tag.charAt(i)) {
 
410
        return false;
 
411
      }
 
412
    }
 
413
 
 
414
    return true;
 
415
  }
 
416
 
 
417
  function buildTree(tokens, kind, stack, customTags) {
 
418
    var instructions = [],
 
419
        opener = null,
 
420
        token = null;
 
421
 
 
422
    while (tokens.length > 0) {
 
423
      token = tokens.shift();
 
424
      if (token.tag == '#' || token.tag == '^' || isOpener(token, customTags)) {
 
425
        stack.push(token);
 
426
        token.nodes = buildTree(tokens, token.tag, stack, customTags);
 
427
        instructions.push(token);
 
428
      } else if (token.tag == '/') {
 
429
        if (stack.length === 0) {
 
430
          throw new Error('Closing tag without opener: /' + token.n);
 
431
        }
 
432
        opener = stack.pop();
 
433
        if (token.n != opener.n && !isCloser(token.n, opener.n, customTags)) {
 
434
          throw new Error('Nesting error: ' + opener.n + ' vs. ' + token.n);
 
435
        }
 
436
        opener.end = token.i;
 
437
        return instructions;
 
438
      } else {
 
439
        instructions.push(token);
 
440
      }
 
441
    }
 
442
 
 
443
    if (stack.length > 0) {
 
444
      throw new Error('missing closing tag: ' + stack.pop().n);
 
445
    }
 
446
 
 
447
    return instructions;
 
448
  }
 
449
 
 
450
  function isOpener(token, tags) {
 
451
    for (var i = 0, l = tags.length; i < l; i++) {
 
452
      if (tags[i].o == token.n) {
 
453
        token.tag = '#';
 
454
        return true;
 
455
      }
 
456
    }
 
457
  }
 
458
 
 
459
  function isCloser(close, open, tags) {
 
460
    for (var i = 0, l = tags.length; i < l; i++) {
 
461
      if (tags[i].c == close && tags[i].o == open) {
 
462
        return true;
 
463
      }
 
464
    }
 
465
  }
 
466
 
 
467
  Hogan.generate = function (tree, text, options) {
 
468
    var code = 'var _=this;_.b(i=i||"");' + walk(tree) + 'return _.fl();';
 
469
    if (options.asString) {
 
470
      return 'function(c,p,i){' + code + ';}';
 
471
    }
 
472
 
 
473
    return new Hogan.Template(new Function('c', 'p', 'i', code), text, Hogan, options);
 
474
  }
 
475
 
 
476
  function esc(s) {
 
477
    return s.replace(rSlash, '\\\\')
 
478
            .replace(rQuot, '\\\"')
 
479
            .replace(rNewline, '\\n')
 
480
            .replace(rCr, '\\r');
 
481
  }
 
482
 
 
483
  function chooseMethod(s) {
 
484
    return (~s.indexOf('.')) ? 'd' : 'f';
 
485
  }
 
486
 
 
487
  function walk(tree) {
 
488
    var code = '';
 
489
    for (var i = 0, l = tree.length; i < l; i++) {
 
490
      var tag = tree[i].tag;
 
491
      if (tag == '#') {
 
492
        code += section(tree[i].nodes, tree[i].n, chooseMethod(tree[i].n),
 
493
                        tree[i].i, tree[i].end, tree[i].otag + " " + tree[i].ctag);
 
494
      } else if (tag == '^') {
 
495
        code += invertedSection(tree[i].nodes, tree[i].n,
 
496
                                chooseMethod(tree[i].n));
 
497
      } else if (tag == '<' || tag == '>') {
 
498
        code += partial(tree[i]);
 
499
      } else if (tag == '{' || tag == '&') {
 
500
        code += tripleStache(tree[i].n, chooseMethod(tree[i].n));
 
501
      } else if (tag == '\n') {
 
502
        code += text('"\\n"' + (tree.length-1 == i ? '' : ' + i'));
 
503
      } else if (tag == '_v') {
 
504
        code += variable(tree[i].n, chooseMethod(tree[i].n));
 
505
      } else if (tag === undefined) {
 
506
        code += text('"' + esc(tree[i]) + '"');
 
507
      }
 
508
    }
 
509
    return code;
 
510
  }
 
511
 
 
512
  function section(nodes, id, method, start, end, tags) {
 
513
    return 'if(_.s(_.' + method + '("' + esc(id) + '",c,p,1),' +
 
514
           'c,p,0,' + start + ',' + end + ',"' + tags + '")){' +
 
515
           '_.rs(c,p,' +
 
516
           'function(c,p,_){' +
 
517
           walk(nodes) +
 
518
           '});c.pop();}';
 
519
  }
 
520
 
 
521
  function invertedSection(nodes, id, method) {
 
522
    return 'if(!_.s(_.' + method + '("' + esc(id) + '",c,p,1),c,p,1,0,0,"")){' +
 
523
           walk(nodes) +
 
524
           '};';
 
525
  }
 
526
 
 
527
  function partial(tok) {
 
528
    return '_.b(_.rp("' +  esc(tok.n) + '",c,p,"' + (tok.indent || '') + '"));';
 
529
  }
 
530
 
 
531
  function tripleStache(id, method) {
 
532
    return '_.b(_.t(_.' + method + '("' + esc(id) + '",c,p,0)));';
 
533
  }
 
534
 
 
535
  function variable(id, method) {
 
536
    return '_.b(_.v(_.' + method + '("' + esc(id) + '",c,p,0)));';
 
537
  }
 
538
 
 
539
  function text(id) {
 
540
    return '_.b(' + id + ');';
 
541
  }
 
542
 
 
543
  Hogan.parse = function(tokens, text, options) {
 
544
    options = options || {};
 
545
    return buildTree(tokens, '', [], options.sectionTags || []);
 
546
  },
 
547
 
 
548
  Hogan.cache = {};
 
549
 
 
550
  Hogan.compile = function(text, options) {
 
551
    // options
 
552
    //
 
553
    // asString: false (default)
 
554
    //
 
555
    // sectionTags: [{o: '_foo', c: 'foo'}]
 
556
    // An array of object with o and c fields that indicate names for custom
 
557
    // section tags. The example above allows parsing of {{_foo}}{{/foo}}.
 
558
    //
 
559
    // delimiters: A string that overrides the default delimiters.
 
560
    // Example: "<% %>"
 
561
    //
 
562
    options = options || {};
 
563
 
 
564
    var key = text + '||' + !!options.asString;
 
565
 
 
566
    var t = this.cache[key];
 
567
 
 
568
    if (t) {
 
569
      return t;
 
570
    }
 
571
 
 
572
    t = this.generate(this.parse(this.scan(text, options.delimiters), text, options), text, options);
 
573
    return this.cache[key] = t;
 
574
  };
 
575
})(typeof exports !== 'undefined' ? exports : Hogan);
 
576