No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.
 
 
 
 
 
 

248 líneas
7.4 KiB

  1. /**
  2. * Copyright © Magento, Inc. All rights reserved.
  3. * See COPYING.txt for license details.
  4. */
  5. /* eslint-disable no-shadow */
  6. define([
  7. 'jquery',
  8. 'underscore',
  9. 'mage/utils/objects',
  10. 'mage/utils/strings'
  11. ], function ($, _, utils, stringUtils) {
  12. 'use strict';
  13. var tmplSettings = _.templateSettings,
  14. interpolate = /\$\{([\s\S]+?)\}/g,
  15. opener = '${',
  16. template,
  17. hasStringTmpls;
  18. /**
  19. * Identifies whether ES6 templates are supported.
  20. */
  21. hasStringTmpls = (function () {
  22. var testString = 'var foo = "bar"; return `${ foo }` === foo';
  23. try {
  24. return Function(testString)();
  25. } catch (e) {
  26. return false;
  27. }
  28. })();
  29. /**
  30. * Objects can specify how to use templating for their properties - getting that configuration.
  31. *
  32. * To disable rendering for all properties of your object add __disableTmpl: true.
  33. * To disable for specific property add __disableTmpl: {propertyName: true}.
  34. * To limit recursion for a specific property add __disableTmpl: {propertyName: numberOfCycles}.
  35. *
  36. * @param {String} tmpl
  37. * @param {Object | undefined} target
  38. * @returns {Boolean|Object}
  39. */
  40. function isTmplIgnored(tmpl, target) {
  41. var parsedTmpl;
  42. try {
  43. parsedTmpl = JSON.parse(tmpl);
  44. if (typeof parsedTmpl === 'object') {
  45. return tmpl.includes('__disableTmpl');
  46. }
  47. } catch (e) {
  48. }
  49. if (typeof target !== 'undefined') {
  50. if (typeof target === 'object' && target.hasOwnProperty('__disableTmpl')) {
  51. return target.__disableTmpl;
  52. }
  53. }
  54. return false;
  55. }
  56. if (hasStringTmpls) {
  57. /*eslint-disable no-unused-vars, no-eval*/
  58. /**
  59. * Evaluates template string using ES6 templates.
  60. *
  61. * @param {String} tmpl - Template string.
  62. * @param {Object} $ - Data object used in a template.
  63. * @returns {String} Compiled template.
  64. */
  65. template = function (tmpl, $) {
  66. return eval('`' + tmpl + '`');
  67. };
  68. /*eslint-enable no-unused-vars, no-eval*/
  69. } else {
  70. /**
  71. * Fallback function used when ES6 templates are not supported.
  72. * Uses underscore templates renderer.
  73. *
  74. * @param {String} tmpl - Template string.
  75. * @param {Object} data - Data object used in a template.
  76. * @returns {String} Compiled template.
  77. */
  78. template = function (tmpl, data) {
  79. var cached = tmplSettings.interpolate;
  80. tmplSettings.interpolate = interpolate;
  81. tmpl = _.template(tmpl, {
  82. variable: '$'
  83. })(data);
  84. tmplSettings.interpolate = cached;
  85. return tmpl;
  86. };
  87. }
  88. /**
  89. * Checks if provided value contains template syntax.
  90. *
  91. * @param {*} value - Value to be checked.
  92. * @returns {Boolean}
  93. */
  94. function isTemplate(value) {
  95. return typeof value === 'string' &&
  96. value.indexOf(opener) !== -1 &&
  97. // the below pattern almost always indicates an accident which should not cause template evaluation
  98. // refuse to evaluate
  99. value.indexOf('${{') === -1;
  100. }
  101. /**
  102. * Iteratively processes provided string
  103. * until no templates syntax will be found.
  104. *
  105. * @param {String} tmpl - Template string.
  106. * @param {Object} data - Data object used in a template.
  107. * @param {Boolean} [castString=false] - Flag that indicates whether template
  108. * should be casted after evaluation to a value of another type or
  109. * that it should be leaved as a string.
  110. * @param {Number|undefined} maxCycles - Maximum number of rendering cycles, can be 0.
  111. * @returns {*} Compiled template.
  112. */
  113. function render(tmpl, data, castString, maxCycles) {
  114. var last = tmpl,
  115. cycles = 0;
  116. while (~tmpl.indexOf(opener) && (typeof maxCycles === 'undefined' || cycles < maxCycles)) {
  117. if (!isTmplIgnored(tmpl)) {
  118. tmpl = template(tmpl, data);
  119. }
  120. if (tmpl === last) {
  121. break;
  122. }
  123. last = tmpl;
  124. cycles++;
  125. }
  126. return castString ?
  127. stringUtils.castString(tmpl) :
  128. tmpl;
  129. }
  130. return {
  131. /**
  132. * Applies provided data to the template.
  133. *
  134. * @param {Object|String} tmpl
  135. * @param {Object} [data] - Data object to match with template.
  136. * @param {Boolean} [castString=false] - Flag that indicates whether template
  137. * should be casted after evaluation to a value of another type or
  138. * that it should be leaved as a string.
  139. * @returns {*}
  140. *
  141. * @example Template defined as a string.
  142. * var source = { foo: 'Random Stuff', bar: 'Some' };
  143. *
  144. * utils.template('${ $.bar } ${ $.foo }', source);
  145. * => 'Some Random Stuff';
  146. *
  147. * @example Template defined as an object.
  148. * var tmpl = {
  149. * key: {'${ $.$data.bar }': '${ $.$data.foo }'},
  150. * foo: 'bar',
  151. * x1: 2, x2: 5,
  152. * delta: '${ $.x2 - $.x1 }',
  153. * baz: 'Upper ${ $.foo.toUpperCase() }'
  154. * };
  155. *
  156. * utils.template(tmpl, source);
  157. * => {
  158. * key: {'Some': 'Random Stuff'},
  159. * foo: 'bar',
  160. * x1: 2, x2: 5,
  161. * delta: 3,
  162. * baz: 'Upper BAR'
  163. * };
  164. */
  165. template: function (tmpl, data, castString, dontClone) {
  166. if (typeof tmpl === 'string') {
  167. return render(tmpl, data, castString);
  168. }
  169. if (!dontClone) {
  170. tmpl = utils.copy(tmpl);
  171. }
  172. tmpl.$data = data || {};
  173. /**
  174. * Template iterator function.
  175. */
  176. _.each(tmpl, function iterate(value, key, list) {
  177. var disabled,
  178. maxCycles;
  179. if (key === '$data') {
  180. return;
  181. }
  182. if (isTemplate(key)) {
  183. delete list[key];
  184. key = render(key, tmpl);
  185. list[key] = value;
  186. }
  187. if (isTemplate(value)) {
  188. //Getting template disabling settings, can be true for all disabled and separate settings
  189. //for each property.
  190. disabled = isTmplIgnored(value, list);
  191. if (typeof disabled === 'object' && disabled.hasOwnProperty(key) && disabled[key] !== false) {
  192. //Checking if specific settings for a property provided.
  193. maxCycles = disabled[key];
  194. }
  195. if (disabled === true || maxCycles === true) {
  196. //Rendering for all properties is disabled.
  197. maxCycles = 0;
  198. }
  199. list[key] = render(value, tmpl, castString, maxCycles);
  200. } else if ($.isPlainObject(value) || Array.isArray(value)) {
  201. _.each(value, iterate);
  202. }
  203. });
  204. delete tmpl.$data;
  205. return tmpl;
  206. }
  207. };
  208. });