UNPKG

25 kBJavaScriptView Raw
1(function (root, factory) {
2 /*global define*/
3 if (typeof define === 'function' && define.amd && define.amd.dust === true) {
4 define('dust.core', [], factory);
5 } else if (typeof exports === 'object') {
6 module.exports = factory();
7 } else {
8 root.dust = factory();
9 }
10}(this, function() {
11 var dust = {
12 "version": "2.6.2"
13 },
14 NONE = 'NONE', ERROR = 'ERROR', WARN = 'WARN', INFO = 'INFO', DEBUG = 'DEBUG',
15 EMPTY_FUNC = function() {};
16
17 dust.config = {
18 whitespace: false,
19 amd: false
20 };
21
22 // Directive aliases to minify code
23 dust._aliases = {
24 "write": "w",
25 "end": "e",
26 "map": "m",
27 "render": "r",
28 "reference": "f",
29 "section": "s",
30 "exists": "x",
31 "notexists": "nx",
32 "block": "b",
33 "partial": "p",
34 "helper": "h"
35 };
36
37 (function initLogging() {
38 /*global process, console*/
39 var loggingLevels = { DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3, NONE: 4 },
40 consoleLog,
41 log;
42
43 if (typeof console !== 'undefined' && console.log) {
44 consoleLog = console.log;
45 if(typeof consoleLog === 'function') {
46 log = function() {
47 consoleLog.apply(console, arguments);
48 };
49 } else {
50 log = function() {
51 consoleLog(Array.prototype.slice.apply(arguments).join(' '));
52 };
53 }
54 } else {
55 log = EMPTY_FUNC;
56 }
57
58 /**
59 * Filters messages based on `dust.debugLevel`.
60 * This default implementation will print to the console if it exists.
61 * @param {String|Error} message the message to print/throw
62 * @param {String} type the severity of the message(ERROR, WARN, INFO, or DEBUG)
63 * @public
64 */
65 dust.log = function(message, type) {
66 type = type || INFO;
67 if (loggingLevels[type] >= loggingLevels[dust.debugLevel]) {
68 log('[DUST:' + type + ']', message);
69 }
70 };
71
72 dust.debugLevel = NONE;
73 if(typeof process !== 'undefined' && process.env && /\bdust\b/.test(process.env.DEBUG)) {
74 dust.debugLevel = DEBUG;
75 }
76
77 }());
78
79 dust.helpers = {};
80
81 dust.cache = {};
82
83 dust.register = function(name, tmpl) {
84 if (!name) {
85 return;
86 }
87 dust.cache[name] = tmpl;
88 };
89
90 dust.render = function(name, context, callback) {
91 var chunk = new Stub(callback).head;
92 try {
93 dust.load(name, chunk, Context.wrap(context, name)).end();
94 } catch (err) {
95 chunk.setError(err);
96 }
97 };
98
99 dust.stream = function(name, context) {
100 var stream = new Stream(),
101 chunk = stream.head;
102 dust.nextTick(function() {
103 try {
104 dust.load(name, stream.head, Context.wrap(context, name)).end();
105 } catch (err) {
106 chunk.setError(err);
107 }
108 });
109 return stream;
110 };
111
112 dust.renderSource = function(source, context, callback) {
113 return dust.compileFn(source)(context, callback);
114 };
115
116 /**
117 * Compile a template to an invokable function.
118 * If `name` is provided, also registers the template under `name`.
119 * @param source {String} template source
120 * @param [name] {String} template name
121 * @return {Function} has the signature `fn(context, cb)`
122 */
123 dust.compileFn = function(source, name) {
124 name = name || null;
125 var tmpl = dust.loadSource(dust.compile(source, name));
126 return function(context, callback) {
127 var master = callback ? new Stub(callback) : new Stream();
128 dust.nextTick(function() {
129 if(typeof tmpl === 'function') {
130 tmpl(master.head, Context.wrap(context, name)).end();
131 } else {
132 dust.log(new Error('Template `' + name + '` could not be loaded'), ERROR);
133 }
134 });
135 return master;
136 };
137 };
138
139 dust.load = function(name, chunk, context) {
140 var tmpl = dust.cache[name];
141 if (tmpl) {
142 return tmpl(chunk, context);
143 } else {
144 if (dust.onLoad) {
145 return chunk.map(function(chunk) {
146 dust.onLoad(name, function(err, src) {
147 if (err) {
148 return chunk.setError(err);
149 }
150 if (!dust.cache[name]) {
151 dust.loadSource(dust.compile(src, name));
152 }
153 dust.cache[name](chunk, context).end();
154 });
155 });
156 }
157 return chunk.setError(new Error('Template Not Found: ' + name));
158 }
159 };
160
161 dust.loadSource = function(source, path) {
162 /*jshint evil:true*/
163 return eval(source);
164 };
165
166 if (Array.isArray) {
167 dust.isArray = Array.isArray;
168 } else {
169 dust.isArray = function(arr) {
170 return Object.prototype.toString.call(arr) === '[object Array]';
171 };
172 }
173
174 dust.nextTick = (function() {
175 return function(callback) {
176 setTimeout(callback,0);
177 };
178 } )();
179
180 /**
181 * Dust has its own rules for what is "empty"-- which is not the same as falsy.
182 * Empty arrays, null, and undefined are empty
183 */
184 dust.isEmpty = function(value) {
185 if (value === 0) {
186 return false;
187 }
188 if (dust.isArray(value) && !value.length) {
189 return true;
190 }
191 return !value;
192 };
193
194 dust.isEmptyObject = function(obj) {
195 var key;
196 if (obj === null) {
197 return false;
198 }
199 if (obj === undefined) {
200 return false;
201 }
202 if (obj.length > 0) {
203 return false;
204 }
205 for (key in obj) {
206 if (Object.prototype.hasOwnProperty.call(obj, key)) {
207 return false;
208 }
209 }
210 return true;
211 };
212
213 /**
214 * Decide somewhat-naively if something is a Thenable.
215 * @param elem {*} object to inspect
216 * @return {Boolean} is `elem` a Thenable?
217 */
218 dust.isThenable = function(elem) {
219 return elem &&
220 typeof elem === 'object' &&
221 typeof elem.then === 'function';
222 };
223
224 // apply the filter chain and return the output string
225 dust.filter = function(string, auto, filters) {
226 var i, len, name;
227 if (filters) {
228 for (i = 0, len = filters.length; i < len; i++) {
229 name = filters[i];
230 if (name === 's') {
231 auto = null;
232 }
233 else if (typeof dust.filters[name] === 'function') {
234 string = dust.filters[name](string);
235 }
236 else {
237 dust.log('Invalid filter `' + name + '`', WARN);
238 }
239 }
240 }
241 // by default always apply the h filter, unless asked to unescape with |s
242 if (auto) {
243 string = dust.filters[auto](string);
244 }
245 return string;
246 };
247
248 dust.filters = {
249 h: function(value) { return dust.escapeHtml(value); },
250 j: function(value) { return dust.escapeJs(value); },
251 u: encodeURI,
252 uc: encodeURIComponent,
253 js: function(value) { return dust.escapeJSON(value); },
254 jp: function(value) {
255 if (!JSON) {dust.log('JSON is undefined; could not parse `' + value + '`', WARN);
256 return value;
257 } else {
258 return JSON.parse(value);
259 }
260 }
261 };
262
263 function Context(stack, global, blocks, templateName) {
264 this.stack = stack;
265 this.global = global;
266 this.blocks = blocks;
267 this.templateName = templateName;
268 }
269
270 dust.makeBase = function(global) {
271 return new Context(new Stack(), global);
272 };
273
274 /**
275 * Factory function that creates a closure scope around a Thenable-callback.
276 * Returns a function that can be passed to a Thenable that will resume a
277 * Context lookup once the Thenable resolves with new data, adding that new
278 * data to the lookup stack.
279 */
280 function getWithResolvedData(ctx, cur, down) {
281 return function(data) {
282 return ctx.push(data)._get(cur, down);
283 };
284 }
285
286 Context.wrap = function(context, name) {
287 if (context instanceof Context) {
288 return context;
289 }
290 return new Context(new Stack(context), {}, null, name);
291 };
292
293 /**
294 * Public API for getting a value from the context.
295 * @method get
296 * @param {string|array} path The path to the value. Supported formats are:
297 * 'key'
298 * 'path.to.key'
299 * '.path.to.key'
300 * ['path', 'to', 'key']
301 * ['key']
302 * @param {boolean} [cur=false] Boolean which determines if the search should be limited to the
303 * current context (true), or if get should search in parent contexts as well (false).
304 * @public
305 * @returns {string|object}
306 */
307 Context.prototype.get = function(path, cur) {
308 if (typeof path === 'string') {
309 if (path[0] === '.') {
310 cur = true;
311 path = path.substr(1);
312 }
313 path = path.split('.');
314 }
315 return this._get(cur, path);
316 };
317
318 /**
319 * Get a value from the context
320 * @method _get
321 * @param {boolean} cur Get only from the current context
322 * @param {array} down An array of each step in the path
323 * @private
324 * @return {string | object}
325 */
326 Context.prototype._get = function(cur, down) {
327 var ctx = this.stack || {},
328 i = 1,
329 value, first, len, ctxThis, fn;
330
331 first = down[0];
332 len = down.length;
333
334 if (cur && len === 0) {
335 ctxThis = ctx;
336 ctx = ctx.head;
337 } else {
338 if (!cur) {
339 // Search up the stack for the first value
340 while (ctx) {
341 if (ctx.isObject) {
342 ctxThis = ctx.head;
343 value = ctx.head[first];
344 if (value !== undefined) {
345 break;
346 }
347 }
348 ctx = ctx.tail;
349 }
350
351 if (value !== undefined) {
352 ctx = value;
353 } else {
354 ctx = this.global ? this.global[first] : undefined;
355 }
356 } else if (ctx) {
357 // if scope is limited by a leading dot, don't search up the tree
358 if(ctx.head) {
359 ctx = ctx.head[first];
360 } else {
361 // context's head is empty, value we are searching for is not defined
362 ctx = undefined;
363 }
364 }
365
366 while (ctx && i < len) {
367 if (dust.isThenable(ctx)) {
368 // Bail early by returning a Thenable for the remainder of the search tree
369 return ctx.then(getWithResolvedData(this, cur, down.slice(i)));
370 }
371 ctxThis = ctx;
372 ctx = ctx[down[i]];
373 i++;
374 }
375 }
376
377 if (typeof ctx === 'function') {
378 fn = function() {
379 try {
380 return ctx.apply(ctxThis, arguments);
381 } catch (err) {
382 dust.log(err, ERROR);
383 throw err;
384 }
385 };
386 fn.__dustBody = !!ctx.__dustBody;
387 return fn;
388 } else {
389 if (ctx === undefined) {
390 dust.log('Cannot find reference `{' + down.join('.') + '}` in template `' + this.getTemplateName() + '`', INFO);
391 }
392 return ctx;
393 }
394 };
395
396 Context.prototype.getPath = function(cur, down) {
397 return this._get(cur, down);
398 };
399
400 Context.prototype.push = function(head, idx, len) {
401 return new Context(new Stack(head, this.stack, idx, len), this.global, this.blocks, this.getTemplateName());
402 };
403
404 Context.prototype.pop = function() {
405 var head = this.current();
406 this.stack = this.stack && this.stack.tail;
407 return head;
408 };
409
410 Context.prototype.rebase = function(head) {
411 return new Context(new Stack(head), this.global, this.blocks, this.getTemplateName());
412 };
413
414 Context.prototype.clone = function() {
415 var context = this.rebase();
416 context.stack = this.stack;
417 return context;
418 };
419
420 Context.prototype.current = function() {
421 return this.stack && this.stack.head;
422 };
423
424 Context.prototype.getBlock = function(key, chk, ctx) {
425 var blocks, len, fn;
426
427 if (typeof key === 'function') {
428 key = key(new Chunk(), this).data.join('');
429 }
430
431 blocks = this.blocks;
432
433 if (!blocks) {
434 dust.log('No blocks for context `' + key + '` in template `' + this.getTemplateName() + '`', DEBUG);
435 return false;
436 }
437
438 len = blocks.length;
439 while (len--) {
440 fn = blocks[len][key];
441 if (fn) {
442 return fn;
443 }
444 }
445
446 dust.log('Malformed template `' + this.getTemplateName() + '` was missing one or more blocks.');
447 return false;
448 };
449
450 Context.prototype.shiftBlocks = function(locals) {
451 var blocks = this.blocks,
452 newBlocks;
453
454 if (locals) {
455 if (!blocks) {
456 newBlocks = [locals];
457 } else {
458 newBlocks = blocks.concat([locals]);
459 }
460 return new Context(this.stack, this.global, newBlocks, this.getTemplateName());
461 }
462 return this;
463 };
464
465 Context.prototype.resolve = function(body) {
466 var chunk;
467
468 if(typeof body !== 'function') {
469 return body;
470 }
471 chunk = new Chunk().render(body, this);
472 if(!body.__dustBody) {
473 return chunk;
474 }
475 return chunk.data.join(''); // ie7 perf
476 };
477
478 Context.prototype.getTemplateName = function() {
479 return this.templateName;
480 };
481
482 function Stack(head, tail, idx, len) {
483 this.tail = tail;
484 this.isObject = head && typeof head === 'object';
485 this.head = head;
486 this.index = idx;
487 this.of = len;
488 }
489
490 function Stub(callback) {
491 this.head = new Chunk(this);
492 this.callback = callback;
493 this.out = '';
494 }
495
496 Stub.prototype.flush = function() {
497 var chunk = this.head;
498
499 while (chunk) {
500 if (chunk.flushable) {
501 this.out += chunk.data.join(''); //ie7 perf
502 } else if (chunk.error) {
503 this.callback(chunk.error);
504 dust.log('Rendering failed with error `' + chunk.error + '`', ERROR);
505 this.flush = EMPTY_FUNC;
506 return;
507 } else {
508 return;
509 }
510 chunk = chunk.next;
511 this.head = chunk;
512 }
513 this.callback(null, this.out);
514 };
515
516 function Stream() {
517 this.head = new Chunk(this);
518 }
519
520 Stream.prototype.flush = function() {
521 var chunk = this.head;
522
523 while(chunk) {
524 if (chunk.flushable) {
525 this.emit('data', chunk.data.join('')); //ie7 perf
526 } else if (chunk.error) {
527 this.emit('error', chunk.error);
528 dust.log('Streaming failed with error `' + chunk.error + '`', ERROR);
529 this.flush = EMPTY_FUNC;
530 return;
531 } else {
532 return;
533 }
534 chunk = chunk.next;
535 this.head = chunk;
536 }
537 this.emit('end');
538 };
539
540 Stream.prototype.emit = function(type, data) {
541 var events = this.events || {},
542 handlers = events[type] || [],
543 i, l;
544
545 if (!handlers.length) {
546 dust.log('Stream broadcasting, but no listeners for `' + type + '`', DEBUG);
547 return;
548 }
549
550 handlers = handlers.slice(0);
551 for (i = 0, l = handlers.length; i < l; i++) {
552 handlers[i](data);
553 }
554 };
555
556 Stream.prototype.on = function(type, callback) {
557 var events = this.events = this.events || {},
558 handlers = events[type] = events[type] || [];
559
560 if(typeof callback !== 'function') {
561 dust.log('No callback function provided for `' + type + '` event listener', WARN);
562 } else {
563 handlers.push(callback);
564 }
565 return this;
566 };
567
568 Stream.prototype.pipe = function(stream) {
569 return this
570 .on('data', function(data) {
571 try {
572 stream.write(data, 'utf8');
573 } catch (err) {
574 dust.log(err, ERROR);
575 }
576 })
577 .on('end', function() {
578 try {
579 stream.end();
580 } catch (err) {
581 dust.log(err, ERROR);
582 }
583 })
584 .on('error', function(err) {
585 stream.error(err);
586 });
587 };
588
589 function Chunk(root, next, taps) {
590 this.root = root;
591 this.next = next;
592 this.data = []; //ie7 perf
593 this.flushable = false;
594 this.taps = taps;
595 }
596
597 Chunk.prototype.write = function(data) {
598 var taps = this.taps;
599
600 if (taps) {
601 data = taps.go(data);
602 }
603 this.data.push(data);
604 return this;
605 };
606
607 Chunk.prototype.end = function(data) {
608 if (data) {
609 this.write(data);
610 }
611 this.flushable = true;
612 this.root.flush();
613 return this;
614 };
615
616 Chunk.prototype.map = function(callback) {
617 var cursor = new Chunk(this.root, this.next, this.taps),
618 branch = new Chunk(this.root, cursor, this.taps);
619
620 this.next = branch;
621 this.flushable = true;
622 try {
623 callback(branch);
624 } catch(err) {
625 dust.log(err, ERROR);
626 branch.setError(err);
627 }
628 return cursor;
629 };
630
631 Chunk.prototype.tap = function(tap) {
632 var taps = this.taps;
633
634 if (taps) {
635 this.taps = taps.push(tap);
636 } else {
637 this.taps = new Tap(tap);
638 }
639 return this;
640 };
641
642 Chunk.prototype.untap = function() {
643 this.taps = this.taps.tail;
644 return this;
645 };
646
647 Chunk.prototype.render = function(body, context) {
648 return body(this, context);
649 };
650
651 Chunk.prototype.reference = function(elem, context, auto, filters) {
652 if (typeof elem === 'function') {
653 elem = elem.apply(context.current(), [this, context, null, {auto: auto, filters: filters}]);
654 if (elem instanceof Chunk) {
655 return elem;
656 }
657 }
658 if (dust.isThenable(elem)) {
659 return this.await(elem, context);
660 } else if (!dust.isEmpty(elem)) {
661 return this.write(dust.filter(elem, auto, filters));
662 } else {
663 return this;
664 }
665 };
666
667 Chunk.prototype.section = function(elem, context, bodies, params) {
668 var body = bodies.block,
669 skip = bodies['else'],
670 chunk = this,
671 i, len;
672
673 if (typeof elem === 'function' && !elem.__dustBody) {
674 try {
675 elem = elem.apply(context.current(), [this, context, bodies, params]);
676 } catch(err) {
677 dust.log(err, ERROR);
678 return this.setError(err);
679 }
680 // Functions that return chunks are assumed to have handled the chunk manually.
681 // Make that chunk the current one and go to the next method in the chain.
682 if (elem instanceof Chunk) {
683 return elem;
684 }
685 }
686
687 if (!dust.isEmptyObject(params)) {
688 context = context.push(params);
689 }
690
691 /*
692 Dust's default behavior is to enumerate over the array elem, passing each object in the array to the block.
693 When elem resolves to a value or object instead of an array, Dust sets the current context to the value
694 and renders the block one time.
695 */
696 if (dust.isArray(elem)) {
697 if (body) {
698 len = elem.length;
699 if (len > 0) {
700 // any custom helper can blow up the stack and store a flattened context, guard defensively
701 if(context.stack.head) {
702 context.stack.head['$len'] = len;
703 }
704 for (i = 0; i < len; i++) {
705 if(context.stack.head) {
706 context.stack.head['$idx'] = i;
707 }
708 chunk = body(chunk, context.push(elem[i], i, len));
709 }
710 if(context.stack.head) {
711 context.stack.head['$idx'] = undefined;
712 context.stack.head['$len'] = undefined;
713 }
714 return chunk;
715 }
716 else if (skip) {
717 return skip(this, context);
718 }
719 }
720 } else if (dust.isThenable(elem)) {
721 return this.await(elem, context, bodies);
722 } else if (elem === true) {
723 // true is truthy but does not change context
724 if (body) {
725 return body(this, context);
726 }
727 } else if (elem || elem === 0) {
728 // everything that evaluates to true are truthy ( e.g. Non-empty strings and Empty objects are truthy. )
729 // zero is truthy
730 // for anonymous functions that did not returns a chunk, truthiness is evaluated based on the return value
731 if (body) {
732 return body(this, context.push(elem));
733 }
734 // nonexistent, scalar false value, scalar empty string, null,
735 // undefined are all falsy
736 } else if (skip) {
737 return skip(this, context);
738 }
739 dust.log('Section without corresponding key in template `' + context.getTemplateName() + '`', DEBUG);
740 return this;
741 };
742
743 Chunk.prototype.exists = function(elem, context, bodies) {
744 var body = bodies.block,
745 skip = bodies['else'];
746
747 if (!dust.isEmpty(elem)) {
748 if (body) {
749 return body(this, context);
750 }
751 dust.log('No block for exists check in template `' + context.getTemplateName() + '`', DEBUG);
752 } else if (skip) {
753 return skip(this, context);
754 }
755 return this;
756 };
757
758 Chunk.prototype.notexists = function(elem, context, bodies) {
759 var body = bodies.block,
760 skip = bodies['else'];
761
762 if (dust.isEmpty(elem)) {
763 if (body) {
764 return body(this, context);
765 }
766 dust.log('No block for not-exists check in template `' + context.getTemplateName() + '`', DEBUG);
767 } else if (skip) {
768 return skip(this, context);
769 }
770 return this;
771 };
772
773 Chunk.prototype.block = function(elem, context, bodies) {
774 var body = elem || bodies.block;
775
776 if (body) {
777 return body(this, context);
778 }
779 return this;
780 };
781
782 Chunk.prototype.partial = function(elem, context, params) {
783 var head;
784
785 if (!dust.isEmptyObject(params)) {
786 context = context.clone();
787 head = context.pop();
788 context = context.push(params)
789 .push(head);
790 }
791
792 if (elem.__dustBody) {
793 // The eventual result of evaluating `elem` is a partial name
794 // Load the partial after getting its name and end the async chunk
795 return this.capture(elem, context, function(name, chunk) {
796 context.templateName = name;
797 dust.load(name, chunk, context).end();
798 });
799 } else {
800 context.templateName = elem;
801 return dust.load(elem, this, context);
802 }
803 };
804
805 Chunk.prototype.helper = function(name, context, bodies, params) {
806 var chunk = this,
807 ret;
808 // handle invalid helpers, similar to invalid filters
809 if(dust.helpers[name]) {
810 try {
811 ret = dust.helpers[name](chunk, context, bodies, params);
812 if (dust.isThenable(ret)) {
813 return this.await(ret, context, bodies);
814 }
815 return ret;
816 } catch(err) {
817 dust.log('Error in helper `' + name + '`: ' + err.message, ERROR);
818 return chunk.setError(err);
819 }
820 } else {
821 dust.log('Helper `' + name + '` does not exist', WARN);
822 return chunk;
823 }
824 };
825
826 /**
827 * Reserve a chunk to be evaluated once a thenable is resolved or rejected
828 * @param thenable {Thenable} the target thenable to await
829 * @param context {Context} context to use to render the deferred chunk
830 * @param bodies {Object} must contain a "body", may contain an "error"
831 * @return {Chunk}
832 */
833 Chunk.prototype.await = function(thenable, context, bodies) {
834 var body = bodies && bodies.block,
835 errorBody = bodies && bodies.error;
836 return this.map(function(chunk) {
837 thenable.then(function(data) {
838 if(body) {
839 chunk.render(body, context.push(data)).end();
840 } else {
841 chunk.end(data);
842 }
843 }, function(err) {
844 if(errorBody) {
845 chunk.render(errorBody, context.push(err)).end();
846 } else {
847 dust.log('Unhandled promise rejection in `' + context.getTemplateName() + '`');
848 chunk.end();
849 }
850 });
851 });
852 };
853
854 Chunk.prototype.capture = function(body, context, callback) {
855 return this.map(function(chunk) {
856 var stub = new Stub(function(err, out) {
857 if (err) {
858 chunk.setError(err);
859 } else {
860 callback(out, chunk);
861 }
862 });
863 body(stub.head, context).end();
864 });
865 };
866
867 Chunk.prototype.setError = function(err) {
868 this.error = err;
869 this.root.flush();
870 return this;
871 };
872
873 // Chunk aliases
874 for(var f in Chunk.prototype) {
875 if(dust._aliases[f]) {
876 Chunk.prototype[dust._aliases[f]] = Chunk.prototype[f];
877 }
878 }
879
880 function Tap(head, tail) {
881 this.head = head;
882 this.tail = tail;
883 }
884
885 Tap.prototype.push = function(tap) {
886 return new Tap(tap, this);
887 };
888
889 Tap.prototype.go = function(value) {
890 var tap = this;
891
892 while(tap) {
893 value = tap.head(value);
894 tap = tap.tail;
895 }
896 return value;
897 };
898
899 var HCHARS = /[&<>"']/,
900 AMP = /&/g,
901 LT = /</g,
902 GT = />/g,
903 QUOT = /\"/g,
904 SQUOT = /\'/g;
905
906 dust.escapeHtml = function(s) {
907 if (typeof s === "string" || (s && typeof s.toString === "function")) {
908 if (typeof s !== "string") {
909 s = s.toString();
910 }
911 if (!HCHARS.test(s)) {
912 return s;
913 }
914 return s.replace(AMP,'&amp;').replace(LT,'&lt;').replace(GT,'&gt;').replace(QUOT,'&quot;').replace(SQUOT, '&#39;');
915 }
916 return s;
917 };
918
919 var BS = /\\/g,
920 FS = /\//g,
921 CR = /\r/g,
922 LS = /\u2028/g,
923 PS = /\u2029/g,
924 NL = /\n/g,
925 LF = /\f/g,
926 SQ = /'/g,
927 DQ = /"/g,
928 TB = /\t/g;
929
930 dust.escapeJs = function(s) {
931 if (typeof s === 'string') {
932 return s
933 .replace(BS, '\\\\')
934 .replace(FS, '\\/')
935 .replace(DQ, '\\"')
936 .replace(SQ, '\\\'')
937 .replace(CR, '\\r')
938 .replace(LS, '\\u2028')
939 .replace(PS, '\\u2029')
940 .replace(NL, '\\n')
941 .replace(LF, '\\f')
942 .replace(TB, '\\t');
943 }
944 return s;
945 };
946
947 dust.escapeJSON = function(o) {
948 if (!JSON) {
949 dust.log('JSON is undefined; could not escape `' + o + '`', WARN);
950 return o;
951 } else {
952 return JSON.stringify(o)
953 .replace(LS, '\\u2028')
954 .replace(PS, '\\u2029')
955 .replace(LT, '\\u003c');
956 }
957 };
958
959 return dust;
960
961}));