UNPKG

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