|
536 | 536 | * also be a function that is used to load partial templates on the fly |
537 | 537 | * that takes a single argument: the name of the partial. |
538 | 538 | * |
539 | | - * If the optional `tags` argument is given here it must be an array with two |
| 539 | + * If the optional `config` argument is given here, then it should be an |
| 540 | + * object with a `tags` attribute or an `escape` attribute or both. |
| 541 | + * If an array is passed, then it will be interpreted the same way as |
| 542 | + * a `tags` attribute on a `config` object. |
| 543 | + * |
| 544 | + * The `tags` attribute of a `config` object must be an array with two |
540 | 545 | * string values: the opening and closing tags used in the template (e.g. |
541 | 546 | * [ "<%", "%>" ]). The default is to mustache.tags. |
| 547 | + * |
| 548 | + * The `escape` attribute of a `config` object must be a function which |
| 549 | + * accepts a string as input and outputs a safely escaped string. |
| 550 | + * If an `escape` function is not provided, then an HTML-safe string |
| 551 | + * escaping function is used as the default. |
542 | 552 | */ |
543 | | - Writer.prototype.render = function render (template, view, partials, tags) { |
| 553 | + Writer.prototype.render = function render (template, view, partials, config) { |
| 554 | + var tags = this.getConfigTags(config); |
544 | 555 | var tokens = this.parse(template, tags); |
545 | 556 | var context = (view instanceof Context) ? view : new Context(view, undefined); |
546 | | - return this.renderTokens(tokens, context, partials, template, tags); |
| 557 | + return this.renderTokens(tokens, context, partials, template, config); |
547 | 558 | }; |
548 | 559 |
|
549 | 560 | /** |
|
555 | 566 | * If the template doesn't use higher-order sections, this argument may |
556 | 567 | * be omitted. |
557 | 568 | */ |
558 | | - Writer.prototype.renderTokens = function renderTokens (tokens, context, partials, originalTemplate, tags) { |
| 569 | + Writer.prototype.renderTokens = function renderTokens (tokens, context, partials, originalTemplate, config) { |
559 | 570 | var buffer = ''; |
560 | 571 |
|
561 | 572 | var token, symbol, value; |
|
564 | 575 | token = tokens[i]; |
565 | 576 | symbol = token[0]; |
566 | 577 |
|
567 | | - if (symbol === '#') value = this.renderSection(token, context, partials, originalTemplate); |
568 | | - else if (symbol === '^') value = this.renderInverted(token, context, partials, originalTemplate); |
569 | | - else if (symbol === '>') value = this.renderPartial(token, context, partials, tags); |
| 578 | + if (symbol === '#') value = this.renderSection(token, context, partials, originalTemplate, config); |
| 579 | + else if (symbol === '^') value = this.renderInverted(token, context, partials, originalTemplate, config); |
| 580 | + else if (symbol === '>') value = this.renderPartial(token, context, partials, config); |
570 | 581 | else if (symbol === '&') value = this.unescapedValue(token, context); |
571 | | - else if (symbol === 'name') value = this.escapedValue(token, context); |
| 582 | + else if (symbol === 'name') value = this.escapedValue(token, context, config); |
572 | 583 | else if (symbol === 'text') value = this.rawValue(token); |
573 | 584 |
|
574 | 585 | if (value !== undefined) |
|
578 | 589 | return buffer; |
579 | 590 | }; |
580 | 591 |
|
581 | | - Writer.prototype.renderSection = function renderSection (token, context, partials, originalTemplate) { |
| 592 | + Writer.prototype.renderSection = function renderSection (token, context, partials, originalTemplate, config) { |
582 | 593 | var self = this; |
583 | 594 | var buffer = ''; |
584 | 595 | var value = context.lookup(token[1]); |
585 | 596 |
|
586 | 597 | // This function is used to render an arbitrary template |
587 | 598 | // in the current context by higher-order sections. |
588 | 599 | function subRender (template) { |
589 | | - return self.render(template, context, partials); |
| 600 | + return self.render(template, context, partials, config); |
590 | 601 | } |
591 | 602 |
|
592 | 603 | if (!value) return; |
593 | 604 |
|
594 | 605 | if (isArray(value)) { |
595 | 606 | for (var j = 0, valueLength = value.length; j < valueLength; ++j) { |
596 | | - buffer += this.renderTokens(token[4], context.push(value[j]), partials, originalTemplate); |
| 607 | + buffer += this.renderTokens(token[4], context.push(value[j]), partials, originalTemplate, config); |
597 | 608 | } |
598 | 609 | } else if (typeof value === 'object' || typeof value === 'string' || typeof value === 'number') { |
599 | | - buffer += this.renderTokens(token[4], context.push(value), partials, originalTemplate); |
| 610 | + buffer += this.renderTokens(token[4], context.push(value), partials, originalTemplate, config); |
600 | 611 | } else if (isFunction(value)) { |
601 | 612 | if (typeof originalTemplate !== 'string') |
602 | 613 | throw new Error('Cannot use higher-order sections without the original template'); |
|
607 | 618 | if (value != null) |
608 | 619 | buffer += value; |
609 | 620 | } else { |
610 | | - buffer += this.renderTokens(token[4], context, partials, originalTemplate); |
| 621 | + buffer += this.renderTokens(token[4], context, partials, originalTemplate, config); |
611 | 622 | } |
612 | 623 | return buffer; |
613 | 624 | }; |
614 | 625 |
|
615 | | - Writer.prototype.renderInverted = function renderInverted (token, context, partials, originalTemplate) { |
| 626 | + Writer.prototype.renderInverted = function renderInverted (token, context, partials, originalTemplate, config) { |
616 | 627 | var value = context.lookup(token[1]); |
617 | 628 |
|
618 | 629 | // Use JavaScript's definition of falsy. Include empty arrays. |
619 | 630 | // See https://github.com/janl/mustache.js/issues/186 |
620 | 631 | if (!value || (isArray(value) && value.length === 0)) |
621 | | - return this.renderTokens(token[4], context, partials, originalTemplate); |
| 632 | + return this.renderTokens(token[4], context, partials, originalTemplate, config); |
622 | 633 | }; |
623 | 634 |
|
624 | 635 | Writer.prototype.indentPartial = function indentPartial (partial, indentation, lineHasNonSpace) { |
|
632 | 643 | return partialByNl.join('\n'); |
633 | 644 | }; |
634 | 645 |
|
635 | | - Writer.prototype.renderPartial = function renderPartial (token, context, partials, tags) { |
| 646 | + Writer.prototype.renderPartial = function renderPartial (token, context, partials, config) { |
636 | 647 | if (!partials) return; |
| 648 | + var tags = this.getConfigTags(config); |
637 | 649 |
|
638 | 650 | var value = isFunction(partials) ? partials(token[1]) : partials[token[1]]; |
639 | 651 | if (value != null) { |
|
644 | 656 | if (tagIndex == 0 && indentation) { |
645 | 657 | indentedValue = this.indentPartial(value, indentation, lineHasNonSpace); |
646 | 658 | } |
647 | | - return this.renderTokens(this.parse(indentedValue, tags), context, partials, indentedValue, tags); |
| 659 | + var tokens = this.parse(indentedValue, tags); |
| 660 | + return this.renderTokens(tokens, context, partials, indentedValue, config); |
648 | 661 | } |
649 | 662 | }; |
650 | 663 |
|
|
654 | 667 | return value; |
655 | 668 | }; |
656 | 669 |
|
657 | | - Writer.prototype.escapedValue = function escapedValue (token, context) { |
| 670 | + Writer.prototype.escapedValue = function escapedValue (token, context, config) { |
| 671 | + var escape = this.getConfigEscape(config) || mustache.escape; |
658 | 672 | var value = context.lookup(token[1]); |
659 | 673 | if (value != null) |
660 | | - return typeof value === 'number' ? String(value) : mustache.escape(value); |
| 674 | + return (typeof value === 'number' && escape === mustache.escape) ? String(value) : escape(value); |
661 | 675 | }; |
662 | 676 |
|
663 | 677 | Writer.prototype.rawValue = function rawValue (token) { |
664 | 678 | return token[1]; |
665 | 679 | }; |
666 | 680 |
|
| 681 | + Writer.prototype.getConfigTags = function getConfigTags (config) { |
| 682 | + if (isArray(config)) { |
| 683 | + return config; |
| 684 | + } |
| 685 | + else if (config && typeof config === 'object') { |
| 686 | + return config.tags; |
| 687 | + } |
| 688 | + else { |
| 689 | + return undefined; |
| 690 | + } |
| 691 | + }; |
| 692 | + |
| 693 | + Writer.prototype.getConfigEscape = function getConfigEscape (config) { |
| 694 | + if (config && typeof config === 'object' && !isArray(config)) { |
| 695 | + return config.escape; |
| 696 | + } |
| 697 | + else { |
| 698 | + return undefined; |
| 699 | + } |
| 700 | + }; |
| 701 | + |
667 | 702 | var mustache = { |
668 | 703 | name: 'mustache.js', |
669 | | - version: '4.0.1', |
| 704 | + version: '4.1.0', |
670 | 705 | tags: [ '{{', '}}' ], |
671 | 706 | clearCache: undefined, |
672 | 707 | escape: undefined, |
|
711 | 746 | }; |
712 | 747 |
|
713 | 748 | /** |
714 | | - * Renders the `template` with the given `view` and `partials` using the |
715 | | - * default writer. If the optional `tags` argument is given here it must be an |
716 | | - * array with two string values: the opening and closing tags used in the |
717 | | - * template (e.g. [ "<%", "%>" ]). The default is to mustache.tags. |
| 749 | + * Renders the `template` with the given `view`, `partials`, and `config` |
| 750 | + * using the default writer. |
718 | 751 | */ |
719 | | - mustache.render = function render (template, view, partials, tags) { |
| 752 | + mustache.render = function render (template, view, partials, config) { |
720 | 753 | if (typeof template !== 'string') { |
721 | 754 | throw new TypeError('Invalid template! Template should be a "string" ' + |
722 | 755 | 'but "' + typeStr(template) + '" was given as the first ' + |
723 | 756 | 'argument for mustache#render(template, view, partials)'); |
724 | 757 | } |
725 | 758 |
|
726 | | - return defaultWriter.render(template, view, partials, tags); |
| 759 | + return defaultWriter.render(template, view, partials, config); |
727 | 760 | }; |
728 | 761 |
|
729 | 762 | // Export the escaping function so that the user may override it. |
|
0 commit comments