UNPKG

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