UNPKG

120 kBJavaScriptView Raw
1/*! Swig v1.3.0 | https://paularmstrong.github.com/swig | @license https://github.com/paularmstrong/swig/blob/master/LICENSE */
2/*! DateZ (c) 2011 Tomo Universalis | @license https://github.com/TomoUniversalis/DateZ/blob/master/LISENCE */
3;(function(e,t,n){function i(n,s){if(!t[n]){if(!e[n]){var o=typeof require=="function"&&require;if(!s&&o)return o(n,!0);if(r)return r(n,!0);throw new Error("Cannot find module '"+n+"'")}var u=t[n]={exports:{}};e[n][0].call(u.exports,function(t){var r=e[n][1][t];return i(r?r:t)},u,u.exports)}return t[n].exports}var r=typeof require=="function"&&require;for(var s=0;s<n.length;s++)i(n[s]);return i})({1:[function(require,module,exports){
4var swig = require('../lib/swig');
5
6if (typeof window.define === 'function' && typeof window.define.amd === 'object') {
7 window.define('swig', [], function () {
8 return swig;
9 });
10} else {
11 window.swig = swig;
12}
13
14},{"../lib/swig":9}],2:[function(require,module,exports){
15var utils = require('./utils');
16
17var _months = {
18 full: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
19 abbr: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
20 },
21 _days = {
22 full: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
23 abbr: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
24 alt: {'-1': 'Yesterday', 0: 'Today', 1: 'Tomorrow'}
25 };
26
27/*
28DateZ is licensed under the MIT License:
29Copyright (c) 2011 Tomo Universalis (http://tomouniversalis.com)
30Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
31The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
32THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33*/
34exports.tzOffset = 0;
35exports.DateZ = function () {
36 var members = {
37 'default': ['getUTCDate', 'getUTCDay', 'getUTCFullYear', 'getUTCHours', 'getUTCMilliseconds', 'getUTCMinutes', 'getUTCMonth', 'getUTCSeconds', 'toISOString', 'toGMTString', 'toUTCString', 'valueOf', 'getTime'],
38 z: ['getDate', 'getDay', 'getFullYear', 'getHours', 'getMilliseconds', 'getMinutes', 'getMonth', 'getSeconds', 'getYear', 'toDateString', 'toLocaleDateString', 'toLocaleTimeString']
39 },
40 d = this;
41
42 d.date = d.dateZ = (arguments.length > 1) ? new Date(Date.UTC.apply(Date, arguments) + ((new Date()).getTimezoneOffset() * 60000)) : (arguments.length === 1) ? new Date(new Date(arguments['0'])) : new Date();
43
44 d.timezoneOffset = d.dateZ.getTimezoneOffset();
45
46 utils.each(members.z, function (name) {
47 d[name] = function () {
48 return d.dateZ[name]();
49 };
50 });
51 utils.each(members['default'], function (name) {
52 d[name] = function () {
53 return d.date[name]();
54 };
55 });
56
57 this.setTimezoneOffset(exports.tzOffset);
58};
59exports.DateZ.prototype = {
60 getTimezoneOffset: function () {
61 return this.timezoneOffset;
62 },
63 setTimezoneOffset: function (offset) {
64 this.timezoneOffset = offset;
65 this.dateZ = new Date(this.date.getTime() + this.date.getTimezoneOffset() * 60000 - this.timezoneOffset * 60000);
66 return this;
67 }
68};
69
70// Day
71exports.d = function (input) {
72 return (input.getDate() < 10 ? '0' : '') + input.getDate();
73};
74exports.D = function (input) {
75 return _days.abbr[input.getDay()];
76};
77exports.j = function (input) {
78 return input.getDate();
79};
80exports.l = function (input) {
81 return _days.full[input.getDay()];
82};
83exports.N = function (input) {
84 var d = input.getDay();
85 return (d >= 1) ? d : 7;
86};
87exports.S = function (input) {
88 var d = input.getDate();
89 return (d % 10 === 1 && d !== 11 ? 'st' : (d % 10 === 2 && d !== 12 ? 'nd' : (d % 10 === 3 && d !== 13 ? 'rd' : 'th')));
90};
91exports.w = function (input) {
92 return input.getDay();
93};
94exports.z = function (input, offset, abbr) {
95 var year = input.getFullYear(),
96 e = new exports.DateZ(year, input.getMonth(), input.getDate(), 12, 0, 0),
97 d = new exports.DateZ(year, 0, 1, 12, 0, 0);
98
99 e.setTimezoneOffset(offset, abbr);
100 d.setTimezoneOffset(offset, abbr);
101 return Math.round((e - d) / 86400000);
102};
103
104// Week
105exports.W = function (input) {
106 var target = new Date(input.valueOf()),
107 dayNr = (input.getDay() + 6) % 7,
108 fThurs;
109
110 target.setDate(target.getDate() - dayNr + 3);
111 fThurs = target.valueOf();
112 target.setMonth(0, 1);
113 if (target.getDay() !== 4) {
114 target.setMonth(0, 1 + ((4 - target.getDay()) + 7) % 7);
115 }
116
117 return 1 + Math.ceil((fThurs - target) / 604800000);
118};
119
120// Month
121exports.F = function (input) {
122 return _months.full[input.getMonth()];
123};
124exports.m = function (input) {
125 return (input.getMonth() < 9 ? '0' : '') + (input.getMonth() + 1);
126};
127exports.M = function (input) {
128 return _months.abbr[input.getMonth()];
129};
130exports.n = function (input) {
131 return input.getMonth() + 1;
132};
133exports.t = function (input) {
134 return 32 - (new Date(input.getFullYear(), input.getMonth(), 32).getDate());
135};
136
137// Year
138exports.L = function (input) {
139 return new Date(input.getFullYear(), 1, 29).getDate() === 29;
140};
141exports.o = function (input) {
142 var target = new Date(input.valueOf());
143 target.setDate(target.getDate() - ((input.getDay() + 6) % 7) + 3);
144 return target.getFullYear();
145};
146exports.Y = function (input) {
147 return input.getFullYear();
148};
149exports.y = function (input) {
150 return (input.getFullYear().toString()).substr(2);
151};
152
153// Time
154exports.a = function (input) {
155 return input.getHours() < 12 ? 'am' : 'pm';
156};
157exports.A = function (input) {
158 return input.getHours() < 12 ? 'AM' : 'PM';
159};
160exports.B = function (input) {
161 var hours = input.getUTCHours(), beats;
162 hours = (hours === 23) ? 0 : hours + 1;
163 beats = Math.abs(((((hours * 60) + input.getUTCMinutes()) * 60) + input.getUTCSeconds()) / 86.4).toFixed(0);
164 return ('000'.concat(beats).slice(beats.length));
165};
166exports.g = function (input) {
167 var h = input.getHours();
168 return h === 0 ? 12 : (h > 12 ? h - 12 : h);
169};
170exports.G = function (input) {
171 return input.getHours();
172};
173exports.h = function (input) {
174 var h = input.getHours();
175 return ((h < 10 || (12 < h && 22 > h)) ? '0' : '') + ((h < 12) ? h : h - 12);
176};
177exports.H = function (input) {
178 var h = input.getHours();
179 return (h < 10 ? '0' : '') + h;
180};
181exports.i = function (input) {
182 var m = input.getMinutes();
183 return (m < 10 ? '0' : '') + m;
184};
185exports.s = function (input) {
186 var s = input.getSeconds();
187 return (s < 10 ? '0' : '') + s;
188};
189//u = function () { return ''; },
190
191// Timezone
192//e = function () { return ''; },
193//I = function () { return ''; },
194exports.O = function (input) {
195 var tz = input.getTimezoneOffset();
196 return (tz < 0 ? '-' : '+') + (tz / 60 < 10 ? '0' : '') + Math.abs((tz / 60)) + '00';
197};
198//T = function () { return ''; },
199exports.Z = function (input) {
200 return input.getTimezoneOffset() * 60;
201};
202
203// Full Date/Time
204exports.c = function (input) {
205 return input.toISOString();
206};
207exports.r = function (input) {
208 return input.toUTCString();
209};
210exports.U = function (input) {
211 return input.getTime() / 1000;
212};
213
214},{"./utils":26}],3:[function(require,module,exports){
215(function(){var utils = require('./utils'),
216 dateFormatter = require('./dateformatter');
217
218/**
219 * Helper method to recursively run a filter across an object/array and apply it to all of the object/array's values.
220 * @param {*} input
221 * @return {*}
222 * @private
223 */
224function iterateFilter(input) {
225 var self = this,
226 out = {};
227
228 if (utils.isArray(input)) {
229 return utils.map(input, function (value) {
230 return self.apply(null, arguments);
231 });
232 }
233
234 if (typeof input === 'object') {
235 utils.each(input, function (value, key) {
236 out[key] = self.apply(null, arguments);
237 });
238 return out;
239 }
240
241 return;
242}
243
244/**
245 * Backslash-escape characters that need to be escaped.
246 *
247 * @example
248 * {{ "\"quoted string\""|addslashes }}
249 * // => \"quoted string\"
250 *
251 * @param {*} input
252 * @return {*} Backslash-escaped string.
253 */
254exports.addslashes = function (input) {
255 var out = iterateFilter.apply(exports.addslashes, arguments);
256 if (out !== undefined) {
257 return out;
258 }
259
260 return input.replace(/\\/g, '\\\\').replace(/\'/g, "\\'").replace(/\"/g, '\\"');
261};
262
263/**
264 * Upper-case the first letter of the input and lower-case the rest.
265 *
266 * @example
267 * {{ "i like Burritos"|capitalize }}
268 * // => I like burritos
269 *
270 * @param {*} input If given an array or object, each string member will be run through the filter individually.
271 * @return {*} Returns the same type as the input.
272 */
273exports.capitalize = function (input) {
274 var out = iterateFilter.apply(exports.capitalize, arguments);
275 if (out !== undefined) {
276 return out;
277 }
278
279 return input.toString().charAt(0).toUpperCase() + input.toString().substr(1).toLowerCase();
280};
281
282/**
283 * Format a date or Date-compatible string.
284 *
285 * @example
286 * // now = new Date();
287 * {{ now|date('Y-m-d') }}
288 * // => 2013-08-14
289 *
290 * @param {?(string|date)} input
291 * @param {string} format PHP-style date format compatible string.
292 * @param {number=} offset Timezone offset from GMT in minutes.
293 * @param {string=} abbr Timezone abbreviation. Used for output only.
294 * @return {string} Formatted date string.
295 */
296exports.date = function (input, format, offset, abbr) {
297 var l = format.length,
298 date = new dateFormatter.DateZ(input),
299 cur,
300 i = 0,
301 out = '';
302
303 if (offset) {
304 date.setTimezoneOffset(offset, abbr);
305 }
306
307 for (i; i < l; i += 1) {
308 cur = format.charAt(i);
309 if (dateFormatter.hasOwnProperty(cur)) {
310 out += dateFormatter[cur](date, offset, abbr);
311 } else {
312 out += cur;
313 }
314 }
315 return out;
316};
317
318/**
319 * If the input is `undefined`, `null`, or `false`, a default return value can be specified.
320 *
321 * @example
322 * {{ null_value|default('Tacos') }}
323 * // => Tacos
324 *
325 * @example
326 * {{ "Burritos"|default("Tacos") }}
327 * // => Burritos
328 *
329 * @param {*} input
330 * @param {*} def Value to return if `input` is `undefined`, `null`, or `false`.
331 * @return {*} `input` or `def` value.
332 */
333exports.default = function (input, def) {
334 return (typeof input !== 'undefined' && (input || typeof input === 'number')) ? input : def;
335};
336
337/**
338 * Force escape the output of the variable. Optionally use `e` as a shortcut filter name. This filter will be applied by default if autoescape is turned on.
339 *
340 * @example
341 * {{ "<blah>"|escape }}
342 * // => &lt;blah&gt;
343 *
344 * @example
345 * {{ "<blah>"|e("js") }}
346 * // => \u003Cblah\u003E
347 *
348 * @param {*} input
349 * @param {string} [type='html'] If you pass the string js in as the type, output will be escaped so that it is safe for JavaScript execution.
350 * @return {string} Escaped string.
351 */
352exports.escape = function (input, type) {
353 var out = iterateFilter.apply(exports.escape, arguments),
354 inp = input,
355 i = 0,
356 code;
357
358 if (out !== undefined) {
359 return out;
360 }
361
362 if (typeof input !== 'string') {
363 return input;
364 }
365
366 out = '';
367
368 switch (type) {
369 case 'js':
370 inp = inp.replace(/\\/g, '\\u005C');
371 for (i; i < inp.length; i += 1) {
372 code = inp.charCodeAt(i);
373 if (code < 32) {
374 code = code.toString(16).toUpperCase();
375 code = (code.length < 2) ? '0' + code : code;
376 out += '\\u00' + code;
377 } else {
378 out += inp[i];
379 }
380 }
381 return out.replace(/&/g, '\\u0026')
382 .replace(/</g, '\\u003C')
383 .replace(/>/g, '\\u003E')
384 .replace(/\'/g, '\\u0027')
385 .replace(/"/g, '\\u0022')
386 .replace(/\=/g, '\\u003D')
387 .replace(/-/g, '\\u002D')
388 .replace(/;/g, '\\u003B');
389
390 default:
391 return inp.replace(/&(?!amp;|lt;|gt;|quot;|#39;)/g, '&amp;')
392 .replace(/</g, '&lt;')
393 .replace(/>/g, '&gt;')
394 .replace(/"/g, '&quot;')
395 .replace(/'/g, '&#39;');
396 }
397};
398exports.e = exports.escape;
399
400/**
401 * Get the first item in an array or character in a string. All other objects will attempt to return the first value available.
402 *
403 * @example
404 * // my_arr = ['a', 'b', 'c']
405 * {{ my_arr|first }}
406 * // => a
407 *
408 * @example
409 * // my_val = 'Tacos'
410 * {{ my_val|first }}
411 * // T
412 *
413 * @param {*} input
414 * @return {*} The first item of the array or first character of the string input.
415 */
416exports.first = function (input) {
417 if (typeof input === 'object' && !utils.isArray(input)) {
418 var keys = utils.keys(input);
419 return input[keys[0]];
420 }
421
422 if (typeof input === 'string') {
423 return input.substr(0, 1);
424 }
425
426 return input[0];
427};
428
429/**
430 * Group an array of objects by a common key. If an array is not provided, the input value will be returned untouched.
431 *
432 * @example
433 * // people = [{ age: 23, name: 'Paul' }, { age: 26, name: 'Jane' }, { age: 23, name: 'Jim' }];
434 * {% for agegroup in people|groupBy('age') %}
435 * <h2>{{ loop.key }}</h2>
436 * <ul>
437 * {% for person in agegroup %}
438 * <li>{{ person.name }}</li>
439 * {% endfor %}
440 * </ul>
441 * {% endfor %}
442 *
443 * @param {*} input Input object.
444 * @param {string} key Key to group by.
445 * @return {object} Grouped arrays by given key.
446 */
447exports.groupBy = function (input, key) {
448 if (!utils.isArray(input)) {
449 return input;
450 }
451
452 var out = {};
453
454 utils.each(input, function (value) {
455 if (!value.hasOwnProperty(key)) {
456 return;
457 }
458
459 var keyname = value[key],
460 newVal = utils.extend({}, value);
461 delete value[key];
462
463 if (!out[keyname]) {
464 out[keyname] = [];
465 }
466
467 out[keyname].push(value);
468 });
469
470 return out;
471};
472
473/**
474 * Join the input with a string.
475 *
476 * @example
477 * // my_array = ['foo', 'bar', 'baz']
478 * {{ my_array|join(', ') }}
479 * // => foo, bar, baz
480 *
481 * @example
482 * // my_key_object = { a: 'foo', b: 'bar', c: 'baz' }
483 * {{ my_key_object|join(' and ') }}
484 * // => foo and bar and baz
485 *
486 * @param {*} input
487 * @param {string} glue String value to join items together.
488 * @return {string}
489 */
490exports.join = function (input, glue) {
491 if (utils.isArray(input)) {
492 return input.join(glue);
493 }
494
495 if (typeof input === 'object') {
496 var out = [];
497 utils.each(input, function (value) {
498 out.push(value);
499 });
500 return out.join(glue);
501 }
502 return input;
503};
504
505/**
506 * Return a string representation of an JavaScript object.
507 *
508 * Backwards compatible with swig@0.x.x using `json_encode`.
509 *
510 * @example
511 * // val = { a: 'b' }
512 * {{ val|json }}
513 * // => {"a":"b"}
514 *
515 * @example
516 * // val = { a: 'b' }
517 * {{ val|json(4) }}
518 * // => {
519 * // "a": "b"
520 * // }
521 *
522 * @param {*} input
523 * @param {number} [indent] Number of spaces to indent for pretty-formatting.
524 * @return {string} A valid JSON string.
525 */
526exports.json = function (input, indent) {
527 return JSON.stringify(input, null, indent || 0);
528};
529exports.json_encode = exports.json;
530
531/**
532 * Get the last item in an array or character in a string. All other objects will attempt to return the last value available.
533 *
534 * @example
535 * // my_arr = ['a', 'b', 'c']
536 * {{ my_arr|last }}
537 * // => c
538 *
539 * @example
540 * // my_val = 'Tacos'
541 * {{ my_val|last }}
542 * // s
543 *
544 * @param {*} input
545 * @return {*} The last item of the array or last character of the string.input.
546 */
547exports.last = function (input) {
548 if (typeof input === 'object' && !utils.isArray(input)) {
549 var keys = utils.keys(input);
550 return input[keys[keys.length - 1]];
551 }
552
553 if (typeof input === 'string') {
554 return input.charAt(input.length - 1);
555 }
556
557 return input[input.length - 1];
558};
559
560/**
561 * Return the input in all lowercase letters.
562 *
563 * @example
564 * {{ "FOOBAR"|lower }}
565 * // => foobar
566 *
567 * @example
568 * // myObj = { a: 'FOO', b: 'BAR' }
569 * {{ myObj|lower|join('') }}
570 * // => foobar
571 *
572 * @param {*} input
573 * @return {*} Returns the same type as the input.
574 */
575exports.lower = function (input) {
576 var out = iterateFilter.apply(exports.lower, arguments);
577 if (out !== undefined) {
578 return out;
579 }
580
581 return input.toString().toLowerCase();
582};
583
584/**
585 * Deprecated in favor of <a href="#safe">safe</a>.
586 */
587exports.raw = function (input) {
588 return exports.safe(input);
589};
590exports.raw.safe = true;
591
592/**
593 * Returns a new string with the matched search pattern replaced by the given replacement string. Uses JavaScript's built-in String.replace() method.
594 *
595 * @example
596 * // my_var = 'foobar';
597 * {{ my_var|replace('o', 'e', 'g') }}
598 * // => feebar
599 *
600 * @example
601 * // my_var = "farfegnugen";
602 * {{ my_var|replace('^f', 'p') }}
603 * // => parfegnugen
604 *
605 * @example
606 * // my_var = 'a1b2c3';
607 * {{ my_var|replace('\w', '0', 'g') }}
608 * // => 010203
609 *
610 * @param {string} input
611 * @param {string} search String or pattern to replace from the input.
612 * @param {string} replacement String to replace matched pattern.
613 * @param {string} [flags] Regular Expression flags. 'g': global match, 'i': ignore case, 'm': match over multiple lines
614 * @return {string} Replaced string.
615 */
616exports.replace = function (input, search, replacement, flags) {
617 var r = new RegExp(search, flags);
618 return input.replace(r, replacement);
619};
620
621/**
622 * Reverse sort the input. This is an alias for <code data-language="swig">{{ input|sort(true) }}</code>.
623 *
624 * @example
625 * // val = [1, 2, 3];
626 * {{ val|reverse }}
627 * // => 3,2,1
628 *
629 * @param {array} input
630 * @return {array} Reversed array. The original input object is returned if it was not an array.
631 */
632exports.reverse = function (input) {
633 return exports.sort(input, true);
634};
635
636/**
637 * Forces the input to not be auto-escaped. Use this only on content that you know is safe to be rendered on your page.
638 *
639 * @example
640 * // my_var = "<p>Stuff</p>";
641 * {{ my_var|safe }}
642 * // => <p>Stuff</p>
643 *
644 * @param {*} input
645 * @return {*} The input exactly how it was given, regardless of autoescaping status.
646 */
647exports.safe = function (input) {
648 // This is a magic filter. Its logic is hard-coded into Swig's parser.
649 return input;
650};
651exports.safe.safe = true;
652
653/**
654 * Sort the input in an ascending direction.
655 * If given an object, will return the keys as a sorted array.
656 * If given a string, each character will be sorted individually.
657 *
658 * @example
659 * // val = [2, 6, 4];
660 * {{ val|sort }}
661 * // => 2,4,6
662 *
663 * @example
664 * // val = 'zaq';
665 * {{ val|sort }}
666 * // => aqz
667 *
668 * @example
669 * // val = { bar: 1, foo: 2 }
670 * {{ val|sort(true) }}
671 * // => foo,bar
672 *
673 * @param {*} input
674 * @param {boolean} [reverse=false] Output is given reverse-sorted if true.
675 * @return {*} Sorted array;
676 */
677exports.sort = function (input, reverse) {
678 var out;
679 if (utils.isArray(input)) {
680 out = input.sort();
681 } else {
682 switch (typeof input) {
683 case 'object':
684 out = utils.keys(input).sort();
685 break;
686 case 'string':
687 out = input.split('');
688 if (reverse) {
689 return out.reverse().join('');
690 }
691 return out.sort().join('');
692 }
693 }
694
695 if (out && reverse) {
696 return out.reverse();
697 }
698
699 return out || input;
700};
701
702/**
703 * Strip HTML tags.
704 *
705 * @example
706 * // stuff = '<p>foobar</p>';
707 * {{ stuff|striptags }}
708 * // => foobar
709 *
710 * @param {*} input
711 * @return {*} Returns the same object as the input, but with all string values stripped of tags.
712 */
713exports.striptags = function (input) {
714 var out = iterateFilter.apply(exports.striptags, arguments);
715 if (out !== undefined) {
716 return out;
717 }
718
719 return input.toString().replace(/(<([^>]+)>)/ig, '');
720};
721
722/**
723 * Capitalizes every word given and lower-cases all other letters.
724 *
725 * @example
726 * // my_str = 'this is soMe text';
727 * {{ my_str|title }}
728 * // => This Is Some Text
729 *
730 * @example
731 * // my_arr = ['hi', 'this', 'is', 'an', 'array'];
732 * {{ my_arr|title|join(' ') }}
733 * // => Hi This Is An Array
734 *
735 * @param {*} input
736 * @return {*} Returns the same object as the input, but with all words in strings title-cased.
737 */
738exports.title = function (input) {
739 var out = iterateFilter.apply(exports.title, arguments);
740 if (out !== undefined) {
741 return out;
742 }
743
744 return input.toString().replace(/\w\S*/g, function (str) {
745 return str.charAt(0).toUpperCase() + str.substr(1).toLowerCase();
746 });
747};
748
749/**
750 * Remove all duplicate items from an array.
751 *
752 * @example
753 * // my_arr = [1, 2, 3, 4, 4, 3, 2, 1];
754 * {{ my_arr|uniq|join(',') }}
755 * // => 1,2,3,4
756 *
757 * @param {array} input
758 * @return {array} Array with unique items. If input was not an array, the original item is returned untouched.
759 */
760exports.uniq = function (input) {
761 var result;
762
763 if (!input || !utils.isArray(input)) {
764 return '';
765 }
766
767 result = [];
768 utils.each(input, function (v) {
769 if (result.indexOf(v) === -1) {
770 result.push(v);
771 }
772 });
773 return result;
774};
775
776/**
777 * Convert the input to all uppercase letters. If an object or array is provided, all values will be uppercased.
778 *
779 * @example
780 * // my_str = 'tacos';
781 * {{ my_str|upper }}
782 * // => TACOS
783 *
784 * @example
785 * // my_arr = ['tacos', 'burritos'];
786 * {{ my_arr|upper|join(' & ') }}
787 * // => TACOS & BURRITOS
788 *
789 * @param {*} input
790 * @return {*} Returns the same type as the input, with all strings upper-cased.
791 */
792exports.upper = function (input) {
793 var out = iterateFilter.apply(exports.upper, arguments);
794 if (out !== undefined) {
795 return out;
796 }
797
798 return input.toString().toUpperCase();
799};
800
801/**
802 * URL-encode a string. If an object or array is passed, all values will be URL-encoded.
803 *
804 * @example
805 * // my_str = 'param=1&anotherParam=2';
806 * {{ my_str|url_encode }}
807 * // => param%3D1%26anotherParam%3D2
808 *
809 * @param {*} input
810 * @return {*} URL-encoded string.
811 */
812exports.url_encode = function (input) {
813 var out = iterateFilter.apply(exports.url_encode, arguments);
814 if (out !== undefined) {
815 return out;
816 }
817 return encodeURIComponent(input);
818};
819
820/**
821 * URL-decode a string. If an object or array is passed, all values will be URL-decoded.
822 *
823 * @example
824 * // my_str = 'param%3D1%26anotherParam%3D2';
825 * {{ my_str|url_decode }}
826 * // => param=1&anotherParam=2
827 *
828 * @param {*} input
829 * @return {*} URL-decoded string.
830 */
831exports.url_decode = function (input) {
832 var out = iterateFilter.apply(exports.url_decode, arguments);
833 if (out !== undefined) {
834 return out;
835 }
836 return decodeURIComponent(input);
837};
838
839})()
840},{"./dateformatter":2,"./utils":26}],4:[function(require,module,exports){
841var utils = require('./utils');
842
843/**
844 * A lexer token.
845 * @typedef {object} LexerToken
846 * @property {string} match The string that was matched.
847 * @property {number} type Lexer type enum.
848 * @property {number} length Length of the original string processed.
849 */
850
851/**
852 * Enum for token types.
853 * @readonly
854 * @enum {number}
855 */
856var TYPES = {
857 /** Whitespace */
858 WHITESPACE: 0,
859 /** Plain string */
860 STRING: 1,
861 /** Variable filter */
862 FILTER: 2,
863 /** Empty variable filter */
864 FILTEREMPTY: 3,
865 /** Function */
866 FUNCTION: 4,
867 /** Function with no arguments */
868 FUNCTIONEMPTY: 5,
869 /** Open parenthesis */
870 PARENOPEN: 6,
871 /** Close parenthesis */
872 PARENCLOSE: 7,
873 /** Comma */
874 COMMA: 8,
875 /** Variable */
876 VAR: 9,
877 /** Number */
878 NUMBER: 10,
879 /** Math operator */
880 OPERATOR: 11,
881 /** Open square bracket */
882 BRACKETOPEN: 12,
883 /** Close square bracket */
884 BRACKETCLOSE: 13,
885 /** Key on an object using dot-notation */
886 DOTKEY: 14,
887 /** Start of an array */
888 ARRAYOPEN: 15,
889 /** End of an array
890 * Currently unused
891 ARRAYCLOSE: 16, */
892 /** Open curly brace */
893 CURLYOPEN: 17,
894 /** Close curly brace */
895 CURLYCLOSE: 18,
896 /** Colon (:) */
897 COLON: 19,
898 /** JavaScript-valid comparator */
899 COMPARATOR: 20,
900 /** Boolean logic */
901 LOGIC: 21,
902 /** Boolean logic "not" */
903 NOT: 22,
904 /** true or false */
905 BOOL: 23,
906 /** Variable assignment */
907 ASSIGNMENT: 24,
908 /** Start of a method */
909 METHODOPEN: 25,
910 /** End of a method
911 * Currently unused
912 METHODEND: 26, */
913 /** Unknown type */
914 UNKNOWN: 100
915 },
916 rules = [
917 {
918 type: TYPES.WHITESPACE,
919 regex: [
920 /^\s+/
921 ]
922 },
923 {
924 type: TYPES.STRING,
925 regex: [
926 /^""/,
927 /^".*?[^\\]"/,
928 /^''/,
929 /^'.*?[^\\]'/
930 ]
931 },
932 {
933 type: TYPES.FILTER,
934 regex: [
935 /^\|\s*(\w+)\(/
936 ],
937 idx: 1
938 },
939 {
940 type: TYPES.FILTEREMPTY,
941 regex: [
942 /^\|\s*(\w+)/
943 ],
944 idx: 1
945 },
946 {
947 type: TYPES.FUNCTIONEMPTY,
948 regex: [
949 /^\s*(\w+)\(\)/
950 ],
951 idx: 1
952 },
953 {
954 type: TYPES.FUNCTION,
955 regex: [
956 /^\s*(\w+)\(/
957 ],
958 idx: 1
959 },
960 {
961 type: TYPES.PARENOPEN,
962 regex: [
963 /^\(/
964 ]
965 },
966 {
967 type: TYPES.PARENCLOSE,
968 regex: [
969 /^\)/
970 ]
971 },
972 {
973 type: TYPES.COMMA,
974 regex: [
975 /^,/
976 ]
977 },
978 {
979 type: TYPES.LOGIC,
980 regex: [
981 /^(&&|\|\|)\s*/,
982 /^(and|or)\s+/
983 ],
984 idx: 1,
985 replace: {
986 'and': '&&',
987 'or': '||'
988 }
989 },
990 {
991 type: TYPES.COMPARATOR,
992 regex: [
993 /^(===|==|\!==|\!=|<=|<|>=|>|in\s|gte\s|gt\s|lte\s|lt\s)\s*/
994 ],
995 idx: 1,
996 replace: {
997 'gte': '>=',
998 'gt': '>',
999 'lte': '<=',
1000 'lt': '<'
1001 }
1002 },
1003 {
1004 type: TYPES.ASSIGNMENT,
1005 regex: [
1006 /^(=|\+=|-=|\*=|\/=)/
1007 ]
1008 },
1009 {
1010 type: TYPES.NOT,
1011 regex: [
1012 /^\!\s*/,
1013 /^not\s+/
1014 ],
1015 replace: {
1016 'not': '!'
1017 }
1018 },
1019 {
1020 type: TYPES.BOOL,
1021 regex: [
1022 /^(true|false)\s+/,
1023 /^(true|false)$/
1024 ],
1025 idx: 1
1026 },
1027 {
1028 type: TYPES.VAR,
1029 regex: [
1030 /^[a-zA-Z_$]\w*((\.\w*)+)?/,
1031 /^[a-zA-Z_$]\w*/
1032 ]
1033 },
1034 {
1035 type: TYPES.BRACKETOPEN,
1036 regex: [
1037 /^\[/
1038 ]
1039 },
1040 {
1041 type: TYPES.BRACKETCLOSE,
1042 regex: [
1043 /^\]/
1044 ]
1045 },
1046 {
1047 type: TYPES.CURLYOPEN,
1048 regex: [
1049 /^\{/
1050 ]
1051 },
1052 {
1053 type: TYPES.COLON,
1054 regex: [
1055 /^\:/
1056 ]
1057 },
1058 {
1059 type: TYPES.CURLYCLOSE,
1060 regex: [
1061 /^\}/
1062 ]
1063 },
1064 {
1065 type: TYPES.DOTKEY,
1066 regex: [
1067 /^\.(\w+)/,
1068 ],
1069 idx: 1
1070 },
1071 {
1072 type: TYPES.NUMBER,
1073 regex: [
1074 /^[+\-]?\d+(\.\d+)?/
1075 ]
1076 },
1077 {
1078 type: TYPES.OPERATOR,
1079 regex: [
1080 /^(\+|\-|\/|\*|%)/
1081 ]
1082 }
1083 ];
1084
1085exports.types = TYPES;
1086
1087/**
1088 * Return the token type object for a single chunk of a string.
1089 * @param {string} str String chunk.
1090 * @return {LexerToken} Defined type, potentially stripped or replaced with more suitable content.
1091 * @private
1092 */
1093function reader(str) {
1094 var matched;
1095
1096 utils.some(rules, function (rule) {
1097 return utils.some(rule.regex, function (regex) {
1098 var match = str.match(regex),
1099 normalized;
1100
1101 if (!match) {
1102 return;
1103 }
1104
1105 normalized = match[rule.idx || 0].replace(/\s*$/, '');
1106 normalized = (rule.hasOwnProperty('replace') && rule.replace.hasOwnProperty(normalized)) ? rule.replace[normalized] : normalized;
1107
1108 matched = {
1109 match: normalized,
1110 type: rule.type,
1111 length: match[0].length
1112 };
1113 return true;
1114 });
1115 });
1116
1117 if (!matched) {
1118 matched = {
1119 match: str,
1120 type: TYPES.UNKNOWN,
1121 length: str.length
1122 };
1123 }
1124
1125 return matched;
1126}
1127
1128/**
1129 * Read a string and break it into separate token types.
1130 * @param {string} str
1131 * @return {Array.LexerToken} Array of defined types, potentially stripped or replaced with more suitable content.
1132 * @private
1133 */
1134exports.read = function (str) {
1135 var offset = 0,
1136 tokens = [],
1137 substr,
1138 match;
1139 while (offset < str.length) {
1140 substr = str.substring(offset);
1141 match = reader(substr);
1142 offset += match.length;
1143 tokens.push(match);
1144 }
1145 return tokens;
1146};
1147
1148},{"./utils":26}],5:[function(require,module,exports){
1149(function(){var fs = require('fs'),
1150 path = require('path');
1151
1152/**
1153 * Loads templates from the file system.
1154 * @alias swig.loaders.fs
1155 * @example
1156 * swig.setDefaults({ loader: swig.loaders.fs() });
1157 * @example
1158 * // Load Templates from a specific directory (does not require using relative paths in your templates)
1159 * swig.setDefaults({ loader: swig.loaders.fs(__dirname + '/templates' )});
1160 * @param {string} [basepath=''] Path to the templates as string. Assigning this value allows you to use semi-absolute paths to templates instead of relative paths.
1161 * @param {string} [encoding='utf8'] Template encoding
1162 */
1163module.exports = function (basepath, encoding) {
1164 var ret = {};
1165
1166 encoding = encoding || 'utf8';
1167 basepath = (basepath) ? path.normalize(basepath) : null;
1168
1169 /**
1170 * Resolves <var>to</var> to an absolute path or unique identifier. This is used for building correct, normalized, and absolute paths to a given template.
1171 * @alias resolve
1172 * @param {string} to Non-absolute identifier or pathname to a file.
1173 * @param {string} [from] If given, should attempt to find the <var>to</var> path in relation to this given, known path.
1174 * @return {string}
1175 */
1176 ret.resolve = function (to, from) {
1177 if (basepath) {
1178 from = basepath;
1179 } else {
1180 from = (from) ? path.dirname(from) : '/';
1181 }
1182 return path.resolve(from, to);
1183 };
1184
1185 /**
1186 * Loads a single template. Given a unique <var>identifier</var> found by the <var>resolve</var> method this should return the given template.
1187 * @alias load
1188 * @param {string} identifier Unique identifier of a template (possibly an absolute path).
1189 * @param {function} [cb] Asynchronous callback function. If not provided, this method should run synchronously.
1190 * @return {string} Template source string.
1191 */
1192 ret.load = function (identifier, cb) {
1193 if (!fs || (cb && !fs.readFile) || !fs.readFileSync) {
1194 throw new Error('Unable to find file ' + identifier + ' because there is no filesystem to read from.');
1195 }
1196
1197 identifier = ret.resolve(identifier);
1198
1199 if (cb) {
1200 fs.readFile(identifier, encoding, cb);
1201 return;
1202 }
1203 return fs.readFileSync(identifier, encoding);
1204 };
1205
1206 return ret;
1207};
1208
1209})()
1210},{"fs":27,"path":28}],6:[function(require,module,exports){
1211/**
1212 * @namespace TemplateLoader
1213 * @description Swig is able to accept custom template loaders written by you, so that your templates can come from your favorite storage medium without needing to be part of the core library.
1214 * A template loader consists of two methods: <var>resolve</var> and <var>load</var>. Each method is used internally by Swig to find and load the source of the template before attempting to parse and compile it.
1215 * @example
1216 * // A theoretical memcached loader
1217 * var path = require('path'),
1218 * Memcached = require('memcached');
1219 * function memcachedLoader(locations, options) {
1220 * var memcached = new Memcached(locations, options);
1221 * return {
1222 * resolve: function (to, from) {
1223 * return path.resolve(from, to);
1224 * },
1225 * load: function (identifier, cb) {
1226 * memcached.get(identifier, function (err, data) {
1227 * // if (!data) { load from filesystem; }
1228 * cb(err, data);
1229 * });
1230 * }
1231 * };
1232 * };
1233 * // Tell swig about the loader:
1234 * swig.setDefaults({ loader: memcachedLoader(['192.168.0.2']) });
1235 */
1236
1237/**
1238 * @function
1239 * @name resolve
1240 * @memberof TemplateLoader
1241 * @description
1242 * Resolves <var>to</var> to an absolute path or unique identifier. This is used for building correct, normalized, and absolute paths to a given template.
1243 * @param {string} to Non-absolute identifier or pathname to a file.
1244 * @param {string} [from] If given, should attempt to find the <var>to</var> path in relation to this given, known path.
1245 * @return {string}
1246 */
1247
1248/**
1249 * @function
1250 * @name load
1251 * @memberof TemplateLoader
1252 * @description
1253 * Loads a single template. Given a unique <var>identifier</var> found by the <var>resolve</var> method this should return the given template.
1254 * @param {string} identifier Unique identifier of a template (possibly an absolute path).
1255 * @param {function} [cb] Asynchronous callback function. If not provided, this method should run synchronously.
1256 * @return {string} Template source string.
1257 */
1258
1259/**
1260 * @private
1261 */
1262exports.fs = require('./filesystem');
1263exports.memory = require('./memory');
1264
1265},{"./filesystem":5,"./memory":7}],7:[function(require,module,exports){
1266var path = require('path'),
1267 utils = require('../utils');
1268
1269/**
1270 * Loads templates from a provided object mapping.
1271 * @alias swig.loaders.memory
1272 * @example
1273 * var templates = {
1274 * "layout": "{% block content %}{% endblock %}",
1275 * "home.html": "{% extends 'layout.html' %}{% block content %}...{% endblock %}"
1276 * };
1277 * swig.setDefaults({ loader: swig.loaders.memory(templates) });
1278 *
1279 * @param {object} mapping Hash object with template paths as keys and template sources as values.
1280 * @param {string} [basepath] Path to the templates as string. Assigning this value allows you to use semi-absolute paths to templates instead of relative paths.
1281 */
1282module.exports = function (mapping, basepath) {
1283 var ret = {};
1284
1285 basepath = (basepath) ? path.normalize(basepath) : null;
1286
1287 /**
1288 * Resolves <var>to</var> to an absolute path or unique identifier. This is used for building correct, normalized, and absolute paths to a given template.
1289 * @alias resolve
1290 * @param {string} to Non-absolute identifier or pathname to a file.
1291 * @param {string} [from] If given, should attempt to find the <var>to</var> path in relation to this given, known path.
1292 * @return {string}
1293 */
1294 ret.resolve = function (to, from) {
1295 if (basepath) {
1296 from = basepath;
1297 } else {
1298 from = (from) ? path.dirname(from) : '/';
1299 }
1300 return path.resolve(from, to);
1301 };
1302
1303 /**
1304 * Loads a single template. Given a unique <var>identifier</var> found by the <var>resolve</var> method this should return the given template.
1305 * @alias load
1306 * @param {string} identifier Unique identifier of a template (possibly an absolute path).
1307 * @param {function} [cb] Asynchronous callback function. If not provided, this method should run synchronously.
1308 * @return {string} Template source string.
1309 */
1310 ret.load = function (pathname, cb) {
1311 var src, paths;
1312
1313 paths = [pathname, pathname.replace(/^(\/|\\)/, '')];
1314
1315 src = mapping[paths[0]] || mapping[paths[1]];
1316 if (!src) {
1317 utils.throwError('Unable to find template "' + pathname + '".');
1318 }
1319
1320 if (cb) {
1321 cb(null, src);
1322 return;
1323 }
1324 return src;
1325 };
1326
1327 return ret;
1328};
1329
1330},{"../utils":26,"path":28}],8:[function(require,module,exports){
1331(function(){var utils = require('./utils'),
1332 lexer = require('./lexer');
1333
1334var _t = lexer.types,
1335 _reserved = ['break', 'case', 'catch', 'continue', 'debugger', 'default', 'delete', 'do', 'else', 'finally', 'for', 'function', 'if', 'in', 'instanceof', 'new', 'return', 'switch', 'this', 'throw', 'try', 'typeof', 'var', 'void', 'while', 'with'];
1336
1337
1338/**
1339 * Filters are simply functions that perform transformations on their first input argument.
1340 * Filters are run at render time, so they may not directly modify the compiled template structure in any way.
1341 * All of Swig's built-in filters are written in this same way. For more examples, reference the `filters.js` file in Swig's source.
1342 *
1343 * To disable auto-escaping on a custom filter, simply add a property to the filter method `safe = true;` and the output from this will not be escaped, no matter what the global settings are for Swig.
1344 *
1345 * @typedef {function} Filter
1346 *
1347 * @example
1348 * // This filter will return 'bazbop' if the idx on the input is not 'foobar'
1349 * swig.setFilter('foobar', function (input, idx) {
1350 * return input[idx] === 'foobar' ? input[idx] : 'bazbop';
1351 * });
1352 * // myvar = ['foo', 'bar', 'baz', 'bop'];
1353 * // => {{ myvar|foobar(3) }}
1354 * // Since myvar[3] !== 'foobar', we render:
1355 * // => bazbop
1356 *
1357 * @example
1358 * // This filter will disable auto-escaping on its output:
1359 * function bazbop (input) { return input; }
1360 * bazbop.safe = true;
1361 * swig.setFilter('bazbop', bazbop);
1362 * // => {{ "<p>"|bazbop }}
1363 * // => <p>
1364 *
1365 * @param {*} input Input argument, automatically sent from Swig's built-in parser.
1366 * @param {...*} [args] All other arguments are defined by the Filter author.
1367 * @return {*}
1368 */
1369
1370/*!
1371 * Makes a string safe for a regular expression.
1372 * @param {string} str
1373 * @return {string}
1374 * @private
1375 */
1376function escapeRegExp(str) {
1377 return str.replace(/[\-\/\\\^$*+?.()|\[\]{}]/g, '\\$&');
1378}
1379
1380/**
1381 * Parse strings of variables and tags into tokens for future compilation.
1382 * @class
1383 * @param {array} tokens Pre-split tokens read by the Lexer.
1384 * @param {object} filters Keyed object of filters that may be applied to variables.
1385 * @param {boolean} autoescape Whether or not this should be autoescaped.
1386 * @param {number} line Beginning line number for the first token.
1387 * @param {string} [filename] Name of the file being parsed.
1388 * @private
1389 */
1390function TokenParser(tokens, filters, autoescape, line, filename) {
1391 this.out = [];
1392 this.state = [];
1393 this.filterApplyIdx = [];
1394 this._parsers = {};
1395 this.line = line;
1396 this.filename = filename;
1397 this.filters = filters;
1398 this.escape = autoescape;
1399
1400 this.parse = function () {
1401 var self = this;
1402
1403 if (self._parsers.start) {
1404 self._parsers.start.call(self);
1405 }
1406 utils.each(tokens, function (token, i) {
1407 var prevToken = tokens[i - 1];
1408 self.isLast = (i === tokens.length - 1);
1409 if (prevToken) {
1410 while (prevToken.type === _t.WHITESPACE) {
1411 i -= 1;
1412 prevToken = tokens[i - 1];
1413 }
1414 }
1415 self.prevToken = prevToken;
1416 self.parseToken(token);
1417 });
1418 if (self._parsers.end) {
1419 self._parsers.end.call(self);
1420 }
1421
1422 if (self.escape) {
1423 self.filterApplyIdx = [0];
1424 if (typeof self.escape === 'string') {
1425 self.parseToken({ type: _t.FILTER, match: 'e' });
1426 self.parseToken({ type: _t.COMMA, match: ',' });
1427 self.parseToken({ type: _t.STRING, match: String(autoescape) });
1428 self.parseToken({ type: _t.PARENCLOSE, match: ')'});
1429 } else {
1430 self.parseToken({ type: _t.FILTEREMPTY, match: 'e' });
1431 }
1432 }
1433
1434 return self.out;
1435 };
1436}
1437
1438TokenParser.prototype = {
1439 /**
1440 * Set a custom method to be called when a token type is found.
1441 *
1442 * @example
1443 * parser.on(types.STRING, function (token) {
1444 * this.out.push(token.match);
1445 * });
1446 * @example
1447 * parser.on('start', function () {
1448 * this.out.push('something at the beginning of your args')
1449 * });
1450 * parser.on('end', function () {
1451 * this.out.push('something at the end of your args');
1452 * });
1453 *
1454 * @param {number} type Token type ID. Found in the Lexer.
1455 * @param {Function} fn Callback function. Return true to continue executing the default parsing function.
1456 * @return {undefined}
1457 */
1458 on: function (type, fn) {
1459 this._parsers[type] = fn;
1460 },
1461
1462 /**
1463 * Parse a single token.
1464 * @param {{match: string, type: number, line: number}} token Lexer token object.
1465 * @return {undefined}
1466 * @private
1467 */
1468 parseToken: function (token) {
1469 var self = this,
1470 fn = self._parsers[token.type] || self._parsers['*'],
1471 match = token.match,
1472 prevToken = self.prevToken,
1473 prevTokenType = prevToken ? prevToken.type : null,
1474 lastState = (self.state.length) ? self.state[self.state.length - 1] : null,
1475 temp;
1476
1477 if (fn && typeof fn === 'function') {
1478 if (!fn.call(this, token)) {
1479 return;
1480 }
1481 }
1482
1483 if (lastState && prevToken &&
1484 lastState === _t.FILTER &&
1485 prevTokenType === _t.FILTER &&
1486 token.type !== _t.PARENCLOSE &&
1487 token.type !== _t.COMMA &&
1488 token.type !== _t.OPERATOR &&
1489 token.type !== _t.FILTER &&
1490 token.type !== _t.FILTEREMPTY) {
1491 self.out.push(', ');
1492 }
1493
1494 if (lastState && lastState === _t.METHODOPEN) {
1495 self.state.pop();
1496 if (token.type !== _t.PARENCLOSE) {
1497 self.out.push(', ');
1498 }
1499 }
1500
1501 switch (token.type) {
1502 case _t.WHITESPACE:
1503 break;
1504
1505 case _t.STRING:
1506 self.filterApplyIdx.push(self.out.length);
1507 self.out.push(match.replace(/\\/g, '\\\\'));
1508 break;
1509
1510 case _t.NUMBER:
1511 case _t.BOOL:
1512 self.filterApplyIdx.push(self.out.length);
1513 self.out.push(match);
1514 break;
1515
1516 case _t.FILTER:
1517 if (!self.filters.hasOwnProperty(match) || typeof self.filters[match] !== "function") {
1518 utils.throwError('Invalid filter "' + match + '"', self.line, self.filename);
1519 }
1520 self.escape = self.filters[match].safe ? false : self.escape;
1521 temp = self.filterApplyIdx.pop();
1522 self.out.splice(temp, 0, '_filters["' + match + '"](');
1523 self.state.push(token.type);
1524 self.filterApplyIdx.push(temp);
1525 break;
1526
1527 case _t.FILTEREMPTY:
1528 if (!self.filters.hasOwnProperty(match) || typeof self.filters[match] !== "function") {
1529 utils.throwError('Invalid filter "' + match + '"', self.line, self.filename);
1530 }
1531 self.escape = self.filters[match].safe ? false : self.escape;
1532 self.out.splice(self.filterApplyIdx[self.filterApplyIdx.length - 1], 0, '_filters["' + match + '"](');
1533 self.out.push(')');
1534 break;
1535
1536 case _t.FUNCTION:
1537 case _t.FUNCTIONEMPTY:
1538 self.out.push('((typeof _ctx.' + match + ' !== "undefined") ? _ctx.' + match +
1539 ' : ((typeof ' + match + ' !== "undefined") ? ' + match +
1540 ' : _fn))(');
1541 self.escape = false;
1542 if (token.type === _t.FUNCTIONEMPTY) {
1543 self.out[self.out.length - 1] = self.out[self.out.length - 1] + ')';
1544 } else {
1545 self.state.push(token.type);
1546 }
1547 self.filterApplyIdx.push(self.out.length - 1);
1548 break;
1549
1550 case _t.PARENOPEN:
1551 self.state.push(token.type);
1552 if (self.filterApplyIdx.length) {
1553 self.out.splice(self.filterApplyIdx[self.filterApplyIdx.length - 1], 0, '(');
1554 if (prevToken && prevTokenType === _t.VAR) {
1555 temp = prevToken.match.split('.').slice(0, -1);
1556 self.out.push(' || _fn).call(' + self.checkMatch(temp));
1557 self.state.push(_t.METHODOPEN);
1558 self.escape = false;
1559 } else {
1560 self.out.push(' || _fn)(');
1561 }
1562 self.filterApplyIdx.push(self.out.length - 3);
1563 } else {
1564 self.out.push('(');
1565 self.filterApplyIdx.push(self.out.length - 1);
1566 }
1567 break;
1568
1569 case _t.PARENCLOSE:
1570 temp = self.state.pop();
1571 if (temp !== _t.PARENOPEN && temp !== _t.FUNCTION && temp !== _t.FILTER) {
1572 utils.throwError('Mismatched nesting state', self.line, self.filename);
1573 }
1574 self.out.push(')');
1575 // Once off the previous entry
1576 self.filterApplyIdx.pop();
1577 // Once for the open paren
1578 self.filterApplyIdx.pop();
1579 break;
1580
1581 case _t.COMMA:
1582 if (lastState !== _t.FUNCTION &&
1583 lastState !== _t.FILTER &&
1584 lastState !== _t.ARRAYOPEN &&
1585 lastState !== _t.CURLYOPEN &&
1586 lastState !== _t.PARENOPEN &&
1587 lastState !== _t.COLON) {
1588 utils.throwError('Unexpected comma', self.line, self.filename);
1589 }
1590 if (lastState === _t.COLON) {
1591 self.state.pop();
1592 }
1593 self.out.push(', ');
1594 self.filterApplyIdx.pop();
1595 break;
1596
1597 case _t.LOGIC:
1598 case _t.COMPARATOR:
1599 if (!prevToken ||
1600 prevTokenType === _t.COMMA ||
1601 prevTokenType === token.type ||
1602 prevTokenType === _t.BRACKETOPEN ||
1603 prevTokenType === _t.CURLYOPEN ||
1604 prevTokenType === _t.PARENOPEN ||
1605 prevTokenType === _t.FUNCTION) {
1606 utils.throwError('Unexpected logic', self.line, self.filename);
1607 }
1608 self.out.push(token.match);
1609 break;
1610
1611 case _t.NOT:
1612 self.out.push(token.match);
1613 break;
1614
1615 case _t.VAR:
1616 self.parseVar(token, match, lastState);
1617 break;
1618
1619 case _t.BRACKETOPEN:
1620 if (!prevToken ||
1621 (prevTokenType !== _t.VAR &&
1622 prevTokenType !== _t.BRACKETCLOSE &&
1623 prevTokenType !== _t.PARENCLOSE)) {
1624 self.state.push(_t.ARRAYOPEN);
1625 self.filterApplyIdx.push(self.out.length);
1626 } else {
1627 self.state.push(token.type);
1628 }
1629 self.out.push('[');
1630 break;
1631
1632 case _t.BRACKETCLOSE:
1633 temp = self.state.pop();
1634 if (temp !== _t.BRACKETOPEN && temp !== _t.ARRAYOPEN) {
1635 utils.throwError('Unexpected closing square bracket', self.line, self.filename);
1636 }
1637 self.out.push(']');
1638 self.filterApplyIdx.pop();
1639 break;
1640
1641 case _t.CURLYOPEN:
1642 self.state.push(token.type);
1643 self.out.push('{');
1644 self.filterApplyIdx.push(self.out.length - 1);
1645 break;
1646
1647 case _t.COLON:
1648 if (lastState !== _t.CURLYOPEN) {
1649 utils.throwError('Unexpected colon', self.line, self.filename);
1650 }
1651 self.state.push(token.type);
1652 self.out.push(':');
1653 self.filterApplyIdx.pop();
1654 break;
1655
1656 case _t.CURLYCLOSE:
1657 if (lastState === _t.COLON) {
1658 self.state.pop();
1659 }
1660 if (self.state.pop() !== _t.CURLYOPEN) {
1661 utils.throwError('Unexpected closing curly brace', self.line, self.filename);
1662 }
1663 self.out.push('}');
1664
1665 self.filterApplyIdx.pop();
1666 break;
1667
1668 case _t.DOTKEY:
1669 if (!prevToken || (
1670 prevTokenType !== _t.VAR &&
1671 prevTokenType !== _t.BRACKETCLOSE &&
1672 prevTokenType !== _t.DOTKEY &&
1673 prevTokenType !== _t.PARENCLOSE &&
1674 prevTokenType !== _t.FUNCTIONEMPTY &&
1675 prevTokenType !== _t.FILTEREMPTY &&
1676 prevTokenType !== _t.CURLYCLOSE
1677 )) {
1678 utils.throwError('Unexpected key "' + match + '"', self.line, self.filename);
1679 }
1680 self.out.push('.' + match);
1681 break;
1682
1683 case _t.OPERATOR:
1684 self.out.push(' ' + match + ' ');
1685 self.filterApplyIdx.pop();
1686 break;
1687 }
1688 },
1689
1690 /**
1691 * Parse variable token
1692 * @param {{match: string, type: number, line: number}} token Lexer token object.
1693 * @param {string} match Shortcut for token.match
1694 * @param {number} lastState Lexer token type state.
1695 * @return {undefined}
1696 * @private
1697 */
1698 parseVar: function (token, match, lastState) {
1699 var self = this;
1700
1701 match = match.split('.');
1702
1703 if (_reserved.indexOf(match[0]) !== -1) {
1704 utils.throwError('Reserved keyword "' + match[0] + '" attempted to be used as a variable', self.line, self.filename);
1705 }
1706
1707 self.filterApplyIdx.push(self.out.length);
1708 if (lastState === _t.CURLYOPEN) {
1709 if (match.length > 1) {
1710 utils.throwError('Unexpected dot', self.line, self.filename);
1711 }
1712 self.out.push(match[0]);
1713 return;
1714 }
1715
1716 self.out.push(self.checkMatch(match));
1717 },
1718
1719 /**
1720 * Return contextual dot-check string for a match
1721 * @param {string} match Shortcut for token.match
1722 * @private
1723 */
1724 checkMatch: function (match) {
1725 var temp = match[0];
1726
1727 function checkDot(ctx) {
1728 var c = ctx + temp,
1729 m = match,
1730 build = '';
1731
1732 build = '(typeof ' + c + ' !== "undefined"';
1733 utils.each(m, function (v, i) {
1734 if (i === 0) {
1735 return;
1736 }
1737 build += ' && ' + c + '.' + v + ' !== undefined';
1738 c += '.' + v;
1739 });
1740 build += ')';
1741
1742 return build;
1743 }
1744
1745 function buildDot(ctx) {
1746 return '(' + checkDot(ctx) + ' ? ' + ctx + match.join('.') + ' : "")';
1747 }
1748
1749 return '(' + checkDot('_ctx.') + ' ? ' + buildDot('_ctx.') + ' : ' + buildDot('') + ')';
1750 }
1751};
1752
1753/**
1754 * Parse a source string into tokens that are ready for compilation.
1755 *
1756 * @example
1757 * exports.parse('{{ tacos }}', {}, tags, filters);
1758 * // => [{ compile: [Function], ... }]
1759 *
1760 * @param {string} source Swig template source.
1761 * @param {object} opts Swig options object.
1762 * @param {object} tags Keyed object of tags that can be parsed and compiled.
1763 * @param {object} filters Keyed object of filters that may be applied to variables.
1764 * @return {array} List of tokens ready for compilation.
1765 */
1766exports.parse = function (source, opts, tags, filters) {
1767 source = source.replace(/\r\n/g, '\n');
1768 var escape = opts.autoescape,
1769 tagOpen = opts.tagControls[0],
1770 tagClose = opts.tagControls[1],
1771 varOpen = opts.varControls[0],
1772 varClose = opts.varControls[1],
1773 escapedTagOpen = escapeRegExp(tagOpen),
1774 escapedTagClose = escapeRegExp(tagClose),
1775 escapedVarOpen = escapeRegExp(varOpen),
1776 escapedVarClose = escapeRegExp(varClose),
1777 tagStrip = new RegExp('^' + escapedTagOpen + '-?\\s*-?|-?\\s*-?' + escapedTagClose + '$', 'g'),
1778 tagStripBefore = new RegExp('^' + escapedTagOpen + '-'),
1779 tagStripAfter = new RegExp('-' + escapedTagClose + '$'),
1780 varStrip = new RegExp('^' + escapedVarOpen + '-?\\s*-?|-?\\s*-?' + escapedVarClose + '$', 'g'),
1781 varStripBefore = new RegExp('^' + escapedVarOpen + '-'),
1782 varStripAfter = new RegExp('-' + escapedVarClose + '$'),
1783 cmtOpen = opts.cmtControls[0],
1784 cmtClose = opts.cmtControls[1],
1785 anyChar = '[\\s\\S]*?',
1786 // Split the template source based on variable, tag, and comment blocks
1787 // /(\{%[\s\S]*?%\}|\{\{[\s\S]*?\}\}|\{#[\s\S]*?#\})/
1788 splitter = new RegExp(
1789 '(' +
1790 escapedTagOpen + anyChar + escapedTagClose + '|' +
1791 escapedVarOpen + anyChar + escapedVarClose + '|' +
1792 escapeRegExp(cmtOpen) + anyChar + escapeRegExp(cmtClose) +
1793 ')'
1794 ),
1795 line = 1,
1796 stack = [],
1797 parent = null,
1798 tokens = [],
1799 blocks = {},
1800 inRaw = false,
1801 stripNext;
1802
1803 /**
1804 * Parse a variable.
1805 * @param {string} str String contents of the variable, between <i>{{</i> and <i>}}</i>
1806 * @param {number} line The line number that this variable starts on.
1807 * @return {VarToken} Parsed variable token object.
1808 * @private
1809 */
1810 function parseVariable(str, line) {
1811 var tokens = lexer.read(utils.strip(str)),
1812 parser,
1813 out;
1814
1815 parser = new TokenParser(tokens, filters, escape, line, opts.filename);
1816 out = parser.parse().join('');
1817
1818 if (parser.state.length) {
1819 utils.throwError('Unable to parse "' + str + '"', line, opts.filename);
1820 }
1821
1822 /**
1823 * A parsed variable token.
1824 * @typedef {object} VarToken
1825 * @property {function} compile Method for compiling this token.
1826 */
1827 return {
1828 compile: function () {
1829 return '_output += ' + out + ';\n';
1830 }
1831 };
1832 }
1833 exports.parseVariable = parseVariable;
1834
1835 /**
1836 * Parse a tag.
1837 * @param {string} str String contents of the tag, between <i>{%</i> and <i>%}</i>
1838 * @param {number} line The line number that this tag starts on.
1839 * @return {TagToken} Parsed token object.
1840 * @private
1841 */
1842 function parseTag(str, line) {
1843 var tokens, parser, chunks, tagName, tag, args, last;
1844
1845 if (utils.startsWith(str, 'end')) {
1846 last = stack[stack.length - 1];
1847 if (last && last.name === str.split(/\s+/)[0].replace(/^end/, '') && last.ends) {
1848 switch (last.name) {
1849 case 'autoescape':
1850 escape = opts.autoescape;
1851 break;
1852 case 'raw':
1853 inRaw = false;
1854 break;
1855 }
1856 stack.pop();
1857 return;
1858 }
1859
1860 if (!inRaw) {
1861 utils.throwError('Unexpected end of tag "' + str.replace(/^end/, '') + '"', line, opts.filename);
1862 }
1863 }
1864
1865 if (inRaw) {
1866 return;
1867 }
1868
1869 chunks = str.split(/\s+(.+)?/);
1870 tagName = chunks.shift();
1871
1872 if (!tags.hasOwnProperty(tagName)) {
1873 utils.throwError('Unexpected tag "' + str + '"', line, opts.filename);
1874 }
1875
1876 tokens = lexer.read(utils.strip(chunks.join(' ')));
1877 parser = new TokenParser(tokens, filters, false, line, opts.filename);
1878 tag = tags[tagName];
1879
1880 /**
1881 * Define custom parsing methods for your tag.
1882 * @callback parse
1883 *
1884 * @example
1885 * exports.parse = function (str, line, parser, types, options) {
1886 * parser.on('start', function () {
1887 * // ...
1888 * });
1889 * parser.on(types.STRING, function (token) {
1890 * // ...
1891 * });
1892 * };
1893 *
1894 * @param {string} str The full token string of the tag.
1895 * @param {number} line The line number that this tag appears on.
1896 * @param {TokenParser} parser A TokenParser instance.
1897 * @param {TYPES} types Lexer token type enum.
1898 * @param {TagToken[]} stack The current stack of open tags.
1899 * @param {SwigOpts} options Swig Options Object.
1900 */
1901 if (!tag.parse(chunks[1], line, parser, _t, stack, opts)) {
1902 utils.throwError('Unexpected tag "' + tagName + '"', line, opts.filename);
1903 }
1904
1905 parser.parse();
1906 args = parser.out;
1907
1908 switch (tagName) {
1909 case 'autoescape':
1910 escape = (args[0] !== 'false') ? args[0] : false;
1911 break;
1912 case 'raw':
1913 inRaw = true;
1914 break;
1915 }
1916
1917 /**
1918 * A parsed tag token.
1919 * @typedef {Object} TagToken
1920 * @property {compile} [compile] Method for compiling this token.
1921 * @property {array} [args] Array of arguments for the tag.
1922 * @property {Token[]} [content=[]] An array of tokens that are children of this Token.
1923 * @property {boolean} [ends] Whether or not this tag requires an end tag.
1924 * @property {string} name The name of this tag.
1925 */
1926 return {
1927 block: !!tags[tagName].block,
1928 compile: tag.compile,
1929 args: args,
1930 content: [],
1931 ends: tag.ends,
1932 name: tagName
1933 };
1934 }
1935
1936 /**
1937 * Strip the whitespace from the previous token, if it is a string.
1938 * @param {object} token Parsed token.
1939 * @return {object} If the token was a string, trailing whitespace will be stripped.
1940 */
1941 function stripPrevToken(token) {
1942 if (typeof token === 'string') {
1943 token = token.replace(/\s*$/, '');
1944 }
1945 return token;
1946 }
1947
1948 /*!
1949 * Loop over the source, split via the tag/var/comment regular expression splitter.
1950 * Send each chunk to the appropriate parser.
1951 */
1952 utils.each(source.split(splitter), function (chunk) {
1953 var token, lines, stripPrev, prevToken, prevChildToken;
1954
1955 if (!chunk) {
1956 return;
1957 }
1958
1959 // Is a variable?
1960 if (!inRaw && utils.startsWith(chunk, varOpen) && utils.endsWith(chunk, varClose)) {
1961 stripPrev = varStripBefore.test(chunk);
1962 stripNext = varStripAfter.test(chunk);
1963 token = parseVariable(chunk.replace(varStrip, ''), line);
1964 // Is a tag?
1965 } else if (utils.startsWith(chunk, tagOpen) && utils.endsWith(chunk, tagClose)) {
1966 stripPrev = tagStripBefore.test(chunk);
1967 stripNext = tagStripAfter.test(chunk);
1968 token = parseTag(chunk.replace(tagStrip, ''), line);
1969 if (token) {
1970 if (token.name === 'extends') {
1971 parent = token.args.join('').replace(/^\'|\'$/g, '').replace(/^\"|\"$/g, '');
1972 }
1973
1974 if (token.block && (!stack.length || token.name === 'block')) {
1975 blocks[token.args.join('')] = token;
1976 }
1977 }
1978 if (inRaw && !token) {
1979 token = chunk;
1980 }
1981 // Is a content string?
1982 } else if (inRaw || (!utils.startsWith(chunk, cmtOpen) && !utils.endsWith(chunk, cmtClose))) {
1983 token = (stripNext) ? chunk.replace(/^\s*/, '') : chunk;
1984 stripNext = false;
1985 } else if (utils.startsWith(chunk, cmtOpen) && utils.endsWith(chunk, cmtClose)) {
1986 return;
1987 }
1988
1989 // Did this tag ask to strip previous whitespace? <code>{%- ... %}</code> or <code>{{- ... }}</code>
1990 if (stripPrev && tokens.length) {
1991 prevToken = tokens.pop();
1992 if (typeof prevToken === 'string') {
1993 prevToken = stripPrevToken(prevToken);
1994 } else if (prevToken.content && prevToken.content.length) {
1995 prevChildToken = stripPrevToken(prevToken.content.pop());
1996 prevToken.content.push(prevChildToken);
1997 }
1998 tokens.push(prevToken);
1999 }
2000
2001 // This was a comment, so let's just keep going.
2002 if (!token) {
2003 return;
2004 }
2005
2006 // If there's an open item in the stack, add this to its content.
2007 if (stack.length) {
2008 stack[stack.length - 1].content.push(token);
2009 } else {
2010 tokens.push(token);
2011 }
2012
2013 // If the token is a tag that requires an end tag, open it on the stack.
2014 if (token.name && token.ends) {
2015 stack.push(token);
2016 }
2017
2018 lines = chunk.match(/\n/g);
2019 line += (lines) ? lines.length : 0;
2020 });
2021
2022 return {
2023 name: opts.filename,
2024 parent: parent,
2025 tokens: tokens,
2026 blocks: blocks
2027 };
2028};
2029
2030
2031/**
2032 * Compile an array of tokens.
2033 * @param {Token[]} template An array of template tokens.
2034 * @param {Templates[]} parents Array of parent templates.
2035 * @param {SwigOpts} [options] Swig options object.
2036 * @param {string} [blockName] Name of the current block context.
2037 * @return {string} Partial for a compiled JavaScript method that will output a rendered template.
2038 */
2039exports.compile = function (template, parents, options, blockName) {
2040 var out = '',
2041 tokens = utils.isArray(template) ? template : template.tokens;
2042
2043 utils.each(tokens, function (token) {
2044 var o;
2045 if (typeof token === 'string') {
2046 out += '_output += "' + token.replace(/\\/g, '\\\\').replace(/\n|\r/g, '\\n').replace(/"/g, '\\"') + '";\n';
2047 return;
2048 }
2049
2050 /**
2051 * Compile callback for VarToken and TagToken objects.
2052 * @callback compile
2053 *
2054 * @example
2055 * exports.compile = function (compiler, args, content, parents, options, blockName) {
2056 * if (args[0] === 'foo') {
2057 * return compiler(content, parents, options, blockName) + '\n';
2058 * }
2059 * return '_output += "fallback";\n';
2060 * };
2061 *
2062 * @param {parserCompiler} compiler
2063 * @param {array} [args] Array of parsed arguments on the for the token.
2064 * @param {array} [content] Array of content within the token.
2065 * @param {array} [parents] Array of parent templates for the current template context.
2066 * @param {SwigOpts} [options] Swig Options Object
2067 * @param {string} [blockName] Name of the direct block parent, if any.
2068 */
2069 o = token.compile(exports.compile, token.args ? token.args.slice(0) : [], token.content ? token.content.slice(0) : [], parents, options, blockName);
2070 out += o || '';
2071 });
2072
2073 return out;
2074};
2075
2076})()
2077},{"./lexer":4,"./utils":26}],9:[function(require,module,exports){
2078(function(){var utils = require('./utils'),
2079 _tags = require('./tags'),
2080 _filters = require('./filters'),
2081 parser = require('./parser'),
2082 dateformatter = require('./dateformatter'),
2083 loaders = require('./loaders');
2084
2085/**
2086 * Swig version number as a string.
2087 * @example
2088 * if (swig.version === "1.3.0") { ... }
2089 *
2090 * @type {String}
2091 */
2092exports.version = "1.3.0";
2093
2094/**
2095 * Swig Options Object. This object can be passed to many of the API-level Swig methods to control various aspects of the engine. All keys are optional.
2096 * @typedef {Object} SwigOpts
2097 * @property {boolean} autoescape Controls whether or not variable output will automatically be escaped for safe HTML output. Defaults to <code data-language="js">true</code>. Functions executed in variable statements will not be auto-escaped. Your application/functions should take care of their own auto-escaping.
2098 * @property {array} varControls Open and close controls for variables. Defaults to <code data-language="js">['{{', '}}']</code>.
2099 * @property {array} tagControls Open and close controls for tags. Defaults to <code data-language="js">['{%', '%}']</code>.
2100 * @property {array} cmtControls Open and close controls for comments. Defaults to <code data-language="js">['{#', '#}']</code>.
2101 * @property {object} locals Default variable context to be passed to <strong>all</strong> templates.
2102 * @property {CacheOptions} cache Cache control for templates. Defaults to saving in <code data-language="js">'memory'</code>. Send <code data-language="js">false</code> to disable. Send an object with <code data-language="js">get</code> and <code data-language="js">set</code> functions to customize.
2103 * @property {TemplateLoader} loader The method that Swig will use to load templates. Defaults to <var>swig.loaders.fs</var>.
2104 */
2105var defaultOptions = {
2106 autoescape: true,
2107 varControls: ['{{', '}}'],
2108 tagControls: ['{%', '%}'],
2109 cmtControls: ['{#', '#}'],
2110 locals: {},
2111 /**
2112 * Cache control for templates. Defaults to saving all templates into memory.
2113 * @typedef {boolean|string|object} CacheOptions
2114 * @example
2115 * // Default
2116 * swig.setDefaults({ cache: 'memory' });
2117 * @example
2118 * // Disables caching in Swig.
2119 * swig.setDefaults({ cache: false });
2120 * @example
2121 * // Custom cache storage and retrieval
2122 * swig.setDefaults({
2123 * cache: {
2124 * get: function (key) { ... },
2125 * set: function (key, val) { ... }
2126 * }
2127 * });
2128 */
2129 cache: 'memory',
2130 /**
2131 * Configure Swig to use either the <var>swig.loaders.fs</var> or <var>swig.loaders.memory</var> template loader. Or, you can write your own!
2132 * For more information, please see the <a href="../loaders/">Template Loaders documentation</a>.
2133 * @typedef {class} TemplateLoader
2134 * @example
2135 * // Default, FileSystem loader
2136 * swig.setDefaults({ loader: swig.loaders.fs() });
2137 * @example
2138 * // FileSystem loader allowing a base path
2139 * // With this, you don't use relative URLs in your template references
2140 * swig.setDefaults({ loader: swig.loaders.fs(__dirname + '/templates') });
2141 * @example
2142 * // Memory Loader
2143 * swig.setDefaults({ loader: swig.loaders.memory({
2144 * layout: '{% block foo %}{% endblock %}',
2145 * page1: '{% extends "layout" %}{% block foo %}Tacos!{% endblock %}'
2146 * })});
2147 */
2148 loader: loaders.fs()
2149 },
2150 defaultInstance;
2151
2152/**
2153 * Empty function, used in templates.
2154 * @return {string} Empty string
2155 * @private
2156 */
2157function efn() { return ''; }
2158
2159/**
2160 * Validate the Swig options object.
2161 * @param {?SwigOpts} options Swig options object.
2162 * @return {undefined} This method will throw errors if anything is wrong.
2163 * @private
2164 */
2165function validateOptions(options) {
2166 if (!options) {
2167 return;
2168 }
2169
2170 utils.each(['varControls', 'tagControls', 'cmtControls'], function (key) {
2171 if (!options.hasOwnProperty(key)) {
2172 return;
2173 }
2174 if (!utils.isArray(options[key]) || options[key].length !== 2) {
2175 throw new Error('Option "' + key + '" must be an array containing 2 different control strings.');
2176 }
2177 if (options[key][0] === options[key][1]) {
2178 throw new Error('Option "' + key + '" open and close controls must not be the same.');
2179 }
2180 utils.each(options[key], function (a, i) {
2181 if (a.length < 2) {
2182 throw new Error('Option "' + key + '" ' + ((i) ? 'open ' : 'close ') + 'control must be at least 2 characters. Saw "' + a + '" instead.');
2183 }
2184 });
2185 });
2186
2187 if (options.hasOwnProperty('cache')) {
2188 if (options.cache && options.cache !== 'memory') {
2189 if (!options.cache.get || !options.cache.set) {
2190 throw new Error('Invalid cache option ' + JSON.stringify(options.cache) + ' found. Expected "memory" or { get: function (key) { ... }, set: function (key, value) { ... } }.');
2191 }
2192 }
2193 }
2194 if (options.hasOwnProperty('loader')) {
2195 if (options.loader) {
2196 if (!options.loader.load || !options.loader.resolve) {
2197 throw new Error('Invalid loader option ' + JSON.stringify(options.loader) + ' found. Expected { load: function (pathname, cb) { ... }, resolve: function (to, from) { ... } }.');
2198 }
2199 }
2200 }
2201
2202}
2203
2204/**
2205 * Set defaults for the base and all new Swig environments.
2206 *
2207 * @example
2208 * swig.setDefaults({ cache: false });
2209 * // => Disables Cache
2210 *
2211 * @example
2212 * swig.setDefaults({ locals: { now: function () { return new Date(); } }});
2213 * // => sets a globally accessible method for all template
2214 * // contexts, allowing you to print the current date
2215 * // => {{ now()|date('F jS, Y') }}
2216 *
2217 * @param {SwigOpts} [options={}] Swig options object.
2218 * @return {undefined}
2219 */
2220exports.setDefaults = function (options) {
2221 validateOptions(options);
2222
2223 var locals = utils.extend({}, defaultOptions.locals, options.locals || {});
2224
2225 utils.extend(defaultOptions, options);
2226 defaultOptions.locals = locals;
2227
2228 defaultInstance.options = utils.extend(defaultInstance.options, options);
2229};
2230
2231/**
2232 * Set the default TimeZone offset for date formatting via the date filter. This is a global setting and will affect all Swig environments, old or new.
2233 * @param {number} offset Offset from GMT, in minutes.
2234 * @return {undefined}
2235 */
2236exports.setDefaultTZOffset = function (offset) {
2237 dateformatter.tzOffset = offset;
2238};
2239
2240/**
2241 * Create a new, separate Swig compile/render environment.
2242 *
2243 * @example
2244 * var swig = require('swig');
2245 * var myswig = new swig.Swig({varControls: ['<%=', '%>']});
2246 * myswig.render('Tacos are <%= tacos =>!', { locals: { tacos: 'delicious' }});
2247 * // => Tacos are delicious!
2248 * swig.render('Tacos are <%= tacos =>!', { locals: { tacos: 'delicious' }});
2249 * // => 'Tacos are <%= tacos =>!'
2250 *
2251 * @param {SwigOpts} [opts={}] Swig options object.
2252 * @return {object} New Swig environment.
2253 */
2254exports.Swig = function (opts) {
2255 validateOptions(opts);
2256 this.options = utils.extend({}, defaultOptions, opts || {});
2257 this.cache = {};
2258 this.extensions = {};
2259 var self = this,
2260 tags = _tags,
2261 filters = _filters;
2262
2263 /**
2264 * Get combined locals context.
2265 * @param {?SwigOpts} [options] Swig options object.
2266 * @return {object} Locals context.
2267 * @private
2268 */
2269 function getLocals(options) {
2270 if (!options || !options.locals) {
2271 return self.options.locals;
2272 }
2273
2274 return utils.extend({}, self.options.locals, options.locals);
2275 }
2276
2277 /**
2278 * Get compiled template from the cache.
2279 * @param {string} key Name of template.
2280 * @return {object|undefined} Template function and tokens.
2281 * @private
2282 */
2283 function cacheGet(key) {
2284 if (!self.options.cache) {
2285 return;
2286 }
2287
2288 if (self.options.cache === 'memory') {
2289 return self.cache[key];
2290 }
2291
2292 return self.options.cache.get(key);
2293 }
2294
2295 /**
2296 * Store a template in the cache.
2297 * @param {string} key Name of template.
2298 * @param {object} val Template function and tokens.
2299 * @return {undefined}
2300 * @private
2301 */
2302 function cacheSet(key, val) {
2303 if (!self.options.cache) {
2304 return;
2305 }
2306
2307 if (self.options.cache === 'memory') {
2308 self.cache[key] = val;
2309 return;
2310 }
2311
2312 self.options.cache.set(key, val);
2313 }
2314
2315 /**
2316 * Clears the in-memory template cache.
2317 *
2318 * @example
2319 * swig.invalidateCache();
2320 *
2321 * @return {undefined}
2322 */
2323 this.invalidateCache = function () {
2324 if (self.options.cache === 'memory') {
2325 self.cache = {};
2326 }
2327 };
2328
2329 /**
2330 * Add a custom filter for swig variables.
2331 *
2332 * @example
2333 * function replaceMs(input) { return input.replace(/m/g, 'f'); }
2334 * swig.setFilter('replaceMs', replaceMs);
2335 * // => {{ "onomatopoeia"|replaceMs }}
2336 * // => onofatopeia
2337 *
2338 * @param {string} name Name of filter, used in templates. <strong>Will</strong> overwrite previously defined filters, if using the same name.
2339 * @param {function} method Function that acts against the input. See <a href="/docs/filters/#custom">Custom Filters</a> for more information.
2340 * @return {undefined}
2341 */
2342 this.setFilter = function (name, method) {
2343 if (typeof method !== "function") {
2344 throw new Error('Filter "' + name + '" is not a valid function.');
2345 }
2346 filters[name] = method;
2347 };
2348
2349 /**
2350 * Add a custom tag. To expose your own extensions to compiled template code, see <code data-language="js">swig.setExtension</code>.
2351 *
2352 * For a more in-depth explanation of writing custom tags, see <a href="../extending/#tags">Custom Tags</a>.
2353 *
2354 * @example
2355 * var tacotag = require('./tacotag');
2356 * swig.setTag('tacos', tacotag.parse, tacotag.compile, tacotag.ends, tacotag.blockLevel);
2357 * // => {% tacos %}Make this be tacos.{% endtacos %}
2358 * // => Tacos tacos tacos tacos.
2359 *
2360 * @param {string} name Tag name.
2361 * @param {function} parse Method for parsing tokens.
2362 * @param {function} compile Method for compiling renderable output.
2363 * @param {boolean} [ends=false] Whether or not this tag requires an <i>end</i> tag.
2364 * @param {boolean} [blockLevel=false] If false, this tag will not be compiled outside of <code>block</code> tags when extending a parent template.
2365 * @return {undefined}
2366 */
2367 this.setTag = function (name, parse, compile, ends, blockLevel) {
2368 if (typeof parse !== 'function') {
2369 throw new Error('Tag "' + name + '" parse method is not a valid function.');
2370 }
2371
2372 if (typeof compile !== 'function') {
2373 throw new Error('Tag "' + name + '" compile method is not a valid function.');
2374 }
2375
2376 tags[name] = {
2377 parse: parse,
2378 compile: compile,
2379 ends: ends || false,
2380 block: !!blockLevel
2381 };
2382 };
2383
2384 /**
2385 * Add extensions for custom tags. This allows any custom tag to access a globally available methods via a special globally available object, <var>_ext</var>, in templates.
2386 *
2387 * @example
2388 * swig.setExtension('trans', function (v) { return translate(v); });
2389 * function compileTrans(compiler, args, content, parent, options) {
2390 * return '_output += _ext.trans(' + args[0] + ');'
2391 * };
2392 * swig.setTag('trans', parseTrans, compileTrans, true);
2393 *
2394 * @param {string} name Key name of the extension. Accessed via <code data-language="js">_ext[name]</code>.
2395 * @param {*} object The method, value, or object that should be available via the given name.
2396 * @return {undefined}
2397 */
2398 this.setExtension = function (name, object) {
2399 self.extensions[name] = object;
2400 };
2401
2402 /**
2403 * Parse a given source string into tokens.
2404 *
2405 * @param {string} source Swig template source.
2406 * @param {SwigOpts} [options={}] Swig options object.
2407 * @return {object} parsed Template tokens object.
2408 * @private
2409 */
2410 this.parse = function (source, options) {
2411 validateOptions(options);
2412
2413 var locals = getLocals(options),
2414 opts = {},
2415 k;
2416
2417 for (k in options) {
2418 if (options.hasOwnProperty(k) && k !== 'locals') {
2419 opts[k] = options[k];
2420 }
2421 }
2422
2423 options = utils.extend({}, self.options, opts);
2424 options.locals = locals;
2425
2426 return parser.parse(source, options, tags, filters);
2427 };
2428
2429 /**
2430 * Parse a given file into tokens.
2431 *
2432 * @param {string} pathname Full path to file to parse.
2433 * @param {SwigOpts} [options={}] Swig options object.
2434 * @return {object} parsed Template tokens object.
2435 * @private
2436 */
2437 this.parseFile = function (pathname, options) {
2438 var src;
2439
2440 if (!options) {
2441 options = {};
2442 }
2443
2444 pathname = self.options.loader.resolve(pathname, options.resolveFrom);
2445
2446 src = self.options.loader.load(pathname);
2447
2448 if (!options.filename) {
2449 options = utils.extend({ filename: pathname }, options);
2450 }
2451
2452 return self.parse(src, options);
2453 };
2454
2455 /**
2456 * Re-Map blocks within a list of tokens to the template's block objects.
2457 * @param {array} tokens List of tokens for the parent object.
2458 * @param {object} template Current template that needs to be mapped to the parent's block and token list.
2459 * @return {array}
2460 * @private
2461 */
2462 function remapBlocks(blocks, tokens) {
2463 return utils.map(tokens, function (token) {
2464 var args = token.args ? token.args.join('') : '';
2465 if (token.name === 'block' && blocks[args]) {
2466 token = blocks[args];
2467 }
2468 if (token.content && token.content.length) {
2469 token.content = remapBlocks(blocks, token.content);
2470 }
2471 return token;
2472 });
2473 }
2474
2475 /**
2476 * Import block-level tags to the token list that are not actual block tags.
2477 * @param {array} blocks List of block-level tags.
2478 * @param {array} tokens List of tokens to render.
2479 * @return {undefined}
2480 * @private
2481 */
2482 function importNonBlocks(blocks, tokens) {
2483 utils.each(blocks, function (block) {
2484 if (block.name !== 'block') {
2485 tokens.unshift(block);
2486 }
2487 });
2488 }
2489
2490 /**
2491 * Recursively compile and get parents of given parsed token object.
2492 *
2493 * @param {object} tokens Parsed tokens from template.
2494 * @param {SwigOpts} [options={}] Swig options object.
2495 * @return {object} Parsed tokens from parent templates.
2496 * @private
2497 */
2498 function getParents(tokens, options) {
2499 var parentName = tokens.parent,
2500 parentFiles = [],
2501 parents = [],
2502 parentFile,
2503 parent,
2504 l;
2505
2506 while (parentName) {
2507 if (!options || !options.filename) {
2508 throw new Error('Cannot extend "' + parentName + '" because current template has no filename.');
2509 }
2510
2511 parentFile = parentFile || options.filename;
2512 parentFile = self.options.loader.resolve(parentName, parentFile);
2513 parent = cacheGet(parentFile) || self.parseFile(parentFile, utils.extend({}, options, { filename: parentFile }));
2514 parentName = parent.parent;
2515
2516 if (parentFiles.indexOf(parentFile) !== -1) {
2517 throw new Error('Illegal circular extends of "' + parentFile + '".');
2518 }
2519 parentFiles.push(parentFile);
2520
2521 parents.push(parent);
2522 }
2523
2524 // Remap each parents'(1) blocks onto its own parent(2), receiving the full token list for rendering the original parent(1) on its own.
2525 l = parents.length;
2526 for (l = parents.length - 2; l >= 0; l -= 1) {
2527 parents[l].tokens = remapBlocks(parents[l].blocks, parents[l + 1].tokens);
2528 importNonBlocks(parents[l].blocks, parents[l].tokens);
2529 }
2530
2531 return parents;
2532 }
2533
2534 /**
2535 * Pre-compile a source string into a cache-able template function.
2536 *
2537 * @example
2538 * swig.precompile('{{ tacos }}');
2539 * // => {
2540 * // tpl: function (_swig, _locals, _filters, _utils, _fn) { ... },
2541 * // tokens: {
2542 * // name: undefined,
2543 * // parent: null,
2544 * // tokens: [...],
2545 * // blocks: {}
2546 * // }
2547 * // }
2548 *
2549 * In order to render a pre-compiled template, you must have access to filters and utils from Swig. <var>efn</var> is simply an empty function that does nothing.
2550 *
2551 * @param {string} source Swig template source string.
2552 * @param {SwigOpts} [options={}] Swig options object.
2553 * @return {object} Renderable function and tokens object.
2554 */
2555 this.precompile = function (source, options) {
2556 var tokens = self.parse(source, options),
2557 parents = getParents(tokens, options),
2558 tpl;
2559
2560 if (parents.length) {
2561 // Remap the templates first-parent's tokens using this template's blocks.
2562 tokens.tokens = remapBlocks(tokens.blocks, parents[0].tokens);
2563 importNonBlocks(tokens.blocks, tokens.tokens);
2564 }
2565
2566 tpl = new Function('_swig', '_ctx', '_filters', '_utils', '_fn',
2567 ' var _ext = _swig.extensions,\n' +
2568 ' _output = "";\n' +
2569 parser.compile(tokens, parents, options) + '\n' +
2570 ' return _output;\n'
2571 );
2572
2573 return { tpl: tpl, tokens: tokens };
2574 };
2575
2576 /**
2577 * Compile and render a template string for final output.
2578 *
2579 * When rendering a source string, a file path should be specified in the options object in order for <var>extends</var>, <var>include</var>, and <var>import</var> to work properly. Do this by adding <code data-language="js">{ filename: '/absolute/path/to/mytpl.html' }</code> to the options argument.
2580 *
2581 * @example
2582 * swig.render('{{ tacos }}', { locals: { tacos: 'Tacos!!!!' }});
2583 * // => Tacos!!!!
2584 *
2585 * @param {string} source Swig template source string.
2586 * @param {SwigOpts} [options={}] Swig options object.
2587 * @return {string} Rendered output.
2588 */
2589 this.render = function (source, options) {
2590 return self.compile(source, options)();
2591 };
2592
2593 /**
2594 * Compile and render a template file for final output. This is most useful for libraries like Express.js.
2595 *
2596 * @example
2597 * swig.renderFile('./template.html', {}, function (err, output) {
2598 * if (err) {
2599 * throw err;
2600 * }
2601 * console.log(output);
2602 * });
2603 *
2604 * @example
2605 * swig.renderFile('./template.html', {});
2606 * // => output
2607 *
2608 * @param {string} pathName File location.
2609 * @param {object} [locals={}] Template variable context.
2610 * @param {Function} [cb] Asyncronous callback function. If not provided, <var>compileFile</var> will run syncronously.
2611 * @return {string} Rendered output.
2612 */
2613 this.renderFile = function (pathName, locals, cb) {
2614 if (cb) {
2615 self.compileFile(pathName, {}, function (err, fn) {
2616 if (err) {
2617 cb(err);
2618 return;
2619 }
2620 cb(null, fn(locals));
2621 });
2622 return;
2623 }
2624
2625 return self.compileFile(pathName)(locals);
2626 };
2627
2628 /**
2629 * Compile string source into a renderable template function.
2630 *
2631 * @example
2632 * var tpl = swig.compile('{{ tacos }}');
2633 * // => {
2634 * // [Function: compiled]
2635 * // parent: null,
2636 * // tokens: [{ compile: [Function] }],
2637 * // blocks: {}
2638 * // }
2639 * tpl({ tacos: 'Tacos!!!!' });
2640 * // => Tacos!!!!
2641 *
2642 * When compiling a source string, a file path should be specified in the options object in order for <var>extends</var>, <var>include</var>, and <var>import</var> to work properly. Do this by adding <code data-language="js">{ filename: '/absolute/path/to/mytpl.html' }</code> to the options argument.
2643 *
2644 * @param {string} source Swig template source string.
2645 * @param {SwigOpts} [options={}] Swig options object.
2646 * @return {function} Renderable function with keys for parent, blocks, and tokens.
2647 */
2648 this.compile = function (source, options) {
2649 var key = options ? options.filename : null,
2650 cached = key ? cacheGet(key) : null,
2651 context,
2652 contextLength,
2653 pre;
2654
2655 if (cached) {
2656 return cached;
2657 }
2658
2659 context = getLocals(options);
2660 contextLength = utils.keys(context).length;
2661 pre = this.precompile(source, options);
2662
2663 function compiled(locals) {
2664 var lcls;
2665 if (locals && contextLength) {
2666 lcls = utils.extend({}, context, locals);
2667 } else if (locals && !contextLength) {
2668 lcls = locals;
2669 } else if (!locals && contextLength) {
2670 lcls = context;
2671 } else {
2672 lcls = {};
2673 }
2674 return pre.tpl(self, lcls, filters, utils, efn);
2675 }
2676
2677 utils.extend(compiled, pre.tokens);
2678
2679 if (key) {
2680 cacheSet(key, compiled);
2681 }
2682
2683 return compiled;
2684 };
2685
2686 /**
2687 * Compile a source file into a renderable template function.
2688 *
2689 * @example
2690 * var tpl = swig.compileFile('./mytpl.html');
2691 * // => {
2692 * // [Function: compiled]
2693 * // parent: null,
2694 * // tokens: [{ compile: [Function] }],
2695 * // blocks: {}
2696 * // }
2697 * tpl({ tacos: 'Tacos!!!!' });
2698 * // => Tacos!!!!
2699 *
2700 * @example
2701 * swig.compileFile('/myfile.txt', { varControls: ['<%=', '=%>'], tagControls: ['<%', '%>']});
2702 * // => will compile 'myfile.txt' using the var and tag controls as specified.
2703 *
2704 * @param {string} pathname File location.
2705 * @param {SwigOpts} [options={}] Swig options object.
2706 * @param {Function} [cb] Asyncronous callback function. If not provided, <var>compileFile</var> will run syncronously.
2707 * @return {function} Renderable function with keys for parent, blocks, and tokens.
2708 */
2709 this.compileFile = function (pathname, options, cb) {
2710 var src, cached;
2711
2712 if (!options) {
2713 options = {};
2714 }
2715
2716 pathname = self.options.loader.resolve(pathname, options.resolveFrom);
2717 if (!options.filename) {
2718 options = utils.extend({ filename: pathname }, options);
2719 }
2720 cached = cacheGet(pathname);
2721
2722 if (cached) {
2723 if (cb) {
2724 cb(null, cached);
2725 return;
2726 }
2727 return cached;
2728 }
2729
2730 if (cb) {
2731 self.options.loader.load(pathname, function (err, src) {
2732 if (err) {
2733 cb(err);
2734 return;
2735 }
2736 var compiled;
2737
2738 try {
2739 compiled = self.compile(src, options);
2740 } catch (err2) {
2741 cb(err2);
2742 return;
2743 }
2744
2745 cb(err, compiled);
2746 });
2747 return;
2748 }
2749
2750 src = self.options.loader.load(pathname);
2751 return self.compile(src, options);
2752 };
2753
2754 /**
2755 * Run a pre-compiled template function. This is most useful in the browser when you've pre-compiled your templates with the Swig command-line tool.
2756 *
2757 * @example
2758 * $ swig compile ./mytpl.html --wrap-start="var mytpl = " > mytpl.js
2759 * @example
2760 * <script src="mytpl.js"></script>
2761 * <script>
2762 * swig.run(mytpl, {});
2763 * // => "rendered template..."
2764 * </script>
2765 *
2766 * @param {function} tpl Pre-compiled Swig template function. Use the Swig CLI to compile your templates.
2767 * @param {object} [locals={}] Template variable context.
2768 * @param {string} [filepath] Filename used for caching the template.
2769 * @return {string} Rendered output.
2770 */
2771 this.run = function (tpl, locals, filepath) {
2772 var context = getLocals({ locals: locals });
2773 if (filepath) {
2774 cacheSet(filepath, tpl);
2775 }
2776 return tpl(self, context, filters, utils, efn);
2777 };
2778};
2779
2780/*!
2781 * Export methods publicly
2782 */
2783defaultInstance = new exports.Swig();
2784exports.setFilter = defaultInstance.setFilter;
2785exports.setTag = defaultInstance.setTag;
2786exports.setExtension = defaultInstance.setExtension;
2787exports.parseFile = defaultInstance.parseFile;
2788exports.precompile = defaultInstance.precompile;
2789exports.compile = defaultInstance.compile;
2790exports.compileFile = defaultInstance.compileFile;
2791exports.render = defaultInstance.render;
2792exports.renderFile = defaultInstance.renderFile;
2793exports.run = defaultInstance.run;
2794exports.invalidateCache = defaultInstance.invalidateCache;
2795exports.loaders = loaders;
2796
2797})()
2798},{"./dateformatter":2,"./filters":3,"./loaders":6,"./parser":8,"./tags":20,"./utils":26}],10:[function(require,module,exports){
2799var utils = require('../utils'),
2800 strings = ['html', 'js'];
2801
2802/**
2803 * Control auto-escaping of variable output from within your templates.
2804 *
2805 * @alias autoescape
2806 *
2807 * @example
2808 * // myvar = '<foo>';
2809 * {% autoescape true %}{{ myvar }}{% endautoescape %}
2810 * // => &lt;foo&gt;
2811 * {% autoescape false %}{{ myvar }}{% endautoescape %}
2812 * // => <foo>
2813 *
2814 * @param {boolean|string} control One of `true`, `false`, `"js"` or `"html"`.
2815 */
2816exports.compile = function (compiler, args, content, parents, options, blockName) {
2817 return compiler(content, parents, options, blockName);
2818};
2819exports.parse = function (str, line, parser, types, stack, opts) {
2820 var matched;
2821 parser.on('*', function (token) {
2822 if (!matched &&
2823 (token.type === types.BOOL ||
2824 (token.type === types.STRING && strings.indexOf(token.match) === -1))
2825 ) {
2826 this.out.push(token.match);
2827 matched = true;
2828 return;
2829 }
2830 utils.throwError('Unexpected token "' + token.match + '" in autoescape tag', line, opts.filename);
2831 });
2832
2833 return true;
2834};
2835exports.ends = true;
2836
2837},{"../utils":26}],11:[function(require,module,exports){
2838/**
2839 * Defines a block in a template that can be overridden by a template extending this one and/or will override the current template's parent template block of the same name.
2840 *
2841 * See <a href="#inheritance">Template Inheritance</a> for more information.
2842 *
2843 * @alias block
2844 *
2845 * @example
2846 * {% block body %}...{% endblock %}
2847 *
2848 * @param {literal} name Name of the block for use in parent and extended templates.
2849 */
2850exports.compile = function (compiler, args, content, parents, options) {
2851 return compiler(content, parents, options, args.join(''));
2852};
2853
2854exports.parse = function (str, line, parser) {
2855 parser.on('*', function (token) {
2856 this.out.push(token.match);
2857 });
2858 return true;
2859};
2860
2861exports.ends = true;
2862exports.block = true;
2863
2864},{}],12:[function(require,module,exports){
2865/**
2866 * Used within an <code data-language="swig">{% if %}</code> tag, the code block following this tag up until <code data-language="swig">{% endif %}</code> will be rendered if the <i>if</i> statement returns false.
2867 *
2868 * @alias else
2869 *
2870 * @example
2871 * {% if false %}
2872 * statement1
2873 * {% else %}
2874 * statement2
2875 * {% endif %}
2876 * // => statement2
2877 *
2878 */
2879exports.compile = function () {
2880 return '} else {\n';
2881};
2882
2883exports.parse = function (str, line, parser, types, stack) {
2884 parser.on('*', function (token) {
2885 throw new Error('"else" tag does not accept any tokens. Found "' + token.match + '" on line ' + line + '.');
2886 });
2887
2888 return (stack.length && stack[stack.length - 1].name === 'if');
2889};
2890
2891},{}],13:[function(require,module,exports){
2892var ifparser = require('./if').parse;
2893
2894/**
2895 * Like <code data-language="swig">{% else %}</code>, except this tag can take more conditional statements.
2896 *
2897 * @alias elseif
2898 * @alias elif
2899 *
2900 * @example
2901 * {% if false %}
2902 * Tacos
2903 * {% elseif true %}
2904 * Burritos
2905 * {% else %}
2906 * Churros
2907 * {% endif %}
2908 * // => Burritos
2909 *
2910 * @param {...mixed} conditional Conditional statement that returns a truthy or falsy value.
2911 */
2912exports.compile = function (compiler, args) {
2913 return '} else if (' + args.join(' ') + ') {\n';
2914};
2915
2916exports.parse = function (str, line, parser, types, stack) {
2917 var okay = ifparser(str, line, parser, types, stack);
2918 return okay && (stack.length && stack[stack.length - 1].name === 'if');
2919};
2920
2921},{"./if":17}],14:[function(require,module,exports){
2922/**
2923 * Makes the current template extend a parent template. This tag must be the first item in your template.
2924 *
2925 * See <a href="#inheritance">Template Inheritance</a> for more information.
2926 *
2927 * @alias extends
2928 *
2929 * @example
2930 * {% extends "./layout.html" %}
2931 *
2932 * @param {string} parentFile Relative path to the file that this template extends.
2933 */
2934exports.compile = function () {};
2935
2936exports.parse = function () {
2937 return true;
2938};
2939
2940exports.ends = false;
2941
2942},{}],15:[function(require,module,exports){
2943var filters = require('../filters');
2944
2945/**
2946 * Apply a filter to an entire block of template.
2947 *
2948 * @alias filter
2949 *
2950 * @example
2951 * {% filter uppercase %}oh hi, {{ name }}{% endfilter %}
2952 * // => OH HI, PAUL
2953 *
2954 * @example
2955 * {% filter replace(".", "!", "g") %}Hi. My name is Paul.{% endfilter %}
2956 * // => Hi! My name is Paul!
2957 *
2958 * @param {function} filter The filter that should be applied to the contents of the tag.
2959 */
2960
2961exports.compile = function (compiler, args, content, parents, options, blockName) {
2962 var filter = args.shift().replace(/\($/, ''),
2963 val = '(function () {\n' +
2964 ' var _output = "";\n' +
2965 compiler(content, parents, options, blockName) +
2966 ' return _output;\n' +
2967 '})()';
2968
2969 if (args[args.length - 1] === ')') {
2970 args.pop();
2971 }
2972
2973 args = (args.length) ? ', ' + args.join('') : '';
2974 return '_output += _filters["' + filter + '"](' + val + args + ');\n';
2975};
2976
2977exports.parse = function (str, line, parser, types) {
2978 var filter;
2979
2980 function check(filter) {
2981 if (!filters.hasOwnProperty(filter)) {
2982 throw new Error('Filter "' + filter + '" does not exist on line ' + line + '.');
2983 }
2984 }
2985
2986 parser.on(types.FUNCTION, function (token) {
2987 if (!filter) {
2988 filter = token.match.replace(/\($/, '');
2989 check(filter);
2990 this.out.push(token.match);
2991 this.state.push(token.type);
2992 return;
2993 }
2994 return true;
2995 });
2996
2997 parser.on(types.VAR, function (token) {
2998 if (!filter) {
2999 filter = token.match;
3000 check(filter);
3001 this.out.push(filter);
3002 return;
3003 }
3004 return true;
3005 });
3006
3007 return true;
3008};
3009
3010exports.ends = true;
3011
3012},{"../filters":3}],16:[function(require,module,exports){
3013var ctx = '_ctx.',
3014 ctxloop = ctx + 'loop',
3015 ctxloopcache = ctx + '___loopcache';
3016
3017/**
3018 * Loop over objects and arrays.
3019 *
3020 * @alias for
3021 *
3022 * @example
3023 * // obj = { one: 'hi', two: 'bye' };
3024 * {% for x in obj %}
3025 * {% if loop.first %}<ul>{% endif %}
3026 * <li>{{ loop.index }} - {{ loop.key }}: {{ x }}</li>
3027 * {% if loop.last %}</ul>{% endif %}
3028 * {% endfor %}
3029 * // => <ul>
3030 * // <li>1 - one: hi</li>
3031 * // <li>2 - two: bye</li>
3032 * // </ul>
3033 *
3034 * @example
3035 * // arr = [1, 2, 3]
3036 * // Reverse the array, shortcut the key/index to `key`
3037 * {% for key, val in arr|reverse %}
3038 * {{ key }} -- {{ val }}
3039 * {% endfor %}
3040 * // => 0 -- 3
3041 * // 1 -- 2
3042 * // 2 -- 1
3043 *
3044 * @param {literal} [key] A shortcut to the index of the array or current key accessor.
3045 * @param {literal} variable The current value will be assigned to this variable name temporarily. The variable will be reset upon ending the for tag.
3046 * @param {literal} in Literally, "in". This token is required.
3047 * @param {object} object An enumerable object that will be iterated over.
3048 *
3049 * @return {loop.index} The current iteration of the loop (1-indexed)
3050 * @return {loop.index0} The current iteration of the loop (0-indexed)
3051 * @return {loop.revindex} The number of iterations from the end of the loop (1-indexed)
3052 * @return {loop.revindex0} The number of iterations from the end of the loop (0-indexed)
3053 * @return {loop.key} If the iterator is an object, this will be the key of the current item, otherwise it will be the same as the loop.index.
3054 * @return {loop.first} True if the current object is the first in the object or array.
3055 * @return {loop.last} True if the current object is the last in the object or array.
3056 */
3057exports.compile = function (compiler, args, content, parents, options, blockName) {
3058 var val = args.shift(),
3059 key = '__k',
3060 last;
3061
3062 if (args[0] && args[0] === ',') {
3063 args.shift();
3064 key = val;
3065 val = args.shift();
3066 }
3067
3068 last = args.join('');
3069
3070 return [
3071 '(function () {\n',
3072 ' var __l = ' + last + ';\n',
3073 ' if (!__l) { return; }\n',
3074 ' ' + ctxloopcache + ' = { loop: ' + ctxloop + ', ' + val + ': ' + ctx + val + ', ' + key + ': ' + ctx + key + ' };\n',
3075 ' ' + ctxloop + ' = { first: false, index: 1, index0: 0, revindex: __l.length, revindex0: __l.length - 1, length: __l.length, last: false };\n',
3076 ' _utils.each(__l, function (' + val + ', ' + key + ') {\n',
3077 ' ' + ctx + val + ' = ' + val + ';\n',
3078 ' ' + ctx + key + ' = ' + key + ';\n',
3079 ' ' + ctxloop + '.key = ' + key + ';\n',
3080 ' ' + ctxloop + '.first = (' + ctxloop + '.index0 === 0);\n',
3081 ' ' + ctxloop + '.last = (' + ctxloop + '.revindex0 === 0);\n',
3082 ' ' + compiler(content, parents, options, blockName),
3083 ' ' + ctxloop + '.index += 1; ' + ctxloop + '.index0 += 1; ' + ctxloop + '.revindex -= 1; ' + ctxloop + '.revindex0 -= 1;\n',
3084 ' });\n',
3085 ' ' + ctxloop + ' = ' + ctxloopcache + '.loop;\n',
3086 ' ' + ctx + val + ' = ' + ctxloopcache + '.' + val + ';\n',
3087 ' ' + ctx + key + ' = ' + ctxloopcache + '.' + key + ';\n',
3088 '})();\n'
3089 ].join('');
3090};
3091
3092exports.parse = function (str, line, parser, types) {
3093 var firstVar, ready;
3094
3095 parser.on(types.NUMBER, function (token) {
3096 var lastState = this.state.length ? this.state[this.state.length - 1] : null;
3097 if (!ready ||
3098 (lastState !== types.ARRAYOPEN &&
3099 lastState !== types.CURLYOPEN &&
3100 lastState !== types.CURLYCLOSE &&
3101 lastState !== types.FUNCTION &&
3102 lastState !== types.FILTER)
3103 ) {
3104 throw new Error('Unexpected number "' + token.match + '" on line ' + line + '.');
3105 }
3106 return true;
3107 });
3108
3109 parser.on(types.VAR, function (token) {
3110 if (ready && firstVar) {
3111 return true;
3112 }
3113
3114 if (!this.out.length) {
3115 firstVar = true;
3116 }
3117
3118 this.out.push(token.match);
3119 });
3120
3121 parser.on(types.COMMA, function (token) {
3122 if (firstVar && this.prevToken.type === types.VAR) {
3123 this.out.push(token.match);
3124 return;
3125 }
3126
3127 return true;
3128 });
3129
3130 parser.on(types.COMPARATOR, function (token) {
3131 if (token.match !== 'in' || !firstVar) {
3132 throw new Error('Unexpected token "' + token.match + '" on line ' + line + '.');
3133 }
3134 ready = true;
3135 });
3136
3137 return true;
3138};
3139
3140exports.ends = true;
3141
3142},{}],17:[function(require,module,exports){
3143/**
3144 * Used to create conditional statements in templates. Accepts most JavaScript valid comparisons.
3145 *
3146 * Can be used in conjunction with <a href="#elseif"><code data-language="swig">{% elseif ... %}</code></a> and <a href="#else"><code data-language="swig">{% else %}</code></a> tags.
3147 *
3148 * @alias if
3149 *
3150 * @example
3151 * {% if x %}{% endif %}
3152 * {% if !x %}{% endif %}
3153 * {% if not x %}{% endif %}
3154 *
3155 * @example
3156 * {% if x and y %}{% endif %}
3157 * {% if x && y %}{% endif %}
3158 * {% if x or y %}{% endif %}
3159 * {% if x || y %}{% endif %}
3160 * {% if x || (y && z) %}{% endif %}
3161 *
3162 * @example
3163 * {% if x [operator] y %}
3164 * Operators: ==, !=, <, <=, >, >=, ===, !==
3165 * {% endif %}
3166 *
3167 * @example
3168 * {% if x == 'five' %}
3169 * The operands can be also be string or number literals
3170 * {% endif %}
3171 *
3172 * @example
3173 * {% if x|lower === 'tacos' %}
3174 * You can use filters on any operand in the statement.
3175 * {% endif %}
3176 *
3177 * @example
3178 * {% if x in y %}
3179 * If x is a value that is present in y, this will return true.
3180 * {% endif %}
3181 *
3182 * @param {...mixed} conditional Conditional statement that returns a truthy or falsy value.
3183 */
3184exports.compile = function (compiler, args, content, parents, options, blockName) {
3185 return 'if (' + args.join(' ') + ') { \n' +
3186 compiler(content, parents, options, blockName) + '\n' +
3187 '}';
3188};
3189
3190exports.parse = function (str, line, parser, types) {
3191 parser.on(types.COMPARATOR, function (token) {
3192 if (this.isLast) {
3193 throw new Error('Unexpected logic "' + token.match + '" on line ' + line + '.');
3194 }
3195 if (this.prevToken.type === types.NOT) {
3196 throw new Error('Attempted logic "not ' + token.match + '" on line ' + line + '. Use !(foo ' + token.match + ') instead.');
3197 }
3198 this.out.push(token.match);
3199 });
3200
3201 parser.on(types.NOT, function (token) {
3202 if (this.isLast) {
3203 throw new Error('Unexpected logic "' + token.match + '" on line ' + line + '.');
3204 }
3205 this.out.push(token.match);
3206 });
3207
3208 parser.on(types.BOOL, function (token) {
3209 this.out.push(token.match);
3210 });
3211
3212 parser.on(types.LOGIC, function (token) {
3213 if (!this.out.length || this.isLast) {
3214 throw new Error('Unexpected logic "' + token.match + '" on line ' + line + '.');
3215 }
3216 this.out.push(token.match);
3217 this.filterApplyIdx.pop();
3218 });
3219
3220 return true;
3221};
3222
3223exports.ends = true;
3224
3225},{}],18:[function(require,module,exports){
3226(function(){var utils = require('../utils');
3227
3228/**
3229 * Allows you to import macros from another file directly into your current context.
3230 * The import tag is specifically designed for importing macros into your template with a specific context scope. This is very useful for keeping your macros from overriding template context that is being injected by your server-side page generation.
3231 *
3232 * @alias import
3233 *
3234 * @example
3235 * {% import './formmacros.html' as forms %}
3236 * {{ form.input("text", "name") }}
3237 * // => <input type="text" name="name">
3238 *
3239 * @example
3240 * {% import "../shared/tags.html" as tags %}
3241 * {{ tags.stylesheet('global') }}
3242 * // => <link rel="stylesheet" href="/global.css">
3243 *
3244 * @param {string|var} file Relative path from the current template file to the file to import macros from.
3245 * @param {literal} as Literally, "as".
3246 * @param {literal} varname Local-accessible object name to assign the macros to.
3247 */
3248exports.compile = function (compiler, args) {
3249 var ctx = args.pop(),
3250 out = '_ctx.' + ctx + ' = {};\n var _output = "";\n',
3251 replacements = utils.map(args, function (arg) {
3252 return {
3253 ex: new RegExp('_ctx.' + arg.name, 'g'),
3254 re: '_ctx.' + ctx + '.' + arg.name
3255 };
3256 });
3257
3258 // Replace all occurrences of all macros in this file with
3259 // proper namespaced definitions and calls
3260 utils.each(args, function (arg) {
3261 var c = arg.compiled;
3262 utils.each(replacements, function (re) {
3263 c = c.replace(re.ex, re.re);
3264 });
3265 out += c;
3266 });
3267
3268 return out;
3269};
3270
3271exports.parse = function (str, line, parser, types, stack, opts) {
3272 var parseFile = require('../swig').parseFile,
3273 compiler = require('../parser').compile,
3274 parseOpts = { resolveFrom: opts.filename },
3275 compileOpts = utils.extend({}, opts, parseOpts),
3276 tokens,
3277 ctx;
3278
3279 parser.on(types.STRING, function (token) {
3280 var self = this;
3281 if (!tokens) {
3282 tokens = parseFile(token.match.replace(/^("|')|("|')$/g, ''), parseOpts).tokens;
3283 utils.each(tokens, function (token) {
3284 var out = '',
3285 macroName;
3286 if (!token || token.name !== 'macro' || !token.compile) {
3287 return;
3288 }
3289 macroName = token.args[0];
3290 out += token.compile(compiler, token.args, token.content, [], compileOpts) + '\n';
3291 self.out.push({compiled: out, name: macroName});
3292 });
3293 return;
3294 }
3295
3296 throw new Error('Unexpected string ' + token.match + ' on line ' + line + '.');
3297 });
3298
3299 parser.on(types.VAR, function (token) {
3300 var self = this;
3301 if (!tokens || ctx) {
3302 throw new Error('Unexpected variable "' + token.match + '" on line ' + line + '.');
3303 }
3304
3305 if (token.match === 'as') {
3306 return;
3307 }
3308
3309 ctx = token.match;
3310 self.out.push(ctx);
3311 return false;
3312 });
3313
3314 return true;
3315};
3316
3317exports.block = true;
3318
3319})()
3320},{"../parser":8,"../swig":9,"../utils":26}],19:[function(require,module,exports){
3321var ignore = 'ignore',
3322 missing = 'missing',
3323 only = 'only';
3324
3325/**
3326 * Includes a template partial in place. The template is rendered within the current locals variable context.
3327 *
3328 * @alias include
3329 *
3330 * @example
3331 * // food = 'burritos';
3332 * // drink = 'lemonade';
3333 * {% include "./partial.html" %}
3334 * // => I like burritos and lemonade.
3335 *
3336 * @example
3337 * // my_obj = { food: 'tacos', drink: 'horchata' };
3338 * {% include "./partial.html" with my_obj only %}
3339 * // => I like tacos and horchata.
3340 *
3341 * @example
3342 * {% include "/this/file/does/not/exist" ignore missing %}
3343 * // => (Nothing! empty string)
3344 *
3345 * @param {string|var} file The path, relative to the template root, to render into the current context.
3346 * @param {literal} [with] Literally, "with".
3347 * @param {object} [context] Local variable key-value object context to provide to the included file.
3348 * @param {literal} [only] Restricts to <strong>only</strong> passing the <code>with context</code> as local variables–the included template will not be aware of any other local variables in the parent template. For best performance, usage of this option is recommended if possible.
3349 * @param {literal} [ignore missing] Will output empty string if not found instead of throwing an error.
3350 */
3351exports.compile = function (compiler, args) {
3352 var file = args.shift(),
3353 onlyIdx = args.indexOf(only),
3354 onlyCtx = onlyIdx !== -1 ? args.splice(onlyIdx, 1) : false,
3355 parentFile = args.pop().replace(/\\/g, '\\\\'),
3356 ignore = args[args.length - 1] === missing ? (args.pop()) : false,
3357 w = args.join('');
3358
3359 return (ignore ? ' try {\n' : '') +
3360 '_output += _swig.compileFile(' + file + ', {' +
3361 'resolveFrom: "' + parentFile + '"' +
3362 '})(' +
3363 ((onlyCtx && w) ? w : (!w ? '_ctx' : '_utils.extend({}, _ctx, ' + w + ')')) +
3364 ');\n' +
3365 (ignore ? '} catch (e) {}\n' : '');
3366};
3367
3368exports.parse = function (str, line, parser, types, stack, opts) {
3369 var file, w;
3370 parser.on(types.STRING, function (token) {
3371 if (!file) {
3372 file = token.match;
3373 this.out.push(file);
3374 return;
3375 }
3376
3377 return true;
3378 });
3379
3380 parser.on(types.VAR, function (token) {
3381 if (!file) {
3382 file = token.match;
3383 return true;
3384 }
3385
3386 if (!w && token.match === 'with') {
3387 w = true;
3388 return;
3389 }
3390
3391 if (w && token.match === only && this.prevToken.match !== 'with') {
3392 this.out.push(token.match);
3393 return;
3394 }
3395
3396 if (token.match === ignore) {
3397 return false;
3398 }
3399
3400 if (token.match === missing) {
3401 if (this.prevToken.match !== ignore) {
3402 throw new Error('Unexpected token "' + missing + '" on line ' + line + '.');
3403 }
3404 this.out.push(token.match);
3405 return false;
3406 }
3407
3408 if (this.prevToken.match === ignore) {
3409 throw new Error('Expected "' + missing + '" on line ' + line + ' but found "' + token.match + '".');
3410 }
3411
3412 return true;
3413 });
3414
3415 parser.on('end', function () {
3416 this.out.push(opts.filename || null);
3417 });
3418
3419 return true;
3420};
3421
3422},{}],20:[function(require,module,exports){
3423exports.autoescape = require('./autoescape');
3424exports.block = require('./block');
3425exports.else = require('./else');
3426exports.elseif = require('./elseif');
3427exports.elif = exports.elseif;
3428exports.extends = require('./extends');
3429exports.filter = require('./filter');
3430exports.for = require('./for');
3431exports.if = require('./if');
3432exports.import = require('./import');
3433exports.include = require('./include');
3434exports.macro = require('./macro');
3435exports.parent = require('./parent');
3436exports.raw = require('./raw');
3437exports.set = require('./set');
3438exports.spaceless = require('./spaceless');
3439
3440},{"./autoescape":10,"./block":11,"./else":12,"./elseif":13,"./extends":14,"./filter":15,"./for":16,"./if":17,"./import":18,"./include":19,"./macro":21,"./parent":22,"./raw":23,"./set":24,"./spaceless":25}],21:[function(require,module,exports){
3441/**
3442 * Create custom, reusable snippets within your templates.
3443 * Can be imported from one template to another using the <a href="#import"><code data-language="swig">{% import ... %}</code></a> tag.
3444 *
3445 * @alias macro
3446 *
3447 * @example
3448 * {% macro input(type, name, id, label, value, error) %}
3449 * <label for="{{ name }}">{{ label }}</label>
3450 * <input type="{{ type }}" name="{{ name }}" id="{{ id }}" value="{{ value }}"{% if error %} class="error"{% endif %}>
3451 * {% endmacro %}
3452 *
3453 * {{ input("text", "fname", "fname", "First Name", fname.value, fname.errors) }}
3454 * // => <label for="fname">First Name</label>
3455 * // <input type="text" name="fname" id="fname" value="">
3456 *
3457 * @param {...arguments} arguments User-defined arguments.
3458 */
3459exports.compile = function (compiler, args, content, parents, options, blockName) {
3460 var fnName = args.shift();
3461
3462 return '_ctx.' + fnName + ' = function (' + args.join('') + ') {\n' +
3463 ' var _output = "";\n' +
3464 compiler(content, parents, options, blockName) + '\n' +
3465 ' return _output;\n' +
3466 '};\n' +
3467 '_ctx.' + fnName + '.safe = true;\n';
3468};
3469
3470exports.parse = function (str, line, parser, types) {
3471 var name;
3472
3473 parser.on(types.VAR, function (token) {
3474 if (token.match.indexOf('.') !== -1) {
3475 throw new Error('Unexpected dot in macro argument "' + token.match + '" on line ' + line + '.');
3476 }
3477 this.out.push(token.match);
3478 });
3479
3480 parser.on(types.FUNCTION, function (token) {
3481 if (!name) {
3482 name = token.match;
3483 this.out.push(name);
3484 this.state.push(types.FUNCTION);
3485 }
3486 });
3487
3488 parser.on(types.FUNCTIONEMPTY, function (token) {
3489 if (!name) {
3490 name = token.match;
3491 this.out.push(name);
3492 }
3493 });
3494
3495 parser.on(types.PARENCLOSE, function () {
3496 if (this.isLast) {
3497 return;
3498 }
3499 throw new Error('Unexpected parenthesis close on line ' + line + '.');
3500 });
3501
3502 parser.on(types.COMMA, function () {
3503 return true;
3504 });
3505
3506 parser.on('*', function () {
3507 return;
3508 });
3509
3510 return true;
3511};
3512
3513exports.ends = true;
3514exports.block = true;
3515
3516},{}],22:[function(require,module,exports){
3517/**
3518 * Inject the content from the parent template's block of the same name into the current block.
3519 *
3520 * See <a href="#inheritance">Template Inheritance</a> for more information.
3521 *
3522 * @alias parent
3523 *
3524 * @example
3525 * {% extends "./foo.html" %}
3526 * {% block content %}
3527 * My content.
3528 * {% parent %}
3529 * {% endblock %}
3530 *
3531 */
3532exports.compile = function (compiler, args, content, parents, options, blockName) {
3533 if (!parents || !parents.length) {
3534 return '';
3535 }
3536
3537 var parentFile = args[0],
3538 breaker = true,
3539 l = parents.length,
3540 i = 0,
3541 parent,
3542 block;
3543
3544 for (i; i < l; i += 1) {
3545 parent = parents[i];
3546 if (!parent.blocks || !parent.blocks.hasOwnProperty(blockName)) {
3547 continue;
3548 }
3549 // Silly JSLint "Strange Loop" requires return to be in a conditional
3550 if (breaker && parentFile !== parent.name) {
3551 block = parent.blocks[blockName];
3552 return block.compile(compiler, [blockName], block.content, parents.slice(i + 1), options) + '\n';
3553 }
3554 }
3555};
3556
3557exports.parse = function (str, line, parser, types, stack, opts) {
3558 parser.on('*', function (token) {
3559 throw new Error('Unexpected argument "' + token.match + '" on line ' + line + '.');
3560 });
3561
3562 parser.on('end', function () {
3563 this.out.push(opts.filename);
3564 });
3565
3566 return true;
3567};
3568
3569},{}],23:[function(require,module,exports){
3570// Magic tag, hardcoded into parser
3571
3572/**
3573 * Forces the content to not be auto-escaped. All swig instructions will be ignored and the content will be rendered exactly as it was given.
3574 *
3575 * @alias raw
3576 *
3577 * @example
3578 * // foobar = '<p>'
3579 * {% raw %}{{ foobar }}{% endraw %}
3580 * // => {{ foobar }}
3581 *
3582 */
3583exports.compile = function (compiler, args, content, parents, options, blockName) {
3584 return compiler(content, parents, options, blockName);
3585};
3586exports.parse = function (str, line, parser) {
3587 parser.on('*', function (token) {
3588 throw new Error('Unexpected token "' + token.match + '" in raw tag on line ' + line + '.');
3589 });
3590 return true;
3591};
3592exports.ends = true;
3593
3594},{}],24:[function(require,module,exports){
3595(function(){/**
3596 * Set a variable for re-use in the current context. This will over-write any value already set to the context for the given <var>varname</var>.
3597 *
3598 * @alias set
3599 *
3600 * @example
3601 * {% set foo = "anything!" %}
3602 * {{ foo }}
3603 * // => anything!
3604 *
3605 * @example
3606 * // index = 2;
3607 * {% set bar = 1 %}
3608 * {% set bar += index|default(3) %}
3609 * // => 3
3610 *
3611 * @example
3612 * // foods = {};
3613 * // food = 'chili';
3614 * {% set foods[food] = "con queso" %}
3615 * {{ foods.chili }}
3616 * // => con queso
3617 *
3618 * @example
3619 * // foods = { chili: 'chili con queso' }
3620 * {% set foods.chili = "guatamalan insanity pepper" %}
3621 * {{ foods.chili }}
3622 * // => guatamalan insanity pepper
3623 *
3624 * @param {literal} varname The variable name to assign the value to.
3625 * @param {literal} assignement Any valid JavaScript assignement. <code data-language="js">=, +=, *=, /=, -=</code>
3626 * @param {*} value Valid variable output.
3627 */
3628exports.compile = function (compiler, args) {
3629 return args.join(' ') + ';\n';
3630};
3631
3632exports.parse = function (str, line, parser, types) {
3633 var nameSet = '',
3634 propertyName;
3635
3636 parser.on(types.VAR, function (token) {
3637 if (propertyName) {
3638 // Tell the parser where to find the variable
3639 propertyName += '_ctx.' + token.match;
3640 return;
3641 }
3642
3643 if (!parser.out.length) {
3644 nameSet += token.match;
3645 return;
3646 }
3647
3648 return true;
3649 });
3650
3651 parser.on(types.BRACKETOPEN, function (token) {
3652 if (!propertyName && !this.out.length) {
3653 propertyName = token.match;
3654 return;
3655 }
3656
3657 return true;
3658 });
3659
3660 parser.on(types.STRING, function (token) {
3661 if (propertyName && !this.out.length) {
3662 propertyName += token.match;
3663 return;
3664 }
3665
3666 return true;
3667 });
3668
3669 parser.on(types.BRACKETCLOSE, function (token) {
3670 if (propertyName && !this.out.length) {
3671 nameSet += propertyName + token.match;
3672 propertyName = undefined;
3673 return;
3674 }
3675
3676 return true;
3677 });
3678
3679 parser.on(types.DOTKEY, function (token) {
3680 if (!propertyName && !nameSet) {
3681 return true;
3682 }
3683 nameSet += '.' + token.match;
3684 return;
3685 });
3686
3687 parser.on(types.ASSIGNMENT, function (token) {
3688 if (this.out.length || !nameSet) {
3689 throw new Error('Unexpected assignment "' + token.match + '" on line ' + line + '.');
3690 }
3691
3692 this.out.push(
3693 // Prevent the set from spilling into global scope
3694 '_ctx.' + nameSet
3695 );
3696 this.out.push(token.match);
3697 });
3698
3699 return true;
3700};
3701
3702exports.block = true;
3703
3704})()
3705},{}],25:[function(require,module,exports){
3706var utils = require('../utils');
3707
3708/**
3709 * Attempts to remove whitespace between HTML tags. Use at your own risk.
3710 *
3711 * @alias spaceless
3712 *
3713 * @example
3714 * {% spaceless %}
3715 * {% for num in foo %}
3716 * <li>{{ loop.index }}</li>
3717 * {% endfor %}
3718 * {% endspaceless %}
3719 * // => <li>1</li><li>2</li><li>3</li>
3720 *
3721 */
3722exports.compile = function (compiler, args, content, parents, options, blockName) {
3723 function stripWhitespace(tokens) {
3724 return utils.map(tokens, function (token) {
3725 if (token.content || typeof token !== 'string') {
3726 token.content = stripWhitespace(token.content);
3727 return token;
3728 }
3729
3730 return token.replace(/^\s+/, '')
3731 .replace(/>\s+</g, '><')
3732 .replace(/\s+$/, '');
3733 });
3734 }
3735
3736 return compiler(stripWhitespace(content), parents, options, blockName);
3737};
3738
3739exports.parse = function (str, line, parser) {
3740 parser.on('*', function (token) {
3741 throw new Error('Unexpected token "' + token.match + '" on line ' + line + '.');
3742 });
3743
3744 return true;
3745};
3746
3747exports.ends = true;
3748
3749},{"../utils":26}],26:[function(require,module,exports){
3750var isArray;
3751
3752/**
3753 * Strip leading and trailing whitespace from a string.
3754 * @param {string} input
3755 * @return {string} Stripped input.
3756 */
3757exports.strip = function (input) {
3758 return input.replace(/^\s+|\s+$/g, '');
3759};
3760
3761/**
3762 * Test if a string starts with a given prefix.
3763 * @param {string} str String to test against.
3764 * @param {string} prefix Prefix to check for.
3765 * @return {boolean}
3766 */
3767exports.startsWith = function (str, prefix) {
3768 return str.indexOf(prefix) === 0;
3769};
3770
3771/**
3772 * Test if a string ends with a given suffix.
3773 * @param {string} str String to test against.
3774 * @param {string} suffix Suffix to check for.
3775 * @return {boolean}
3776 */
3777exports.endsWith = function (str, suffix) {
3778 return str.indexOf(suffix, str.length - suffix.length) !== -1;
3779};
3780
3781/**
3782 * Iterate over an array or object.
3783 * @param {array|object} obj Enumerable object.
3784 * @param {Function} fn Callback function executed for each item.
3785 * @return {array|object} The original input object.
3786 */
3787exports.each = function (obj, fn) {
3788 var i, l;
3789
3790 if (isArray(obj)) {
3791 i = 0;
3792 l = obj.length;
3793 for (i; i < l; i += 1) {
3794 if (fn(obj[i], i, obj) === false) {
3795 break;
3796 }
3797 }
3798 } else {
3799 for (i in obj) {
3800 if (obj.hasOwnProperty(i)) {
3801 if (fn(obj[i], i, obj) === false) {
3802 break;
3803 }
3804 }
3805 }
3806 }
3807
3808 return obj;
3809};
3810
3811/**
3812 * Test if an object is an Array.
3813 * @param {object} obj
3814 * @return {boolean}
3815 */
3816exports.isArray = isArray = (Array.hasOwnProperty('isArray')) ? Array.isArray : function (obj) {
3817 return (obj) ? (typeof obj === 'object' && Object.prototype.toString.call(obj).indexOf() !== -1) : false;
3818};
3819
3820/**
3821 * Test if an item in an enumerable matches your conditions.
3822 * @param {array|object} obj Enumerable object.
3823 * @param {Function} fn Executed for each item. Return true if your condition is met.
3824 * @return {boolean}
3825 */
3826exports.some = function (obj, fn) {
3827 var i = 0,
3828 result,
3829 l;
3830 if (isArray(obj)) {
3831 l = obj.length;
3832
3833 for (i; i < l; i += 1) {
3834 result = fn(obj[i], i, obj);
3835 if (result) {
3836 break;
3837 }
3838 }
3839 } else {
3840 exports.each(obj, function (value, index) {
3841 result = fn(value, index, obj);
3842 return !(result);
3843 });
3844 }
3845 return !!result;
3846};
3847
3848/**
3849 * Return a new enumerable, mapped by a given iteration function.
3850 * @param {object} obj Enumerable object.
3851 * @param {Function} fn Executed for each item. Return the item to replace the original item with.
3852 * @return {object} New mapped object.
3853 */
3854exports.map = function (obj, fn) {
3855 var i = 0,
3856 result = [],
3857 l;
3858
3859 if (isArray(obj)) {
3860 l = obj.length;
3861 for (i; i < l; i += 1) {
3862 result[i] = fn(obj[i], i);
3863 }
3864 } else {
3865 for (i in obj) {
3866 if (obj.hasOwnProperty(i)) {
3867 result[i] = fn(obj[i], i);
3868 }
3869 }
3870 }
3871 return result;
3872};
3873
3874/**
3875 * Copy all of the properties in the source objects over to the destination object, and return the destination object. It's in-order, so the last source will override properties of the same name in previous arguments.
3876 * @param {...object} arguments
3877 * @return {object}
3878 */
3879exports.extend = function () {
3880 var args = arguments,
3881 target = args[0],
3882 objs = (args.length > 1) ? Array.prototype.slice.call(args, 1) : [],
3883 i = 0,
3884 l = objs.length,
3885 key,
3886 obj;
3887
3888 for (i; i < l; i += 1) {
3889 obj = objs[i] || {};
3890 for (key in obj) {
3891 if (obj.hasOwnProperty(key)) {
3892 target[key] = obj[key];
3893 }
3894 }
3895 }
3896 return target;
3897};
3898
3899/**
3900 * Get all of the keys on an object.
3901 * @param {object} obj
3902 * @return {array}
3903 */
3904exports.keys = function (obj) {
3905 if (Object.keys) {
3906 return Object.keys(obj);
3907 }
3908
3909 return exports.map(obj, function (v, k) {
3910 return k;
3911 });
3912};
3913
3914/**
3915 * Throw an error with possible line number and source file.
3916 * @param {string} message Error message
3917 * @param {number} [line] Line number in template.
3918 * @param {string} [file] Template file the error occured in.
3919 * @throws {Error} No seriously, the point is to throw an error.
3920 */
3921exports.throwError = function (message, line, file) {
3922 if (line) {
3923 message += ' on line ' + line;
3924 }
3925 if (file) {
3926 message += ' in file ' + file;
3927 }
3928 throw new Error(message + '.');
3929};
3930
3931},{}],27:[function(require,module,exports){
3932// nothing to see here... no file methods for the browser
3933
3934},{}],28:[function(require,module,exports){
3935(function(process){function filter (xs, fn) {
3936 var res = [];
3937 for (var i = 0; i < xs.length; i++) {
3938 if (fn(xs[i], i, xs)) res.push(xs[i]);
3939 }
3940 return res;
3941}
3942
3943// resolves . and .. elements in a path array with directory names there
3944// must be no slashes, empty elements, or device names (c:\) in the array
3945// (so also no leading and trailing slashes - it does not distinguish
3946// relative and absolute paths)
3947function normalizeArray(parts, allowAboveRoot) {
3948 // if the path tries to go above the root, `up` ends up > 0
3949 var up = 0;
3950 for (var i = parts.length; i >= 0; i--) {
3951 var last = parts[i];
3952 if (last == '.') {
3953 parts.splice(i, 1);
3954 } else if (last === '..') {
3955 parts.splice(i, 1);
3956 up++;
3957 } else if (up) {
3958 parts.splice(i, 1);
3959 up--;
3960 }
3961 }
3962
3963 // if the path is allowed to go above the root, restore leading ..s
3964 if (allowAboveRoot) {
3965 for (; up--; up) {
3966 parts.unshift('..');
3967 }
3968 }
3969
3970 return parts;
3971}
3972
3973// Regex to split a filename into [*, dir, basename, ext]
3974// posix version
3975var splitPathRe = /^(.+\/(?!$)|\/)?((?:.+?)?(\.[^.]*)?)$/;
3976
3977// path.resolve([from ...], to)
3978// posix version
3979exports.resolve = function() {
3980var resolvedPath = '',
3981 resolvedAbsolute = false;
3982
3983for (var i = arguments.length; i >= -1 && !resolvedAbsolute; i--) {
3984 var path = (i >= 0)
3985 ? arguments[i]
3986 : process.cwd();
3987
3988 // Skip empty and invalid entries
3989 if (typeof path !== 'string' || !path) {
3990 continue;
3991 }
3992
3993 resolvedPath = path + '/' + resolvedPath;
3994 resolvedAbsolute = path.charAt(0) === '/';
3995}
3996
3997// At this point the path should be resolved to a full absolute path, but
3998// handle relative paths to be safe (might happen when process.cwd() fails)
3999
4000// Normalize the path
4001resolvedPath = normalizeArray(filter(resolvedPath.split('/'), function(p) {
4002 return !!p;
4003 }), !resolvedAbsolute).join('/');
4004
4005 return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.';
4006};
4007
4008// path.normalize(path)
4009// posix version
4010exports.normalize = function(path) {
4011var isAbsolute = path.charAt(0) === '/',
4012 trailingSlash = path.slice(-1) === '/';
4013
4014// Normalize the path
4015path = normalizeArray(filter(path.split('/'), function(p) {
4016 return !!p;
4017 }), !isAbsolute).join('/');
4018
4019 if (!path && !isAbsolute) {
4020 path = '.';
4021 }
4022 if (path && trailingSlash) {
4023 path += '/';
4024 }
4025
4026 return (isAbsolute ? '/' : '') + path;
4027};
4028
4029
4030// posix version
4031exports.join = function() {
4032 var paths = Array.prototype.slice.call(arguments, 0);
4033 return exports.normalize(filter(paths, function(p, index) {
4034 return p && typeof p === 'string';
4035 }).join('/'));
4036};
4037
4038
4039exports.dirname = function(path) {
4040 var dir = splitPathRe.exec(path)[1] || '';
4041 var isWindows = false;
4042 if (!dir) {
4043 // No dirname
4044 return '.';
4045 } else if (dir.length === 1 ||
4046 (isWindows && dir.length <= 3 && dir.charAt(1) === ':')) {
4047 // It is just a slash or a drive letter with a slash
4048 return dir;
4049 } else {
4050 // It is a full dirname, strip trailing slash
4051 return dir.substring(0, dir.length - 1);
4052 }
4053};
4054
4055
4056exports.basename = function(path, ext) {
4057 var f = splitPathRe.exec(path)[2] || '';
4058 // TODO: make this comparison case-insensitive on windows?
4059 if (ext && f.substr(-1 * ext.length) === ext) {
4060 f = f.substr(0, f.length - ext.length);
4061 }
4062 return f;
4063};
4064
4065
4066exports.extname = function(path) {
4067 return splitPathRe.exec(path)[3] || '';
4068};
4069
4070exports.relative = function(from, to) {
4071 from = exports.resolve(from).substr(1);
4072 to = exports.resolve(to).substr(1);
4073
4074 function trim(arr) {
4075 var start = 0;
4076 for (; start < arr.length; start++) {
4077 if (arr[start] !== '') break;
4078 }
4079
4080 var end = arr.length - 1;
4081 for (; end >= 0; end--) {
4082 if (arr[end] !== '') break;
4083 }
4084
4085 if (start > end) return [];
4086 return arr.slice(start, end - start + 1);
4087 }
4088
4089 var fromParts = trim(from.split('/'));
4090 var toParts = trim(to.split('/'));
4091
4092 var length = Math.min(fromParts.length, toParts.length);
4093 var samePartsLength = length;
4094 for (var i = 0; i < length; i++) {
4095 if (fromParts[i] !== toParts[i]) {
4096 samePartsLength = i;
4097 break;
4098 }
4099 }
4100
4101 var outputParts = [];
4102 for (var i = samePartsLength; i < fromParts.length; i++) {
4103 outputParts.push('..');
4104 }
4105
4106 outputParts = outputParts.concat(toParts.slice(samePartsLength));
4107
4108 return outputParts.join('/');
4109};
4110
4111})(require("__browserify_process"))
4112},{"__browserify_process":29}],29:[function(require,module,exports){
4113// shim for using process in browser
4114
4115var process = module.exports = {};
4116
4117process.nextTick = (function () {
4118 var canSetImmediate = typeof window !== 'undefined'
4119 && window.setImmediate;
4120 var canPost = typeof window !== 'undefined'
4121 && window.postMessage && window.addEventListener
4122 ;
4123
4124 if (canSetImmediate) {
4125 return function (f) { return window.setImmediate(f) };
4126 }
4127
4128 if (canPost) {
4129 var queue = [];
4130 window.addEventListener('message', function (ev) {
4131 if (ev.source === window && ev.data === 'process-tick') {
4132 ev.stopPropagation();
4133 if (queue.length > 0) {
4134 var fn = queue.shift();
4135 fn();
4136 }
4137 }
4138 }, true);
4139
4140 return function nextTick(fn) {
4141 queue.push(fn);
4142 window.postMessage('process-tick', '*');
4143 };
4144 }
4145
4146 return function nextTick(fn) {
4147 setTimeout(fn, 0);
4148 };
4149})();
4150
4151process.title = 'browser';
4152process.browser = true;
4153process.env = {};
4154process.argv = [];
4155
4156process.binding = function (name) {
4157 throw new Error('process.binding is not supported');
4158}
4159
4160// TODO(shtylman)
4161process.cwd = function () { return '/' };
4162process.chdir = function (dir) {
4163 throw new Error('process.chdir is not supported');
4164};
4165
4166},{}]},{},[1])
4167;
\No newline at end of file