Proyectos de Subversion LeadersLinked - Antes de SPA

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
6056 efrain 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
var Hogan = {};
17
 
18
(function (Hogan) {
19
  Hogan.Template = function (codeObj, text, compiler, options) {
20
    codeObj = codeObj || {};
21
    this.r = codeObj.code || this.r;
22
    this.c = compiler;
23
    this.options = options || {};
24
    this.text = text || '';
25
    this.partials = codeObj.partials || {};
26
    this.subs = codeObj.subs || {};
27
    this.buf = '';
28
  }
29
 
30
  Hogan.Template.prototype = {
31
    // render: replaced by generated code.
32
    r: function (context, partials, indent) { return ''; },
33
 
34
    // variable escaping
35
    v: hoganEscape,
36
 
37
    // triple stache
38
    t: coerceToString,
39
 
40
    render: function render(context, partials, indent) {
41
      return this.ri([context], partials || {}, indent);
42
    },
43
 
44
    // render internal -- a hook for overrides that catches partials too
45
    ri: function (context, partials, indent) {
46
      return this.r(context, partials, indent);
47
    },
48
 
49
    // ensurePartial
50
    ep: function(symbol, partials) {
51
      var partial = this.partials[symbol];
52
 
53
      // check to see that if we've instantiated this partial before
54
      var template = partials[partial.name];
55
      if (partial.instance && partial.base == template) {
56
        return partial.instance;
57
      }
58
 
59
      if (typeof template == 'string') {
60
        if (!this.c) {
61
          throw new Error("No compiler available.");
62
        }
63
        template = this.c.compile(template, this.options);
64
      }
65
 
66
      if (!template) {
67
        return null;
68
      }
69
 
70
      // We use this to check whether the partials dictionary has changed
71
      this.partials[symbol].base = template;
72
 
73
      if (partial.subs) {
74
        // Make sure we consider parent template now
75
        if (!partials.stackText) partials.stackText = {};
76
        for (key in partial.subs) {
77
          if (!partials.stackText[key]) {
78
            partials.stackText[key] = (this.activeSub !== undefined && partials.stackText[this.activeSub]) ? partials.stackText[this.activeSub] : this.text;
79
          }
80
        }
81
        template = createSpecializedPartial(template, partial.subs, partial.partials,
82
          this.stackSubs, this.stackPartials, partials.stackText);
83
      }
84
      this.partials[symbol].instance = template;
85
 
86
      return template;
87
    },
88
 
89
    // tries to find a partial in the current scope and render it
90
    rp: function(symbol, context, partials, indent) {
91
      var partial = this.ep(symbol, partials);
92
      if (!partial) {
93
        return '';
94
      }
95
 
96
      return partial.ri(context, partials, indent);
97
    },
98
 
99
    // render a section
100
    rs: function(context, partials, section) {
101
      var tail = context[context.length - 1];
102
 
103
      if (!isArray(tail)) {
104
        section(context, partials, this);
105
        return;
106
      }
107
 
108
      for (var i = 0; i < tail.length; i++) {
109
        context.push(tail[i]);
110
        section(context, partials, this);
111
        context.pop();
112
      }
113
    },
114
 
115
    // maybe start a section
116
    s: function(val, ctx, partials, inverted, start, end, tags) {
117
      var pass;
118
 
119
      if (isArray(val) && val.length === 0) {
120
        return false;
121
      }
122
 
123
      if (typeof val == 'function') {
124
        val = this.ms(val, ctx, partials, inverted, start, end, tags);
125
      }
126
 
127
      pass = !!val;
128
 
129
      if (!inverted && pass && ctx) {
130
        ctx.push((typeof val == 'object') ? val : ctx[ctx.length - 1]);
131
      }
132
 
133
      return pass;
134
    },
135
 
136
    // find values with dotted names
137
    d: function(key, ctx, partials, returnFound) {
138
      var found,
139
          names = key.split('.'),
140
          val = this.f(names[0], ctx, partials, returnFound),
141
          doModelGet = this.options.modelGet,
142
          cx = null;
143
 
144
      if (key === '.' && isArray(ctx[ctx.length - 2])) {
145
        val = ctx[ctx.length - 1];
146
      } else {
147
        for (var i = 1; i < names.length; i++) {
148
          found = findInScope(names[i], val, doModelGet);
149
          if (found != null) {
150
            cx = val;
151
            val = found;
152
          } else {
153
            val = '';
154
          }
155
        }
156
      }
157
 
158
      if (returnFound && !val) {
159
        return false;
160
      }
161
 
162
      if (!returnFound && typeof val == 'function') {
163
        ctx.push(cx);
164
        val = this.mv(val, ctx, partials);
165
        ctx.pop();
166
      }
167
 
168
      return val;
169
    },
170
 
171
    // find values with normal names
172
    f: function(key, ctx, partials, returnFound) {
173
      var val = false,
174
          v = null,
175
          found = false,
176
          doModelGet = this.options.modelGet;
177
 
178
      for (var i = ctx.length - 1; i >= 0; i--) {
179
        v = ctx[i];
180
        val = findInScope(key, v, doModelGet);
181
        if (val != null) {
182
          found = true;
183
          break;
184
        }
185
      }
186
 
187
      if (!found) {
188
        return (returnFound) ? false : "";
189
      }
190
 
191
      if (!returnFound && typeof val == 'function') {
192
        val = this.mv(val, ctx, partials);
193
      }
194
 
195
      return val;
196
    },
197
 
198
    // higher order templates
199
    ls: function(func, cx, partials, text, tags) {
200
      var oldTags = this.options.delimiters;
201
 
202
      this.options.delimiters = tags;
203
      this.b(this.ct(coerceToString(func.call(cx, text)), cx, partials));
204
      this.options.delimiters = oldTags;
205
 
206
      return false;
207
    },
208
 
209
    // compile text
210
    ct: function(text, cx, partials) {
211
      if (this.options.disableLambda) {
212
        throw new Error('Lambda features disabled.');
213
      }
214
      return this.c.compile(text, this.options).render(cx, partials);
215
    },
216
 
217
    // template result buffering
218
    b: function(s) { this.buf += s; },
219
 
220
    fl: function() { var r = this.buf; this.buf = ''; return r; },
221
 
222
    // method replace section
223
    ms: function(func, ctx, partials, inverted, start, end, tags) {
224
      var textSource,
225
          cx = ctx[ctx.length - 1],
226
          result = func.call(cx);
227
 
228
      if (typeof result == 'function') {
229
        if (inverted) {
230
          return true;
231
        } else {
232
          textSource = (this.activeSub && this.subsText && this.subsText[this.activeSub]) ? this.subsText[this.activeSub] : this.text;
233
          return this.ls(result, cx, partials, textSource.substring(start, end), tags);
234
        }
235
      }
236
 
237
      return result;
238
    },
239
 
240
    // method replace variable
241
    mv: function(func, ctx, partials) {
242
      var cx = ctx[ctx.length - 1];
243
      var result = func.call(cx);
244
 
245
      if (typeof result == 'function') {
246
        return this.ct(coerceToString(result.call(cx)), cx, partials);
247
      }
248
 
249
      return result;
250
    },
251
 
252
    sub: function(name, context, partials, indent) {
253
      var f = this.subs[name];
254
      if (f) {
255
        this.activeSub = name;
256
        f(context, partials, this, indent);
257
        this.activeSub = false;
258
      }
259
    }
260
 
261
  };
262
 
263
  //Find a key in an object
264
  function findInScope(key, scope, doModelGet) {
265
    var val, checkVal;
266
 
267
    if (scope && typeof scope == 'object') {
268
 
269
      if (scope[key] != null) {
270
        val = scope[key];
271
 
272
      // try lookup with get for backbone or similar model data
273
      } else if (doModelGet && scope.get && typeof scope.get == 'function') {
274
        val = scope.get(key);
275
      }
276
    }
277
 
278
    return val;
279
  }
280
 
281
  function createSpecializedPartial(instance, subs, partials, stackSubs, stackPartials, stackText) {
282
    function PartialTemplate() {};
283
    PartialTemplate.prototype = instance;
284
    function Substitutions() {};
285
    Substitutions.prototype = instance.subs;
286
    var key;
287
    var partial = new PartialTemplate();
288
    partial.subs = new Substitutions();
289
    partial.subsText = {};  //hehe. substext.
290
    partial.buf = '';
291
 
292
    stackSubs = stackSubs || {};
293
    partial.stackSubs = stackSubs;
294
    partial.subsText = stackText;
295
    for (key in subs) {
296
      if (!stackSubs[key]) stackSubs[key] = subs[key];
297
    }
298
    for (key in stackSubs) {
299
      partial.subs[key] = stackSubs[key];
300
    }
301
 
302
    stackPartials = stackPartials || {};
303
    partial.stackPartials = stackPartials;
304
    for (key in partials) {
305
      if (!stackPartials[key]) stackPartials[key] = partials[key];
306
    }
307
    for (key in stackPartials) {
308
      partial.partials[key] = stackPartials[key];
309
    }
310
 
311
    return partial;
312
  }
313
 
314
  var rAmp = /&/g,
315
      rLt = /</g,
316
      rGt = />/g,
317
      rApos = /\'/g,
318
      rQuot = /\"/g,
319
      hChars = /[&<>\"\']/;
320
 
321
  function coerceToString(val) {
322
    return String((val === null || val === undefined) ? '' : val);
323
  }
324
 
325
  function hoganEscape(str) {
326
    str = coerceToString(str);
327
    return hChars.test(str) ?
328
      str
329
        .replace(rAmp, '&amp;')
330
        .replace(rLt, '&lt;')
331
        .replace(rGt, '&gt;')
332
        .replace(rApos, '&#39;')
333
        .replace(rQuot, '&quot;') :
334
      str;
335
  }
336
 
337
  var isArray = Array.isArray || function(a) {
338
    return Object.prototype.toString.call(a) === '[object Array]';
339
  };
340
 
341
})(typeof exports !== 'undefined' ? exports : Hogan);
342
 
343
 
344
 
345
(function (Hogan) {
346
  // Setup regex  assignments
347
  // remove whitespace according to Mustache spec
348
  var rIsWhitespace = /\S/,
349
      rQuot = /\"/g,
350
      rNewline =  /\n/g,
351
      rCr = /\r/g,
352
      rSlash = /\\/g;
353
 
354
  Hogan.tags = {
355
    '#': 1, '^': 2, '<': 3, '$': 4,
356
    '/': 5, '!': 6, '>': 7, '=': 8, '_v': 9,
357
    '{': 10, '&': 11, '_t': 12
358
  };
359
 
360
  Hogan.scan = function scan(text, delimiters) {
361
    var len = text.length,
362
        IN_TEXT = 0,
363
        IN_TAG_TYPE = 1,
364
        IN_TAG = 2,
365
        state = IN_TEXT,
366
        tagType = null,
367
        tag = null,
368
        buf = '',
369
        tokens = [],
370
        seenTag = false,
371
        i = 0,
372
        lineStart = 0,
373
        otag = '{{',
374
        ctag = '}}';
375
 
376
    function addBuf() {
377
      if (buf.length > 0) {
378
        tokens.push({tag: '_t', text: new String(buf)});
379
        buf = '';
380
      }
381
    }
382
 
383
    function lineIsWhitespace() {
384
      var isAllWhitespace = true;
385
      for (var j = lineStart; j < tokens.length; j++) {
386
        isAllWhitespace =
387
          (Hogan.tags[tokens[j].tag] < Hogan.tags['_v']) ||
388
          (tokens[j].tag == '_t' && tokens[j].text.match(rIsWhitespace) === null);
389
        if (!isAllWhitespace) {
390
          return false;
391
        }
392
      }
393
 
394
      return isAllWhitespace;
395
    }
396
 
397
    function filterLine(haveSeenTag, noNewLine) {
398
      addBuf();
399
 
400
      if (haveSeenTag && lineIsWhitespace()) {
401
        for (var j = lineStart, next; j < tokens.length; j++) {
402
          if (tokens[j].text) {
403
            if ((next = tokens[j+1]) && next.tag == '>') {
404
              // set indent to token value
405
              next.indent = tokens[j].text.toString()
406
            }
407
            tokens.splice(j, 1);
408
          }
409
        }
410
      } else if (!noNewLine) {
411
        tokens.push({tag:'\n'});
412
      }
413
 
414
      seenTag = false;
415
      lineStart = tokens.length;
416
    }
417
 
418
    function changeDelimiters(text, index) {
419
      var close = '=' + ctag,
420
          closeIndex = text.indexOf(close, index),
421
          delimiters = trim(
422
            text.substring(text.indexOf('=', index) + 1, closeIndex)
423
          ).split(' ');
424
 
425
      otag = delimiters[0];
426
      ctag = delimiters[delimiters.length - 1];
427
 
428
      return closeIndex + close.length - 1;
429
    }
430
 
431
    if (delimiters) {
432
      delimiters = delimiters.split(' ');
433
      otag = delimiters[0];
434
      ctag = delimiters[1];
435
    }
436
 
437
    for (i = 0; i < len; i++) {
438
      if (state == IN_TEXT) {
439
        if (tagChange(otag, text, i)) {
440
          --i;
441
          addBuf();
442
          state = IN_TAG_TYPE;
443
        } else {
444
          if (text.charAt(i) == '\n') {
445
            filterLine(seenTag);
446
          } else {
447
            buf += text.charAt(i);
448
          }
449
        }
450
      } else if (state == IN_TAG_TYPE) {
451
        i += otag.length - 1;
452
        tag = Hogan.tags[text.charAt(i + 1)];
453
        tagType = tag ? text.charAt(i + 1) : '_v';
454
        if (tagType == '=') {
455
          i = changeDelimiters(text, i);
456
          state = IN_TEXT;
457
        } else {
458
          if (tag) {
459
            i++;
460
          }
461
          state = IN_TAG;
462
        }
463
        seenTag = i;
464
      } else {
465
        if (tagChange(ctag, text, i)) {
466
          tokens.push({tag: tagType, n: trim(buf), otag: otag, ctag: ctag,
467
                       i: (tagType == '/') ? seenTag - otag.length : i + ctag.length});
468
          buf = '';
469
          i += ctag.length - 1;
470
          state = IN_TEXT;
471
          if (tagType == '{') {
472
            if (ctag == '}}') {
473
              i++;
474
            } else {
475
              cleanTripleStache(tokens[tokens.length - 1]);
476
            }
477
          }
478
        } else {
479
          buf += text.charAt(i);
480
        }
481
      }
482
    }
483
 
484
    filterLine(seenTag, true);
485
 
486
    return tokens;
487
  }
488
 
489
  function cleanTripleStache(token) {
490
    if (token.n.substr(token.n.length - 1) === '}') {
491
      token.n = token.n.substring(0, token.n.length - 1);
492
    }
493
  }
494
 
495
  function trim(s) {
496
    if (s.trim) {
497
      return s.trim();
498
    }
499
 
500
    return s.replace(/^\s*|\s*$/g, '');
501
  }
502
 
503
  function tagChange(tag, text, index) {
504
    if (text.charAt(index) != tag.charAt(0)) {
505
      return false;
506
    }
507
 
508
    for (var i = 1, l = tag.length; i < l; i++) {
509
      if (text.charAt(index + i) != tag.charAt(i)) {
510
        return false;
511
      }
512
    }
513
 
514
    return true;
515
  }
516
 
517
  // the tags allowed inside super templates
518
  var allowedInSuper = {'_t': true, '\n': true, '$': true, '/': true};
519
 
520
  function buildTree(tokens, kind, stack, customTags) {
521
    var instructions = [],
522
        opener = null,
523
        tail = null,
524
        token = null;
525
 
526
    tail = stack[stack.length - 1];
527
 
528
    while (tokens.length > 0) {
529
      token = tokens.shift();
530
 
531
      if (tail && tail.tag == '<' && !(token.tag in allowedInSuper)) {
532
        throw new Error('Illegal content in < super tag.');
533
      }
534
 
535
      if (Hogan.tags[token.tag] <= Hogan.tags['$'] || isOpener(token, customTags)) {
536
        stack.push(token);
537
        token.nodes = buildTree(tokens, token.tag, stack, customTags);
538
      } else if (token.tag == '/') {
539
        if (stack.length === 0) {
540
          throw new Error('Closing tag without opener: /' + token.n);
541
        }
542
        opener = stack.pop();
543
        if (token.n != opener.n && !isCloser(token.n, opener.n, customTags)) {
544
          throw new Error('Nesting error: ' + opener.n + ' vs. ' + token.n);
545
        }
546
        opener.end = token.i;
547
        return instructions;
548
      } else if (token.tag == '\n') {
549
        token.last = (tokens.length == 0) || (tokens[0].tag == '\n');
550
      }
551
 
552
      instructions.push(token);
553
    }
554
 
555
    if (stack.length > 0) {
556
      throw new Error('missing closing tag: ' + stack.pop().n);
557
    }
558
 
559
    return instructions;
560
  }
561
 
562
  function isOpener(token, tags) {
563
    for (var i = 0, l = tags.length; i < l; i++) {
564
      if (tags[i].o == token.n) {
565
        token.tag = '#';
566
        return true;
567
      }
568
    }
569
  }
570
 
571
  function isCloser(close, open, tags) {
572
    for (var i = 0, l = tags.length; i < l; i++) {
573
      if (tags[i].c == close && tags[i].o == open) {
574
        return true;
575
      }
576
    }
577
  }
578
 
579
  function stringifySubstitutions(obj) {
580
    var items = [];
581
    for (var key in obj) {
582
      items.push('"' + esc(key) + '": function(c,p,t,i) {' + obj[key] + '}');
583
    }
584
    return "{ " + items.join(",") + " }";
585
  }
586
 
587
  function stringifyPartials(codeObj) {
588
    var partials = [];
589
    for (var key in codeObj.partials) {
590
      partials.push('"' + esc(key) + '":{name:"' + esc(codeObj.partials[key].name) + '", ' + stringifyPartials(codeObj.partials[key]) + "}");
591
    }
592
    return "partials: {" + partials.join(",") + "}, subs: " + stringifySubstitutions(codeObj.subs);
593
  }
594
 
595
  Hogan.stringify = function(codeObj, text, options) {
596
    return "{code: function (c,p,i) { " + Hogan.wrapMain(codeObj.code) + " }," + stringifyPartials(codeObj) +  "}";
597
  }
598
 
599
  var serialNo = 0;
600
  Hogan.generate = function(tree, text, options) {
601
    serialNo = 0;
602
    var context = { code: '', subs: {}, partials: {} };
603
    Hogan.walk(tree, context);
604
 
605
    if (options.asString) {
606
      return this.stringify(context, text, options);
607
    }
608
 
609
    return this.makeTemplate(context, text, options);
610
  }
611
 
612
  Hogan.wrapMain = function(code) {
613
    return 'var t=this;t.b(i=i||"");' + code + 'return t.fl();';
614
  }
615
 
616
  Hogan.template = Hogan.Template;
617
 
618
  Hogan.makeTemplate = function(codeObj, text, options) {
619
    var template = this.makePartials(codeObj);
620
    template.code = new Function('c', 'p', 'i', this.wrapMain(codeObj.code));
621
    return new this.template(template, text, this, options);
622
  }
623
 
624
  Hogan.makePartials = function(codeObj) {
625
    var key, template = {subs: {}, partials: codeObj.partials, name: codeObj.name};
626
    for (key in template.partials) {
627
      template.partials[key] = this.makePartials(template.partials[key]);
628
    }
629
    for (key in codeObj.subs) {
630
      template.subs[key] = new Function('c', 'p', 't', 'i', codeObj.subs[key]);
631
    }
632
    return template;
633
  }
634
 
635
  function esc(s) {
636
    return s.replace(rSlash, '\\\\')
637
            .replace(rQuot, '\\\"')
638
            .replace(rNewline, '\\n')
639
            .replace(rCr, '\\r');
640
  }
641
 
642
  function chooseMethod(s) {
643
    return (~s.indexOf('.')) ? 'd' : 'f';
644
  }
645
 
646
  function createPartial(node, context) {
647
    var prefix = "<" + (context.prefix || "");
648
    var sym = prefix + node.n + serialNo++;
649
    context.partials[sym] = {name: node.n, partials: {}};
650
    context.code += 't.b(t.rp("' +  esc(sym) + '",c,p,"' + (node.indent || '') + '"));';
651
    return sym;
652
  }
653
 
654
  Hogan.codegen = {
655
    '#': function(node, context) {
656
      context.code += 'if(t.s(t.' + chooseMethod(node.n) + '("' + esc(node.n) + '",c,p,1),' +
657
                      'c,p,0,' + node.i + ',' + node.end + ',"' + node.otag + " " + node.ctag + '")){' +
658
                      't.rs(c,p,' + 'function(c,p,t){';
659
      Hogan.walk(node.nodes, context);
660
      context.code += '});c.pop();}';
661
    },
662
 
663
    '^': function(node, context) {
664
      context.code += 'if(!t.s(t.' + chooseMethod(node.n) + '("' + esc(node.n) + '",c,p,1),c,p,1,0,0,"")){';
665
      Hogan.walk(node.nodes, context);
666
      context.code += '};';
667
    },
668
 
669
    '>': createPartial,
670
    '<': function(node, context) {
671
      var ctx = {partials: {}, code: '', subs: {}, inPartial: true};
672
      Hogan.walk(node.nodes, ctx);
673
      var template = context.partials[createPartial(node, context)];
674
      template.subs = ctx.subs;
675
      template.partials = ctx.partials;
676
    },
677
 
678
    '$': function(node, context) {
679
      var ctx = {subs: {}, code: '', partials: context.partials, prefix: node.n};
680
      Hogan.walk(node.nodes, ctx);
681
      context.subs[node.n] = ctx.code;
682
      if (!context.inPartial) {
683
        context.code += 't.sub("' + esc(node.n) + '",c,p,i);';
684
      }
685
    },
686
 
687
    '\n': function(node, context) {
688
      context.code += write('"\\n"' + (node.last ? '' : ' + i'));
689
    },
690
 
691
    '_v': function(node, context) {
692
      context.code += 't.b(t.v(t.' + chooseMethod(node.n) + '("' + esc(node.n) + '",c,p,0)));';
693
    },
694
 
695
    '_t': function(node, context) {
696
      context.code += write('"' + esc(node.text) + '"');
697
    },
698
 
699
    '{': tripleStache,
700
 
701
    '&': tripleStache
702
  }
703
 
704
  function tripleStache(node, context) {
705
    context.code += 't.b(t.t(t.' + chooseMethod(node.n) + '("' + esc(node.n) + '",c,p,0)));';
706
  }
707
 
708
  function write(s) {
709
    return 't.b(' + s + ');';
710
  }
711
 
712
  Hogan.walk = function(nodelist, context) {
713
    var func;
714
    for (var i = 0, l = nodelist.length; i < l; i++) {
715
      func = Hogan.codegen[nodelist[i].tag];
716
      func && func(nodelist[i], context);
717
    }
718
    return context;
719
  }
720
 
721
  Hogan.parse = function(tokens, text, options) {
722
    options = options || {};
723
    return buildTree(tokens, '', [], options.sectionTags || []);
724
  }
725
 
726
  Hogan.cache = {};
727
 
728
  Hogan.cacheKey = function(text, options) {
729
    return [text, !!options.asString, !!options.disableLambda, options.delimiters, !!options.modelGet].join('||');
730
  }
731
 
732
  Hogan.compile = function(text, options) {
733
    options = options || {};
734
    var key = Hogan.cacheKey(text, options);
735
    var template = this.cache[key];
736
 
737
    if (template) {
738
      var partials = template.partials;
739
      for (var name in partials) {
740
        delete partials[name].instance;
741
      }
742
      return template;
743
    }
744
 
745
    template = this.generate(this.parse(this.scan(text, options.delimiters), text, options), text, options);
746
    return this.cache[key] = template;
747
  }
748
})(typeof exports !== 'undefined' ? exports : Hogan);