248 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
			
		
		
	
	
			248 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
/**
 | 
						|
 * Copyright © Magento, Inc. All rights reserved.
 | 
						|
 * See COPYING.txt for license details.
 | 
						|
 */
 | 
						|
 | 
						|
/* eslint-disable no-shadow */
 | 
						|
 | 
						|
define([
 | 
						|
    'jquery',
 | 
						|
    'underscore',
 | 
						|
    'mage/utils/objects',
 | 
						|
    'mage/utils/strings'
 | 
						|
], function ($, _, utils, stringUtils) {
 | 
						|
    'use strict';
 | 
						|
 | 
						|
    var tmplSettings = _.templateSettings,
 | 
						|
        interpolate = /\$\{([\s\S]+?)\}/g,
 | 
						|
        opener = '${',
 | 
						|
        template,
 | 
						|
        hasStringTmpls;
 | 
						|
 | 
						|
    /**
 | 
						|
     * Identifies whether ES6 templates are supported.
 | 
						|
     */
 | 
						|
    hasStringTmpls = (function () {
 | 
						|
        var testString = 'var foo = "bar"; return `${ foo }` === foo';
 | 
						|
 | 
						|
        try {
 | 
						|
            return Function(testString)();
 | 
						|
        } catch (e) {
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
    })();
 | 
						|
 | 
						|
    /**
 | 
						|
     * Objects can specify how to use templating for their properties - getting that configuration.
 | 
						|
     *
 | 
						|
     * To disable rendering for all properties of your object add __disableTmpl: true.
 | 
						|
     * To disable for specific property add __disableTmpl: {propertyName: true}.
 | 
						|
     * To limit recursion for a specific property add __disableTmpl: {propertyName: numberOfCycles}.
 | 
						|
     *
 | 
						|
     * @param {String} tmpl
 | 
						|
     * @param {Object | undefined} target
 | 
						|
     * @returns {Boolean|Object}
 | 
						|
     */
 | 
						|
    function isTmplIgnored(tmpl, target) {
 | 
						|
        var parsedTmpl;
 | 
						|
 | 
						|
        try {
 | 
						|
            parsedTmpl = JSON.parse(tmpl);
 | 
						|
 | 
						|
            if (typeof parsedTmpl === 'object') {
 | 
						|
                return tmpl.includes('__disableTmpl');
 | 
						|
            }
 | 
						|
        } catch (e) {
 | 
						|
        }
 | 
						|
 | 
						|
        if (typeof target !== 'undefined') {
 | 
						|
            if (typeof target === 'object' && target.hasOwnProperty('__disableTmpl')) {
 | 
						|
                return target.__disableTmpl;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return false;
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
    if (hasStringTmpls) {
 | 
						|
 | 
						|
        /*eslint-disable no-unused-vars, no-eval*/
 | 
						|
        /**
 | 
						|
         * Evaluates template string using ES6 templates.
 | 
						|
         *
 | 
						|
         * @param {String} tmpl - Template string.
 | 
						|
         * @param {Object} $ - Data object used in a template.
 | 
						|
         * @returns {String} Compiled template.
 | 
						|
         */
 | 
						|
        template = function (tmpl, $) {
 | 
						|
            return eval('`' + tmpl + '`');
 | 
						|
        };
 | 
						|
 | 
						|
        /*eslint-enable no-unused-vars, no-eval*/
 | 
						|
    } else {
 | 
						|
 | 
						|
        /**
 | 
						|
         * Fallback function used when ES6 templates are not supported.
 | 
						|
         * Uses underscore templates renderer.
 | 
						|
         *
 | 
						|
         * @param {String} tmpl - Template string.
 | 
						|
         * @param {Object} data - Data object used in a template.
 | 
						|
         * @returns {String} Compiled template.
 | 
						|
         */
 | 
						|
        template = function (tmpl, data) {
 | 
						|
            var cached = tmplSettings.interpolate;
 | 
						|
 | 
						|
            tmplSettings.interpolate = interpolate;
 | 
						|
 | 
						|
            tmpl = _.template(tmpl, {
 | 
						|
                variable: '$'
 | 
						|
            })(data);
 | 
						|
 | 
						|
            tmplSettings.interpolate = cached;
 | 
						|
 | 
						|
            return tmpl;
 | 
						|
        };
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Checks if provided value contains template syntax.
 | 
						|
     *
 | 
						|
     * @param {*} value - Value to be checked.
 | 
						|
     * @returns {Boolean}
 | 
						|
     */
 | 
						|
    function isTemplate(value) {
 | 
						|
        return typeof value === 'string' &&
 | 
						|
            value.indexOf(opener) !== -1 &&
 | 
						|
            // the below pattern almost always indicates an accident which should not cause template evaluation
 | 
						|
            // refuse to evaluate
 | 
						|
            value.indexOf('${{') === -1;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Iteratively processes provided string
 | 
						|
     * until no templates syntax will be found.
 | 
						|
     *
 | 
						|
     * @param {String} tmpl - Template string.
 | 
						|
     * @param {Object} data - Data object used in a template.
 | 
						|
     * @param {Boolean} [castString=false] - Flag that indicates whether template
 | 
						|
     *      should be casted after evaluation to a value of another type or
 | 
						|
     *      that it should be leaved as a string.
 | 
						|
     * @param {Number|undefined} maxCycles - Maximum number of rendering cycles, can be 0.
 | 
						|
     * @returns {*} Compiled template.
 | 
						|
     */
 | 
						|
    function render(tmpl, data, castString, maxCycles) {
 | 
						|
        var last = tmpl,
 | 
						|
            cycles = 0;
 | 
						|
 | 
						|
        while (~tmpl.indexOf(opener) && (typeof maxCycles === 'undefined' || cycles < maxCycles)) {
 | 
						|
            if (!isTmplIgnored(tmpl)) {
 | 
						|
                tmpl = template(tmpl, data);
 | 
						|
            }
 | 
						|
 | 
						|
            if (tmpl === last) {
 | 
						|
                break;
 | 
						|
            }
 | 
						|
 | 
						|
            last = tmpl;
 | 
						|
            cycles++;
 | 
						|
        }
 | 
						|
 | 
						|
        return castString ?
 | 
						|
            stringUtils.castString(tmpl) :
 | 
						|
            tmpl;
 | 
						|
    }
 | 
						|
 | 
						|
    return {
 | 
						|
 | 
						|
        /**
 | 
						|
         * Applies provided data to the template.
 | 
						|
         *
 | 
						|
         * @param {Object|String} tmpl
 | 
						|
         * @param {Object} [data] - Data object to match with template.
 | 
						|
         * @param {Boolean} [castString=false] - Flag that indicates whether template
 | 
						|
         *      should be casted after evaluation to a value of another type or
 | 
						|
         *      that it should be leaved as a string.
 | 
						|
         * @returns {*}
 | 
						|
         *
 | 
						|
         * @example Template defined as a string.
 | 
						|
         *      var source = { foo: 'Random Stuff', bar: 'Some' };
 | 
						|
         *
 | 
						|
         *      utils.template('${ $.bar } ${ $.foo }', source);
 | 
						|
         *      => 'Some Random Stuff';
 | 
						|
         *
 | 
						|
         * @example Template defined as an object.
 | 
						|
         *      var tmpl = {
 | 
						|
         *              key: {'${ $.$data.bar }': '${ $.$data.foo }'},
 | 
						|
         *              foo: 'bar',
 | 
						|
         *              x1: 2, x2: 5,
 | 
						|
         *              delta: '${ $.x2 - $.x1 }',
 | 
						|
         *              baz: 'Upper ${ $.foo.toUpperCase() }'
 | 
						|
         *      };
 | 
						|
         *
 | 
						|
         *      utils.template(tmpl, source);
 | 
						|
         *      => {
 | 
						|
         *          key: {'Some': 'Random Stuff'},
 | 
						|
         *          foo: 'bar',
 | 
						|
         *          x1: 2, x2: 5,
 | 
						|
         *          delta: 3,
 | 
						|
         *          baz: 'Upper BAR'
 | 
						|
         *      };
 | 
						|
         */
 | 
						|
        template: function (tmpl, data, castString, dontClone) {
 | 
						|
            if (typeof tmpl === 'string') {
 | 
						|
                return render(tmpl, data, castString);
 | 
						|
            }
 | 
						|
 | 
						|
            if (!dontClone) {
 | 
						|
                tmpl = utils.copy(tmpl);
 | 
						|
            }
 | 
						|
 | 
						|
            tmpl.$data = data || {};
 | 
						|
 | 
						|
            /**
 | 
						|
             * Template iterator function.
 | 
						|
             */
 | 
						|
            _.each(tmpl, function iterate(value, key, list) {
 | 
						|
                var disabled,
 | 
						|
                    maxCycles;
 | 
						|
 | 
						|
                if (key === '$data') {
 | 
						|
                    return;
 | 
						|
                }
 | 
						|
 | 
						|
                if (isTemplate(key)) {
 | 
						|
                    delete list[key];
 | 
						|
 | 
						|
                    key = render(key, tmpl);
 | 
						|
                    list[key] = value;
 | 
						|
                }
 | 
						|
 | 
						|
                if (isTemplate(value)) {
 | 
						|
                    //Getting template disabling settings, can be true for all disabled and separate settings
 | 
						|
                    //for each property.
 | 
						|
                    disabled = isTmplIgnored(value, list);
 | 
						|
 | 
						|
                    if (typeof disabled === 'object' && disabled.hasOwnProperty(key) && disabled[key] !== false) {
 | 
						|
                        //Checking if specific settings for a property provided.
 | 
						|
                        maxCycles = disabled[key];
 | 
						|
                    }
 | 
						|
 | 
						|
                    if (disabled === true || maxCycles === true) {
 | 
						|
                        //Rendering for all properties is disabled.
 | 
						|
                        maxCycles = 0;
 | 
						|
                    }
 | 
						|
 | 
						|
                    list[key] = render(value, tmpl, castString, maxCycles);
 | 
						|
                } else if ($.isPlainObject(value) || Array.isArray(value)) {
 | 
						|
                    _.each(value, iterate);
 | 
						|
                }
 | 
						|
            });
 | 
						|
 | 
						|
            delete tmpl.$data;
 | 
						|
 | 
						|
            return tmpl;
 | 
						|
        }
 | 
						|
    };
 | 
						|
});
 |