UNPKG

45.4 kBJavaScriptView Raw
1// Browser bundle of nunjucks 1.0.7 (slim, only works with precompiled templates)
2
3(function() {
4var modules = {};
5(function() {
6
7// A simple class system, more documentation to come
8
9function extend(cls, name, props) {
10 // This does that same thing as Object.create, but with support for IE8
11 var F = function() {};
12 F.prototype = cls.prototype;
13 var prototype = new F();
14
15 var fnTest = /xyz/.test(function(){ xyz; }) ? /\bparent\b/ : /.*/;
16 props = props || {};
17
18 for(var k in props) {
19 var src = props[k];
20 var parent = prototype[k];
21
22 if(typeof parent == "function" &&
23 typeof src == "function" &&
24 fnTest.test(src)) {
25 prototype[k] = (function (src, parent) {
26 return function() {
27 // Save the current parent method
28 var tmp = this.parent;
29
30 // Set parent to the previous method, call, and restore
31 this.parent = parent;
32 var res = src.apply(this, arguments);
33 this.parent = tmp;
34
35 return res;
36 };
37 })(src, parent);
38 }
39 else {
40 prototype[k] = src;
41 }
42 }
43
44 prototype.typename = name;
45
46 var new_cls = function() {
47 if(prototype.init) {
48 prototype.init.apply(this, arguments);
49 }
50 };
51
52 new_cls.prototype = prototype;
53 new_cls.prototype.constructor = new_cls;
54
55 new_cls.extend = function(name, props) {
56 if(typeof name == "object") {
57 props = name;
58 name = "anonymous";
59 }
60 return extend(new_cls, name, props);
61 };
62
63 return new_cls;
64}
65
66modules['object'] = extend(Object, "Object", {});
67})();
68(function() {
69var ArrayProto = Array.prototype;
70var ObjProto = Object.prototype;
71
72var escapeMap = {
73 '&': '&',
74 '"': '"',
75 "'": ''',
76 "<": '&lt;',
77 ">": '&gt;'
78};
79
80var escapeRegex = /[&"'<>]/g;
81
82var lookupEscape = function(ch) {
83 return escapeMap[ch];
84};
85
86var exports = modules['lib'] = {};
87
88exports.withPrettyErrors = function(path, withInternals, func) {
89 try {
90 return func();
91 } catch (e) {
92 if (!e.Update) {
93 // not one of ours, cast it
94 e = new exports.TemplateError(e);
95 }
96 e.Update(path);
97
98 // Unless they marked the dev flag, show them a trace from here
99 if (!withInternals) {
100 var old = e;
101 e = new Error(old.message);
102 e.name = old.name;
103 }
104
105 throw e;
106 }
107};
108
109exports.TemplateError = function(message, lineno, colno) {
110 var err = this;
111
112 if (message instanceof Error) { // for casting regular js errors
113 err = message;
114 message = message.name + ": " + message.message;
115 } else {
116 if(Error.captureStackTrace) {
117 Error.captureStackTrace(err);
118 }
119 }
120
121 err.name = 'Template render error';
122 err.message = message;
123 err.lineno = lineno;
124 err.colno = colno;
125 err.firstUpdate = true;
126
127 err.Update = function(path) {
128 var message = "(" + (path || "unknown path") + ")";
129
130 // only show lineno + colno next to path of template
131 // where error occurred
132 if (this.firstUpdate) {
133 if(this.lineno && this.colno) {
134 message += ' [Line ' + this.lineno + ', Column ' + this.colno + ']';
135 }
136 else if(this.lineno) {
137 message += ' [Line ' + this.lineno + ']';
138 }
139 }
140
141 message += '\n ';
142 if (this.firstUpdate) {
143 message += ' ';
144 }
145
146 this.message = message + (this.message || '');
147 this.firstUpdate = false;
148 return this;
149 };
150
151 return err;
152};
153
154exports.TemplateError.prototype = Error.prototype;
155
156exports.escape = function(val) {
157 return val.replace(escapeRegex, lookupEscape);
158};
159
160exports.isFunction = function(obj) {
161 return ObjProto.toString.call(obj) == '[object Function]';
162};
163
164exports.isArray = Array.isArray || function(obj) {
165 return ObjProto.toString.call(obj) == '[object Array]';
166};
167
168exports.isString = function(obj) {
169 return ObjProto.toString.call(obj) == '[object String]';
170};
171
172exports.isObject = function(obj) {
173 return ObjProto.toString.call(obj) == '[object Object]';
174};
175
176exports.groupBy = function(obj, val) {
177 var result = {};
178 var iterator = exports.isFunction(val) ? val : function(obj) { return obj[val]; };
179 for(var i=0; i<obj.length; i++) {
180 var value = obj[i];
181 var key = iterator(value, i);
182 (result[key] || (result[key] = [])).push(value);
183 }
184 return result;
185};
186
187exports.toArray = function(obj) {
188 return Array.prototype.slice.call(obj);
189};
190
191exports.without = function(array) {
192 var result = [];
193 if (!array) {
194 return result;
195 }
196 var index = -1,
197 length = array.length,
198 contains = exports.toArray(arguments).slice(1);
199
200 while(++index < length) {
201 if(contains.indexOf(array[index]) === -1) {
202 result.push(array[index]);
203 }
204 }
205 return result;
206};
207
208exports.extend = function(obj, obj2) {
209 for(var k in obj2) {
210 obj[k] = obj2[k];
211 }
212 return obj;
213};
214
215exports.repeat = function(char_, n) {
216 var str = '';
217 for(var i=0; i<n; i++) {
218 str += char_;
219 }
220 return str;
221};
222
223exports.each = function(obj, func, context) {
224 if(obj == null) {
225 return;
226 }
227
228 if(ArrayProto.each && obj.each == ArrayProto.each) {
229 obj.forEach(func, context);
230 }
231 else if(obj.length === +obj.length) {
232 for(var i=0, l=obj.length; i<l; i++) {
233 func.call(context, obj[i], i, obj);
234 }
235 }
236};
237
238exports.map = function(obj, func) {
239 var results = [];
240 if(obj == null) {
241 return results;
242 }
243
244 if(ArrayProto.map && obj.map === ArrayProto.map) {
245 return obj.map(func);
246 }
247
248 for(var i=0; i<obj.length; i++) {
249 results[results.length] = func(obj[i], i);
250 }
251
252 if(obj.length === +obj.length) {
253 results.length = obj.length;
254 }
255
256 return results;
257};
258
259exports.asyncIter = function(arr, iter, cb) {
260 var i = -1;
261
262 function next() {
263 i++;
264
265 if(i < arr.length) {
266 iter(arr[i], i, next, cb);
267 }
268 else {
269 cb();
270 }
271 }
272
273 next();
274};
275
276exports.asyncFor = function(obj, iter, cb) {
277 var keys = exports.keys(obj);
278 var len = keys.length;
279 var i = -1;
280
281 function next() {
282 i++;
283 var k = keys[i];
284
285 if(i < len) {
286 iter(k, obj[k], i, len, next);
287 }
288 else {
289 cb();
290 }
291 }
292
293 next();
294};
295
296if(!Array.prototype.indexOf) {
297 Array.prototype.indexOf = function(array, searchElement /*, fromIndex */) {
298 if (array == null) {
299 throw new TypeError();
300 }
301 var t = Object(array);
302 var len = t.length >>> 0;
303 if (len === 0) {
304 return -1;
305 }
306 var n = 0;
307 if (arguments.length > 2) {
308 n = Number(arguments[2]);
309 if (n != n) { // shortcut for verifying if it's NaN
310 n = 0;
311 } else if (n != 0 && n != Infinity && n != -Infinity) {
312 n = (n > 0 || -1) * Math.floor(Math.abs(n));
313 }
314 }
315 if (n >= len) {
316 return -1;
317 }
318 var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
319 for (; k < len; k++) {
320 if (k in t && t[k] === searchElement) {
321 return k;
322 }
323 }
324 return -1;
325 };
326}
327
328if(!Array.prototype.map) {
329 Array.prototype.map = function() {
330 throw new Error("map is unimplemented for this js engine");
331 };
332}
333
334exports.keys = function(obj) {
335 if(Object.prototype.keys) {
336 return obj.keys();
337 }
338 else {
339 var keys = [];
340 for(var k in obj) {
341 if(obj.hasOwnProperty(k)) {
342 keys.push(k);
343 }
344 }
345 return keys;
346 }
347}
348})();
349(function() {
350var lib = modules["lib"];
351var Obj = modules["object"];
352
353// Frames keep track of scoping both at compile-time and run-time so
354// we know how to access variables. Block tags can introduce special
355// variables, for example.
356var Frame = Obj.extend({
357 init: function(parent) {
358 this.variables = {};
359 this.parent = parent;
360 },
361
362 set: function(name, val, resolveUp) {
363 // Allow variables with dots by automatically creating the
364 // nested structure
365 var parts = name.split('.');
366 var obj = this.variables;
367 var frame = this;
368
369 if(resolveUp) {
370 if((frame = this.resolve(parts[0]))) {
371 frame.set(name, val);
372 return;
373 }
374 frame = this;
375 }
376
377 for(var i=0; i<parts.length - 1; i++) {
378 var id = parts[i];
379
380 if(!obj[id]) {
381 obj[id] = {};
382 }
383 obj = obj[id];
384 }
385
386 obj[parts[parts.length - 1]] = val;
387 },
388
389 get: function(name) {
390 var val = this.variables[name];
391 if(val !== undefined && val !== null) {
392 return val;
393 }
394 return null;
395 },
396
397 lookup: function(name) {
398 var p = this.parent;
399 var val = this.variables[name];
400 if(val !== undefined && val !== null) {
401 return val;
402 }
403 return p && p.lookup(name);
404 },
405
406 resolve: function(name) {
407 var p = this.parent;
408 var val = this.variables[name];
409 if(val != null) {
410 return this;
411 }
412 return p && p.resolve(name);
413 },
414
415 push: function() {
416 return new Frame(this);
417 },
418
419 pop: function() {
420 return this.parent;
421 }
422});
423
424function makeMacro(argNames, kwargNames, func) {
425 return function() {
426 var argCount = numArgs(arguments);
427 var args;
428 var kwargs = getKeywordArgs(arguments);
429
430 if(argCount > argNames.length) {
431 args = Array.prototype.slice.call(arguments, 0, argNames.length);
432
433 // Positional arguments that should be passed in as
434 // keyword arguments (essentially default values)
435 var vals = Array.prototype.slice.call(arguments, args.length, argCount);
436 for(var i=0; i<vals.length; i++) {
437 if(i < kwargNames.length) {
438 kwargs[kwargNames[i]] = vals[i];
439 }
440 }
441
442 args.push(kwargs);
443 }
444 else if(argCount < argNames.length) {
445 args = Array.prototype.slice.call(arguments, 0, argCount);
446
447 for(var i=argCount; i<argNames.length; i++) {
448 var arg = argNames[i];
449
450 // Keyword arguments that should be passed as
451 // positional arguments, i.e. the caller explicitly
452 // used the name of a positional arg
453 args.push(kwargs[arg]);
454 delete kwargs[arg];
455 }
456
457 args.push(kwargs);
458 }
459 else {
460 args = arguments;
461 }
462
463 return func.apply(this, args);
464 };
465}
466
467function makeKeywordArgs(obj) {
468 obj.__keywords = true;
469 return obj;
470}
471
472function getKeywordArgs(args) {
473 var len = args.length;
474 if(len) {
475 var lastArg = args[len - 1];
476 if(lastArg && lastArg.hasOwnProperty('__keywords')) {
477 return lastArg;
478 }
479 }
480 return {};
481}
482
483function numArgs(args) {
484 var len = args.length;
485 if(len === 0) {
486 return 0;
487 }
488
489 var lastArg = args[len - 1];
490 if(lastArg && lastArg.hasOwnProperty('__keywords')) {
491 return len - 1;
492 }
493 else {
494 return len;
495 }
496}
497
498// A SafeString object indicates that the string should not be
499// autoescaped. This happens magically because autoescaping only
500// occurs on primitive string objects.
501function SafeString(val) {
502 if(typeof val != 'string') {
503 return val;
504 }
505
506 this.val = val;
507}
508
509SafeString.prototype = Object.create(String.prototype);
510SafeString.prototype.valueOf = function() {
511 return this.val;
512};
513SafeString.prototype.toString = function() {
514 return this.val;
515};
516
517function copySafeness(dest, target) {
518 if(dest instanceof SafeString) {
519 return new SafeString(target);
520 }
521 return target.toString();
522}
523
524function markSafe(val) {
525 var type = typeof val;
526
527 if(type === 'string') {
528 return new SafeString(val);
529 }
530 else if(type !== 'function') {
531 return val;
532 }
533 else {
534 return function() {
535 var ret = val.apply(this, arguments);
536
537 if(typeof ret === 'string') {
538 return new SafeString(ret);
539 }
540
541 return ret;
542 };
543 }
544}
545
546function suppressValue(val, autoescape) {
547 val = (val !== undefined && val !== null) ? val : "";
548
549 if(autoescape && typeof val === "string") {
550 val = lib.escape(val);
551 }
552
553 return val;
554}
555
556function memberLookup(obj, val) {
557 obj = obj || {};
558
559 if(typeof obj[val] === 'function') {
560 return function() {
561 return obj[val].apply(obj, arguments);
562 };
563 }
564
565 return obj[val];
566}
567
568function callWrap(obj, name, args) {
569 if(!obj) {
570 throw new Error('Unable to call `' + name + '`, which is undefined or falsey');
571 }
572 else if(typeof obj !== 'function') {
573 throw new Error('Unable to call `' + name + '`, which is not a function');
574 }
575
576 return obj.apply(this, args);
577}
578
579function contextOrFrameLookup(context, frame, name) {
580 var val = frame.lookup(name);
581 return (val !== undefined && val !== null) ?
582 val :
583 context.lookup(name);
584}
585
586function handleError(error, lineno, colno) {
587 if(error.lineno) {
588 return error;
589 }
590 else {
591 return new lib.TemplateError(error, lineno, colno);
592 }
593}
594
595function asyncEach(arr, dimen, iter, cb) {
596 if(lib.isArray(arr)) {
597 var len = arr.length;
598
599 lib.asyncIter(arr, function(item, i, next) {
600 switch(dimen) {
601 case 1: iter(item, i, len, next); break;
602 case 2: iter(item[0], item[1], i, len, next); break;
603 case 3: iter(item[0], item[1], item[2], i, len, next); break;
604 default:
605 item.push(i, next);
606 iter.apply(this, item);
607 }
608 }, cb);
609 }
610 else {
611 lib.asyncFor(arr, function(key, val, i, len, next) {
612 iter(key, val, i, len, next);
613 }, cb);
614 }
615}
616
617function asyncAll(arr, dimen, func, cb) {
618 var finished = 0;
619 var len;
620 var outputArr;
621
622 function done(i, output) {
623 finished++;
624 outputArr[i] = output;
625
626 if(finished == len) {
627 cb(null, outputArr.join(''));
628 }
629 }
630
631 if(lib.isArray(arr)) {
632 len = arr.length;
633 outputArr = new Array(len);
634
635 if(len == 0) {
636 cb(null, '');
637 }
638 else {
639 for(var i=0; i<arr.length; i++) {
640 var item = arr[i];
641
642 switch(dimen) {
643 case 1: func(item, i, len, done); break;
644 case 2: func(item[0], item[1], i, len, done); break;
645 case 3: func(item[0], item[1], item[2], i, len, done); break;
646 default:
647 item.push(i, done);
648 func.apply(this, item);
649 }
650 }
651 }
652 }
653 else {
654 var keys = lib.keys(arr);
655 len = keys.length;
656 outputArr = new Array(len);
657
658 if(len == 0) {
659 cb(null, '');
660 }
661 else {
662 for(var i=0; i<keys.length; i++) {
663 var k = keys[i];
664 func(k, arr[k], i, len, done);
665 }
666 }
667 }
668}
669
670modules['runtime'] = {
671 Frame: Frame,
672 makeMacro: makeMacro,
673 makeKeywordArgs: makeKeywordArgs,
674 numArgs: numArgs,
675 suppressValue: suppressValue,
676 memberLookup: memberLookup,
677 contextOrFrameLookup: contextOrFrameLookup,
678 callWrap: callWrap,
679 handleError: handleError,
680 isArray: lib.isArray,
681 keys: lib.keys,
682 SafeString: SafeString,
683 copySafeness: copySafeness,
684 markSafe: markSafe,
685 asyncEach: asyncEach,
686 asyncAll: asyncAll
687};
688})();
689(function() {
690var Obj = modules["object"];
691var lib = modules["lib"];
692
693var Loader = Obj.extend({
694 on: function(name, func) {
695 this.listeners = this.listeners || {};
696 this.listeners[name] = this.listeners[name] || [];
697 this.listeners[name].push(func);
698 },
699
700 emit: function(name /*, arg1, arg2, ...*/) {
701 var args = Array.prototype.slice.call(arguments, 1);
702
703 if(this.listeners && this.listeners[name]) {
704 lib.each(this.listeners[name], function(listener) {
705 listener.apply(null, args);
706 });
707 }
708 }
709});
710
711modules['loader'] = Loader;
712})();
713(function() {
714var Loader = modules["loader"];
715
716var WebLoader = Loader.extend({
717 init: function(baseURL, neverUpdate) {
718 // It's easy to use precompiled templates: just include them
719 // before you configure nunjucks and this will automatically
720 // pick it up and use it
721 this.precompiled = window.nunjucksPrecompiled || {};
722
723 this.baseURL = baseURL || '';
724 this.neverUpdate = neverUpdate;
725 },
726
727 getSource: function(name) {
728 if(this.precompiled[name]) {
729 return {
730 src: { type: "code",
731 obj: this.precompiled[name] },
732 path: name
733 };
734 }
735 else {
736 var src = this.fetch(this.baseURL + '/' + name);
737 if(!src) {
738 return null;
739 }
740
741 return { src: src,
742 path: name,
743 noCache: !this.neverUpdate };
744 }
745 },
746
747 fetch: function(url, callback) {
748 // Only in the browser please
749 var ajax;
750 var loading = true;
751 var src;
752
753 if(window.XMLHttpRequest) { // Mozilla, Safari, ...
754 ajax = new XMLHttpRequest();
755 }
756 else if(window.ActiveXObject) { // IE 8 and older
757 ajax = new ActiveXObject("Microsoft.XMLHTTP");
758 }
759
760 ajax.onreadystatechange = function() {
761 if(ajax.readyState === 4 && (ajax.status === 0 || ajax.status === 200) && loading) {
762 loading = false;
763 src = ajax.responseText;
764 }
765 };
766
767 url += (url.indexOf('?') === -1 ? '?' : '&') + 's=' +
768 (new Date().getTime());
769
770 // Synchronous because this API shouldn't be used in
771 // production (pre-load compiled templates instead)
772 ajax.open('GET', url, false);
773 ajax.send();
774
775 return src;
776 }
777});
778
779modules['web-loaders'] = {
780 WebLoader: WebLoader
781};
782})();
783(function() {
784if(typeof window === 'undefined' || window !== this) {
785 modules['loaders'] = modules["node-loaders"];
786}
787else {
788 modules['loaders'] = modules["web-loaders"];
789}
790})();
791(function() {
792var lib = modules["lib"];
793var r = modules["runtime"];
794
795var filters = {
796 abs: function(n) {
797 return Math.abs(n);
798 },
799
800 batch: function(arr, linecount, fill_with) {
801 var res = [];
802 var tmp = [];
803
804 for(var i=0; i<arr.length; i++) {
805 if(i % linecount === 0 && tmp.length) {
806 res.push(tmp);
807 tmp = [];
808 }
809
810 tmp.push(arr[i]);
811 }
812
813 if(tmp.length) {
814 if(fill_with) {
815 for(var i=tmp.length; i<linecount; i++) {
816 tmp.push(fill_with);
817 }
818 }
819
820 res.push(tmp);
821 }
822
823 return res;
824 },
825
826 capitalize: function(str) {
827 var ret = str.toLowerCase();
828 return r.copySafeness(str, ret.charAt(0).toUpperCase() + ret.slice(1));
829 },
830
831 center: function(str, width) {
832 width = width || 80;
833
834 if(str.length >= width) {
835 return str;
836 }
837
838 var spaces = width - str.length;
839 var pre = lib.repeat(" ", spaces/2 - spaces % 2);
840 var post = lib.repeat(" ", spaces/2);
841 return r.copySafeness(str, pre + str + post);
842 },
843
844 'default': function(val, def) {
845 return val ? val : def;
846 },
847
848 dictsort: function(val, case_sensitive, by) {
849 if (!lib.isObject(val)) {
850 throw new lib.TemplateError("dictsort filter: val must be an object");
851 }
852
853 var array = [];
854 for (var k in val) {
855 // deliberately include properties from the object's prototype
856 array.push([k,val[k]]);
857 }
858
859 var si;
860 if (by === undefined || by === "key") {
861 si = 0;
862 } else if (by === "value") {
863 si = 1;
864 } else {
865 throw new lib.TemplateError(
866 "dictsort filter: You can only sort by either key or value");
867 }
868
869 array.sort(function(t1, t2) {
870 var a = t1[si];
871 var b = t2[si];
872
873 if (!case_sensitive) {
874 if (lib.isString(a)) {
875 a = a.toUpperCase();
876 }
877 if (lib.isString(b)) {
878 b = b.toUpperCase();
879 }
880 }
881
882 return a > b ? 1 : (a == b ? 0 : -1);
883 });
884
885 return array;
886 },
887
888 escape: function(str) {
889 if(typeof str == 'string' ||
890 str instanceof r.SafeString) {
891 return lib.escape(str);
892 }
893 return str;
894 },
895
896 safe: function(str) {
897 return r.markSafe(str);
898 },
899
900 first: function(arr) {
901 return arr[0];
902 },
903
904 groupby: function(arr, attr) {
905 return lib.groupBy(arr, attr);
906 },
907
908 indent: function(str, width, indentfirst) {
909 width = width || 4;
910 var res = '';
911 var lines = str.split('\n');
912 var sp = lib.repeat(' ', width);
913
914 for(var i=0; i<lines.length; i++) {
915 if(i == 0 && !indentfirst) {
916 res += lines[i] + '\n';
917 }
918 else {
919 res += sp + lines[i] + '\n';
920 }
921 }
922
923 return r.copySafeness(str, res);
924 },
925
926 join: function(arr, del, attr) {
927 del = del || '';
928
929 if(attr) {
930 arr = lib.map(arr, function(v) {
931 return v[attr];
932 });
933 }
934
935 return arr.join(del);
936 },
937
938 last: function(arr) {
939 return arr[arr.length-1];
940 },
941
942 length: function(arr) {
943 return arr !== undefined ? arr.length : 0;
944 },
945
946 list: function(val) {
947 if(lib.isString(val)) {
948 return val.split('');
949 }
950 else if(lib.isObject(val)) {
951 var keys = [];
952
953 if(Object.keys) {
954 keys = Object.keys(val);
955 }
956 else {
957 for(var k in val) {
958 keys.push(k);
959 }
960 }
961
962 return lib.map(keys, function(k) {
963 return { key: k,
964 value: val[k] };
965 });
966 }
967 else {
968 throw new lib.TemplateError("list filter: type not iterable");
969 }
970 },
971
972 lower: function(str) {
973 return str.toLowerCase();
974 },
975
976 random: function(arr) {
977 return arr[Math.floor(Math.random() * arr.length)];
978 },
979
980 replace: function(str, old, new_, maxCount) {
981 var res = str;
982 var last = res;
983 var count = 1;
984 res = res.replace(old, new_);
985
986 while(last != res) {
987 if(count >= maxCount) {
988 break;
989 }
990
991 last = res;
992 res = res.replace(old, new_);
993 count++;
994 }
995
996 return r.copySafeness(str, res);
997 },
998
999 reverse: function(val) {
1000 var arr;
1001 if(lib.isString(val)) {
1002 arr = filters.list(val);
1003 }
1004 else {
1005 // Copy it
1006 arr = lib.map(val, function(v) { return v; });
1007 }
1008
1009 arr.reverse();
1010
1011 if(lib.isString(val)) {
1012 return r.copySafeness(val, arr.join(''));
1013 }
1014 return arr;
1015 },
1016
1017 round: function(val, precision, method) {
1018 precision = precision || 0;
1019 var factor = Math.pow(10, precision);
1020 var rounder;
1021
1022 if(method == 'ceil') {
1023 rounder = Math.ceil;
1024 }
1025 else if(method == 'floor') {
1026 rounder = Math.floor;
1027 }
1028 else {
1029 rounder = Math.round;
1030 }
1031
1032 return rounder(val * factor) / factor;
1033 },
1034
1035 slice: function(arr, slices, fillWith) {
1036 var sliceLength = Math.floor(arr.length / slices);
1037 var extra = arr.length % slices;
1038 var offset = 0;
1039 var res = [];
1040
1041 for(var i=0; i<slices; i++) {
1042 var start = offset + i * sliceLength;
1043 if(i < extra) {
1044 offset++;
1045 }
1046 var end = offset + (i + 1) * sliceLength;
1047
1048 var slice = arr.slice(start, end);
1049 if(fillWith && i >= extra) {
1050 slice.push(fillWith);
1051 }
1052 res.push(slice);
1053 }
1054
1055 return res;
1056 },
1057
1058 sort: function(arr, reverse, caseSens, attr) {
1059 // Copy it
1060 arr = lib.map(arr, function(v) { return v; });
1061
1062 arr.sort(function(a, b) {
1063 var x, y;
1064
1065 if(attr) {
1066 x = a[attr];
1067 y = b[attr];
1068 }
1069 else {
1070 x = a;
1071 y = b;
1072 }
1073
1074 if(!caseSens && lib.isString(x) && lib.isString(y)) {
1075 x = x.toLowerCase();
1076 y = y.toLowerCase();
1077 }
1078
1079 if(x < y) {
1080 return reverse ? 1 : -1;
1081 }
1082 else if(x > y) {
1083 return reverse ? -1: 1;
1084 }
1085 else {
1086 return 0;
1087 }
1088 });
1089
1090 return arr;
1091 },
1092
1093 string: function(obj) {
1094 return r.copySafeness(obj, obj);
1095 },
1096
1097 title: function(str) {
1098 var words = str.split(' ');
1099 for(var i = 0; i < words.length; i++) {
1100 words[i] = filters.capitalize(words[i]);
1101 }
1102 return r.copySafeness(str, words.join(' '));
1103 },
1104
1105 trim: function(str) {
1106 return r.copySafeness(str, str.replace(/^\s*|\s*$/g, ''));
1107 },
1108
1109 truncate: function(input, length, killwords, end) {
1110 var orig = input;
1111 length = length || 255;
1112
1113 if (input.length <= length)
1114 return input;
1115
1116 if (killwords) {
1117 input = input.substring(0, length);
1118 } else {
1119 var idx = input.lastIndexOf(' ', length);
1120 if(idx === -1) {
1121 idx = length;
1122 }
1123
1124 input = input.substring(0, idx);
1125 }
1126
1127 input += (end !== undefined && end !== null) ? end : '...';
1128 return r.copySafeness(orig, input);
1129 },
1130
1131 upper: function(str) {
1132 return str.toUpperCase();
1133 },
1134
1135 urlencode: function(obj) {
1136 var enc = encodeURIComponent;
1137 if (lib.isString(obj)) {
1138 return enc(obj);
1139 } else {
1140 var parts;
1141 if (lib.isArray(obj)) {
1142 parts = obj.map(function(item) {
1143 return enc(item[0]) + '=' + enc(item[1]);
1144 })
1145 } else {
1146 parts = [];
1147 for (var k in obj) {
1148 if (obj.hasOwnProperty(k)) {
1149 parts.push(enc(k) + '=' + enc(obj[k]));
1150 }
1151 }
1152 }
1153 return parts.join('&');
1154 }
1155 },
1156
1157 urlize: function(str, length, nofollow) {
1158 if (isNaN(length)) length = Infinity;
1159
1160 var noFollowAttr = (nofollow === true ? ' rel="nofollow"' : '');
1161
1162 // For the jinja regexp, see
1163 // https://github.com/mitsuhiko/jinja2/blob/f15b814dcba6aa12bc74d1f7d0c881d55f7126be/jinja2/utils.py#L20-L23
1164 var puncRE = /^(?:\(|<|&lt;)?(.*?)(?:\.|,|\)|\n|&gt;)?$/;
1165 // from http://blog.gerv.net/2011/05/html5_email_address_regexp/
1166 var emailRE = /^[\w.!#$%&'*+\-\/=?\^`{|}~]+@[a-z\d\-]+(\.[a-z\d\-]+)+$/i;
1167 var httpHttpsRE = /^https?:\/\/.*$/;
1168 var wwwRE = /^www\./;
1169 var tldRE = /\.(?:org|net|com)(?:\:|\/|$)/;
1170
1171 var words = str.split(/\s+/).filter(function(word) {
1172 // If the word has no length, bail. This can happen for str with
1173 // trailing whitespace.
1174 return word && word.length;
1175 }).map(function(word) {
1176 var matches = word.match(puncRE);
1177
1178 var possibleUrl = matches && matches[1] || word;
1179
1180 // url that starts with http or https
1181 if (httpHttpsRE.test(possibleUrl))
1182 return '<a href="' + possibleUrl + '"' + noFollowAttr + '>' + possibleUrl.substr(0, length) + '</a>';
1183
1184 // url that starts with www.
1185 if (wwwRE.test(possibleUrl))
1186 return '<a href="http://' + possibleUrl + '"' + noFollowAttr + '>' + possibleUrl.substr(0, length) + '</a>';
1187
1188 // an email address of the form username@domain.tld
1189 if (emailRE.test(possibleUrl))
1190 return '<a href="mailto:' + possibleUrl + '">' + possibleUrl + '</a>';
1191
1192 // url that ends in .com, .org or .net that is not an email address
1193 if (tldRE.test(possibleUrl))
1194 return '<a href="http://' + possibleUrl + '"' + noFollowAttr + '>' + possibleUrl.substr(0, length) + '</a>';
1195
1196 return possibleUrl;
1197
1198 });
1199
1200 return words.join(' ');
1201 },
1202
1203 wordcount: function(str) {
1204 var words = (str) ? str.match(/\w+/g) : null;
1205 return (words) ? words.length : null;
1206 },
1207
1208 'float': function(val, def) {
1209 var res = parseFloat(val);
1210 return isNaN(res) ? def : res;
1211 },
1212
1213 'int': function(val, def) {
1214 var res = parseInt(val, 10);
1215 return isNaN(res) ? def : res;
1216 }
1217};
1218
1219// Aliases
1220filters.d = filters['default'];
1221filters.e = filters.escape;
1222
1223modules['filters'] = filters;
1224})();
1225(function() {
1226
1227function cycler(items) {
1228 var index = -1;
1229 var current = null;
1230
1231 return {
1232 reset: function() {
1233 index = -1;
1234 current = null;
1235 },
1236
1237 next: function() {
1238 index++;
1239 if(index >= items.length) {
1240 index = 0;
1241 }
1242
1243 current = items[index];
1244 return current;
1245 }
1246 };
1247
1248}
1249
1250function joiner(sep) {
1251 sep = sep || ',';
1252 var first = true;
1253
1254 return function() {
1255 var val = first ? '' : sep;
1256 first = false;
1257 return val;
1258 };
1259}
1260
1261var globals = {
1262 range: function(start, stop, step) {
1263 if(!stop) {
1264 stop = start;
1265 start = 0;
1266 step = 1;
1267 }
1268 else if(!step) {
1269 step = 1;
1270 }
1271
1272 var arr = [];
1273 for(var i=start; i<stop; i+=step) {
1274 arr.push(i);
1275 }
1276 return arr;
1277 },
1278
1279 // lipsum: function(n, html, min, max) {
1280 // },
1281
1282 cycler: function() {
1283 return cycler(Array.prototype.slice.call(arguments));
1284 },
1285
1286 joiner: function(sep) {
1287 return joiner(sep);
1288 }
1289}
1290
1291modules['globals'] = globals;
1292})();
1293(function() {
1294var path = modules["path"];
1295var lib = modules["lib"];
1296var Obj = modules["object"];
1297var lexer = modules["lexer"];
1298var compiler = modules["compiler"];
1299var builtin_filters = modules["filters"];
1300var builtin_loaders = modules["loaders"];
1301var runtime = modules["runtime"];
1302var globals = modules["globals"];
1303var Frame = runtime.Frame;
1304
1305var Environment = Obj.extend({
1306 init: function(loaders, opts) {
1307 // The dev flag determines the trace that'll be shown on errors.
1308 // If set to true, returns the full trace from the error point,
1309 // otherwise will return trace starting from Template.render
1310 // (the full trace from within nunjucks may confuse developers using
1311 // the library)
1312 // defaults to false
1313 opts = opts || {};
1314 this.dev = !!opts.dev;
1315
1316 // The autoescape flag sets global autoescaping. If true,
1317 // every string variable will be escaped by default.
1318 // If false, strings can be manually escaped using the `escape` filter.
1319 // defaults to false
1320 this.autoesc = !!opts.autoescape;
1321
1322 if(!loaders) {
1323 // The filesystem loader is only available client-side
1324 if(builtin_loaders.FileSystemLoader) {
1325 this.loaders = [new builtin_loaders.FileSystemLoader('views')];
1326 }
1327 else {
1328 this.loaders = [new builtin_loaders.WebLoader('/views')];
1329 }
1330 }
1331 else {
1332 this.loaders = lib.isArray(loaders) ? loaders : [loaders];
1333 }
1334
1335 this.initCache();
1336 this.filters = {};
1337 this.asyncFilters = [];
1338 this.extensions = {};
1339 this.extensionsList = [];
1340
1341 if(opts.tags) {
1342 lexer.setTags(opts.tags);
1343 }
1344
1345 for(var name in builtin_filters) {
1346 this.addFilter(name, builtin_filters[name]);
1347 }
1348 },
1349
1350 initCache: function() {
1351 // Caching and cache busting
1352 var cache = {};
1353
1354 lib.each(this.loaders, function(loader) {
1355 if(typeof loader.on === 'function'){
1356 loader.on('update', function(template) {
1357 cache[template] = null;
1358 });
1359 }
1360 });
1361
1362 this.cache = cache;
1363 },
1364
1365 addExtension: function(name, extension) {
1366 extension._name = name;
1367 this.extensions[name] = extension;
1368 this.extensionsList.push(extension);
1369 },
1370
1371 getExtension: function(name) {
1372 return this.extensions[name];
1373 },
1374
1375 addGlobal: function(name, value) {
1376 globals[name] = value;
1377 },
1378
1379 addFilter: function(name, func, async) {
1380 var wrapped = func;
1381
1382 if(async) {
1383 this.asyncFilters.push(name);
1384 }
1385 this.filters[name] = wrapped;
1386 },
1387
1388 getFilter: function(name) {
1389 if(!this.filters[name]) {
1390 throw new Error('filter not found: ' + name);
1391 }
1392 return this.filters[name];
1393 },
1394
1395 getTemplate: function(name, eagerCompile, cb) {
1396 if(name && name.raw) {
1397 // this fixes autoescape for templates referenced in symbols
1398 name = name.raw;
1399 }
1400
1401 if(lib.isFunction(eagerCompile)) {
1402 cb = eagerCompile;
1403 eagerCompile = false;
1404 }
1405
1406 if(typeof name !== 'string') {
1407 throw new Error('template names must be a string: ' + name);
1408 }
1409
1410 var tmpl = this.cache[name];
1411
1412 if(tmpl) {
1413 if(eagerCompile) {
1414 tmpl.compile();
1415 }
1416
1417 if(cb) {
1418 cb(null, tmpl);
1419 }
1420 else {
1421 return tmpl;
1422 }
1423 } else {
1424 var syncResult;
1425
1426 lib.asyncIter(this.loaders, function(loader, i, next, done) {
1427 function handle(src) {
1428 if(src) {
1429 done(src);
1430 }
1431 else {
1432 next();
1433 }
1434 }
1435
1436 if(loader.async) {
1437 loader.getSource(name, function(err, src) {
1438 if(err) { throw err; }
1439 handle(src);
1440 });
1441 }
1442 else {
1443 handle(loader.getSource(name));
1444 }
1445 }, function(info) {
1446 if(!info) {
1447 var err = new Error('template not found: ' + name);
1448 if(cb) {
1449 cb(err);
1450 }
1451 else {
1452 throw err;
1453 }
1454 }
1455 else {
1456 var tmpl = new Template(info.src, this,
1457 info.path, eagerCompile);
1458
1459 if(!info.noCache) {
1460 this.cache[name] = tmpl;
1461 }
1462
1463 if(cb) {
1464 cb(null, tmpl);
1465 }
1466 else {
1467 syncResult = tmpl;
1468 }
1469 }
1470 }.bind(this));
1471
1472 return syncResult;
1473 }
1474 },
1475
1476 express: function(app) {
1477 var env = this;
1478
1479 function NunjucksView(name, opts) {
1480 this.name = name;
1481 this.path = name;
1482 this.defaultEngine = opts.defaultEngine;
1483 this.ext = path.extname(name);
1484 if (!this.ext && !this.defaultEngine) throw new Error('No default engine was specified and no extension was provided.');
1485 if (!this.ext) this.name += (this.ext = ('.' !== this.defaultEngine[0] ? '.' : '') + this.defaultEngine);
1486 }
1487
1488 NunjucksView.prototype.render = function(opts, cb) {
1489 env.render(this.name, opts, cb);
1490 };
1491
1492 app.set('view', NunjucksView);
1493 },
1494
1495 render: function(name, ctx, cb) {
1496 if(lib.isFunction(ctx)) {
1497 cb = ctx;
1498 ctx = null;
1499 }
1500
1501 // We support a synchronous API to make it easier to migrate
1502 // existing code to async. This works because if you don't do
1503 // anything async work, the whole thing is actually run
1504 // synchronously.
1505 var syncResult = null;
1506
1507 this.getTemplate(name, function(err, tmpl) {
1508 if(err && cb) {
1509 cb(err);
1510 }
1511 else if(err) {
1512 throw err;
1513 }
1514 else {
1515 tmpl.render(ctx, cb || function(err, res) {
1516 if(err) { throw err; }
1517 syncResult = res;
1518 });
1519 }
1520 });
1521
1522 return syncResult;
1523 },
1524
1525 renderString: function(src, ctx, cb) {
1526 var tmpl = new Template(src, this);
1527 return tmpl.render(ctx, cb);
1528 }
1529});
1530
1531var Context = Obj.extend({
1532 init: function(ctx, blocks) {
1533 this.ctx = ctx;
1534 this.blocks = {};
1535 this.exported = [];
1536
1537 for(var name in blocks) {
1538 this.addBlock(name, blocks[name]);
1539 }
1540 },
1541
1542 lookup: function(name) {
1543 // This is one of the most called functions, so optimize for
1544 // the typical case where the name isn't in the globals
1545 if(name in globals && !(name in this.ctx)) {
1546 return globals[name];
1547 }
1548 else {
1549 return this.ctx[name];
1550 }
1551 },
1552
1553 setVariable: function(name, val) {
1554 this.ctx[name] = val;
1555 },
1556
1557 getVariables: function() {
1558 return this.ctx;
1559 },
1560
1561 addBlock: function(name, block) {
1562 this.blocks[name] = this.blocks[name] || [];
1563 this.blocks[name].push(block);
1564 },
1565
1566 getBlock: function(name) {
1567 if(!this.blocks[name]) {
1568 throw new Error('unknown block "' + name + '"');
1569 }
1570
1571 return this.blocks[name][0];
1572 },
1573
1574 getSuper: function(env, name, block, frame, runtime, cb) {
1575 var idx = (this.blocks[name] || []).indexOf(block);
1576 var blk = this.blocks[name][idx + 1];
1577 var context = this;
1578
1579 if(idx == -1 || !blk) {
1580 throw new Error('no super block available for "' + name + '"');
1581 }
1582
1583 blk(env, context, frame, runtime, cb);
1584 },
1585
1586 addExport: function(name) {
1587 this.exported.push(name);
1588 },
1589
1590 getExported: function() {
1591 var exported = {};
1592 for(var i=0; i<this.exported.length; i++) {
1593 var name = this.exported[i];
1594 exported[name] = this.ctx[name];
1595 }
1596 return exported;
1597 }
1598});
1599
1600var Template = Obj.extend({
1601 init: function (src, env, path, eagerCompile) {
1602 this.env = env || new Environment();
1603
1604 if(lib.isObject(src)) {
1605 switch(src.type) {
1606 case 'code': this.tmplProps = src.obj; break;
1607 case 'string': this.tmplStr = src.obj; break;
1608 }
1609 }
1610 else if(lib.isString(src)) {
1611 this.tmplStr = src;
1612 }
1613 else {
1614 throw new Error("src must be a string or an object describing " +
1615 "the source");
1616 }
1617
1618 this.path = path;
1619
1620 if(eagerCompile) {
1621 lib.withPrettyErrors(this.path,
1622 this.env.dev,
1623 this._compile.bind(this));
1624 }
1625 else {
1626 this.compiled = false;
1627 }
1628 },
1629
1630 render: function(ctx, frame, cb) {
1631 if (typeof ctx === 'function') {
1632 cb = ctx;
1633 ctx = {};
1634 }
1635 else if (typeof frame === 'function') {
1636 cb = frame;
1637 frame = null;
1638 }
1639
1640 return lib.withPrettyErrors(this.path, this.env.dev, function() {
1641 this.compile();
1642
1643 var context = new Context(ctx || {}, this.blocks);
1644 var syncResult = null;
1645
1646 this.rootRenderFunc(this.env,
1647 context,
1648 frame || new Frame(),
1649 runtime,
1650 cb || function(err, res) {
1651 if(err) { throw err; }
1652 syncResult = res;
1653 });
1654
1655 return syncResult;
1656 }.bind(this));
1657 },
1658
1659 getExported: function(cb) {
1660 this.compile();
1661
1662 // Run the rootRenderFunc to populate the context with exported vars
1663 var context = new Context({}, this.blocks);
1664 this.rootRenderFunc(this.env,
1665 context,
1666 new Frame(),
1667 runtime,
1668 function() {
1669 cb(null, context.getExported());
1670 });
1671 },
1672
1673 compile: function() {
1674 if(!this.compiled) {
1675 this._compile();
1676 }
1677 },
1678
1679 _compile: function() {
1680 var props;
1681
1682 if(this.tmplProps) {
1683 props = this.tmplProps;
1684 }
1685 else {
1686 var source = compiler.compile(this.tmplStr,
1687 this.env.asyncFilters,
1688 this.env.extensionsList,
1689 this.path);
1690 var func = new Function(source);
1691 props = func();
1692 }
1693
1694 this.blocks = this._getBlocks(props);
1695 this.rootRenderFunc = props.root;
1696 this.compiled = true;
1697 },
1698
1699 _getBlocks: function(props) {
1700 var blocks = {};
1701
1702 for(var k in props) {
1703 if(k.slice(0, 2) == 'b_') {
1704 blocks[k.slice(2)] = props[k];
1705 }
1706 }
1707
1708 return blocks;
1709 }
1710});
1711
1712// test code
1713// var src = '{% macro foo() %}{% include "include.html" %}{% endmacro %}{{ foo() }}';
1714// var env = new Environment(new builtin_loaders.FileSystemLoader('../tests/templates', true), { dev: true });
1715// console.log(env.renderString(src, { name: 'poop' }));
1716
1717modules['environment'] = {
1718 Environment: Environment,
1719 Template: Template
1720};
1721})();
1722var nunjucks;
1723
1724var lib = modules["lib"];
1725var env = modules["environment"];
1726var compiler = modules["compiler"];
1727var parser = modules["parser"];
1728var lexer = modules["lexer"];
1729var runtime = modules["runtime"];
1730var Loader = modules["loader"];
1731var loaders = modules["loaders"];
1732var precompile = modules["precompile"];
1733
1734nunjucks = {};
1735nunjucks.Environment = env.Environment;
1736nunjucks.Template = env.Template;
1737
1738nunjucks.Loader = Loader;
1739nunjucks.FileSystemLoader = loaders.FileSystemLoader;
1740nunjucks.WebLoader = loaders.WebLoader;
1741
1742nunjucks.compiler = compiler;
1743nunjucks.parser = parser;
1744nunjucks.lexer = lexer;
1745nunjucks.runtime = runtime;
1746
1747// A single instance of an environment, since this is so commonly used
1748
1749var e;
1750nunjucks.configure = function(templatesPath, opts) {
1751 opts = opts || {};
1752 if(lib.isObject(templatesPath)) {
1753 opts = templatesPath;
1754 templatesPath = null;
1755 }
1756
1757 var noWatch = 'watch' in opts ? !opts.watch : false;
1758 var loader = loaders.FileSystemLoader || loaders.WebLoader;
1759 e = new env.Environment(new loader(templatesPath, noWatch), opts);
1760
1761 if(opts && opts.express) {
1762 e.express(opts.express);
1763 }
1764
1765 return e;
1766};
1767
1768nunjucks.compile = function(src, env, path, eagerCompile) {
1769 if(!e) {
1770 nunjucks.configure();
1771 }
1772 return new nunjucks.Template(src, env, path, eagerCompile);
1773};
1774
1775nunjucks.render = function(name, ctx, cb) {
1776 if(!e) {
1777 nunjucks.configure();
1778 }
1779
1780 return e.render(name, ctx, cb);
1781};
1782
1783nunjucks.renderString = function(src, ctx, cb) {
1784 if(!e) {
1785 nunjucks.configure();
1786 }
1787
1788 return e.renderString(src, ctx, cb);
1789};
1790
1791if(precompile) {
1792 nunjucks.precompile = precompile.precompile;
1793 nunjucks.precompileString = precompile.precompileString;
1794}
1795
1796nunjucks.require = function(name) { return modules[name]; };
1797
1798if(typeof define === 'function' && define.amd) {
1799 define(function() { return nunjucks; });
1800}
1801else {
1802 window.nunjucks = nunjucks;
1803 if(typeof module !== 'undefined') module.exports = nunjucks;
1804}
1805
1806})();