PageSourceSearch

https://www.springerpub.com/static/version1789146322/base/Magento/…/js/lib/knockout/template/renderer.js

js springerpub.com collected 2026-09-24 08:47:42 UTC 16,401 bytes, 567 lines download raw bytes

1/**
2 * Copyright © Magento, Inc. All rights reserved.
3 * See COPYING.txt for license details.
4 */
5define([
6    'jquery',
7    'underscore',
8    './loader'
9], function ($, _, loader) {
10    'use strict';
11
12    var colonReg       = /\\:/g,
13        renderedTemplatePromises = {},
14        attributes     = {},
15        elements       = {},
16        globals        = [],
17        renderer,
18        preset;
19
20    renderer = {
21
22        /**
23         * Loads template by provided path and
24         * than converts it's content to html.
25         *
26         * @param {String} tmplPath - Path to the template.
27         * @returns {jQueryPromise}
28         * @alias getRendered
29         */
30        render: function (tmplPath) {
31            var cachedPromise = renderedTemplatePromises[tmplPath];
32
33            if (!cachedPromise) {
34                cachedPromise = renderedTemplatePromises[tmplPath] = loader
35                    .loadTemplate(tmplPath)
36                    .then(renderer.parseTemplate);
37            }
38
39            return cachedPromise;
40        },
41
42        /**
43         * @ignore
44         */
45        getRendered: function (tmplPath) {
46            return renderer.render(tmplPath);
47        },
48
49        /**
50         * Parses provided string as html content
51         * and returns an array of DOM elements.
52         *
53         * @param {String} html - String to be processed.
54         * @returns {Array}
55         */
56        parseTemplate: function (html) {
57            var fragment = document.createDocumentFragment();
58
59            $(fragment).append(html);
60
61            return renderer.normalize(fragment);
62        },
63
64        /**
65         * Processes custom attributes and nodes of provided DOM element.
66         *
67         * @param {HTMLElement} content - Element to be processed.
68         * @returns {Array} An array of content's child nodes.
69         */
70        normalize: function (content) {
71            globals.forEach(function (handler) {
72                handler(content);
73            });
74
75            return _.toArray(content.childNodes);
76        },
77
78        /**
79         * Adds new global content handler.
80         *
81         * @param {Function} handler - Function which will be invoked for
82         *      an every content passed to 'normalize' method.
83         * @returns {Renderer} Chainable.
84         */
85        addGlobal: function (handler) {
86            if (!_.contains(globals, handler)) {
87                globals.push(handler);
88            }
89
90            return this;
91        },
92
93        /**
94         * Removes specified global content handler.
95         *
96         * @param {Function} handler - Handler to be removed.
97         * @returns {Renderer} Chainable.
98         */
99        removeGlobal: function (handler) {
100            var index = globals.indexOf(handler);
101
102            if (~index) {
103                globals.splice(index, 1);
104            }
105
106            return this;
107        },
108
109        /**
110         * Adds new custom attribute handler.
111         *
112         * @param {String} id - Attribute identifier.
113         * @param {(Object|Function)} [config={}]
114         * @returns {Renderer} Chainable.
115         */
116        addAttribute: function (id, config) {
117            var data = {
118                name: id,
119                binding: id,
120                handler: renderer.handlers.attribute
121            };
122
123            if (_.isFunction(config)) {
124                data.handler = config;
125            } else if (_.isObject(config)) {
126                _.extend(data, config);
127            }
128
129            data.id = id;
130            attributes[id] = data;
131
132            return this;
133        },
134
135        /**
136         * Removes specified attribute handler.
137         *
138         * @param {String} id - Attribute identifier.
139         * @returns {Renderer} Chainable.
140         */
141        removeAttribute: function (id) {
142            delete attributes[id];
143
144            return this;
145        },
146
147        /**
148         * Adds new custom node handler.
149         *
150         * @param {String}
150 id - Node identifier.
151         * @param {(Object|Function)} [config={}]
152         * @returns {Renderer} Chainable.
153         */
154        addNode: function (id, config) {
155            var data = {
156                name: id,
157                binding: id,
158                handler: renderer.handlers.node
159            };
160
161            if (_.isFunction(config)) {
162                data.handler = config;
163            } else if (_.isObject(config)) {
164                _.extend(data, config);
165            }
166
167            data.id = id;
168            elements[id] = data;
169
170            return this;
171        },
172
173        /**
174         * Removes specified custom node handler.
175         *
176         * @param {String} id - Node identifier.
177         * @returns {Renderer} Chainable.
178         */
179        removeNode: function (id) {
180            delete elements[id];
181
182            return this;
183        },
184
185        /**
186         * Checks if provided DOM element is a custom node.
187         *
188         * @param {HTMLElement} node - Node to be checked.
189         * @returns {Boolean}
190         */
191        isCustomNode: function (node) {
192            return _.some(elements, function (elem) {
193                return elem.name.toUpperCase() === node.tagName;
194            });
195        },
196
197        /**
198         * Processes custom attributes of a content's child nodes.
199         *
200         * @param {HTMLElement} content - DOM element to be processed.
201         */
202        processAttributes: function (content) {
203            var repeat;
204
205            repeat = _.some(attributes, function (attr) {
206                var attrName = attr.name,
207                    nodes    = content.querySelectorAll('[' + attrName + ']'),
208                    handler  = attr.handler;
209
210                return _.toArray(nodes).some(function (node) {
211                    var data = node.getAttribute(attrName);
212
213                    return handler(node, data, attr) === true;
214                });
215            });
216
217            if (repeat) {
218                renderer.processAttributes(content);
219            }
220        },
221
222        /**
223         * Processes custom nodes of a provided content.
224         *
225         * @param {HTMLElement} content - DOM element to be processed.
226         */
227        processNodes: function (content) {
228            var repeat;
229
230            repeat = _.some(elements, function (element) {
231                var nodes   = content.querySelectorAll(element.name),
232                    handler = element.handler;
233
234                return _.toArray(nodes).some(function (node) {
235                    var data = node.getAttribute('args');
236
237                    return handler(node, data, element) === true;
238                });
239            });
240
241            if (repeat) {
242                renderer.processNodes(content);
243            }
244        },
245
246        /**
247         * Wraps provided string in curly braces if it's necessary.
248         *
249         * @param {String} args - String to be wrapped.
250         * @returns {String} Wrapped string.
251         */
252        wrapArgs: function (args) {
253            if (~args.indexOf('\\:')) {
254                args = args.replace(colonReg, ':');
255            } else if (~args.indexOf(':') && !~args.indexOf('}')) {
256                args = '{' + args + '}';
257            }
258
259            return args;
260        },
261
262        /**
263         * Wraps child nodes of provided DOM element
264         * with knockout's comment tag.
265         *
266         * @param {HTMLElement} node - Node whose children should be wrapped.
267         * @param {String} binding - Name of the binding for the opener comment tag.
268         * @param {String} data - Data associated with a binding.
269         *
270         * @example
271         *      <div id="example"><span/></div>
272         *      wrapChildren(document.getElementById('example'), 'foreach', 'data');
273         *      =>
274         *      <div id="example">
275         *      <!-- ko foreach: data -->
276         *          <span></span>
277         *      <!-- /ko -->
278         *      </div>
279         */
280        wrapChildren: function (node, binding, data) {
281            var tag = this.createComment(binding, data),
282                $node = $(node);
283
284            $node.prepend(tag.open);
285            $node.append(tag.close);
286        },
287
288        /**
289         * Wraps specified node with knockout's comment tag.
290         *
291         * @param {HTMLElement} node - Node to be wrapped.
292         * @param {String} binding - Name of the binding for the opener comment tag.
293         * @param {String} data - Data associated with a binding.
294         *
295         * @example
296         *      <div id="example"></div>
297         *      wrapNode(document.getElementById('example'), 'foreach', 'data');
298         *      =>
299         *      <!-- ko foreach: data -->
300         *          <div id="example"></div>
301         *      <!-- /ko -->
302         */
303        wrapNode: function (node, binding, data) {
304            var tag = this.createComment(binding, data),
305                $node = $(node);
306
307            $node.before(tag.open);
308            $node.after(tag.close);
309        },
310
311        /**
312         * Creates knockouts' comment tag for the provided binding.
313         *
314         * @param {String} binding - Name of the binding.
315         * @param {String} data - Data associated with a binding.
316         * @returns {Object} Object with an open and close comment elements.
317         */
318        createComment: function (binding, data) {
319            return {
320                open: document.createComment(' ko ' + binding + ': ' + data + ' '),
321                close: document.createComment(' /ko ')
322            };
323        }
324    };
325
326    renderer.handlers = {
327
328        /**
329         * Basic node handler. Replaces custom nodes
330         * with a corresponding knockout's comment tag.
331         *
332         * @param {HTMLElement} node - Node to be processed.
333         * @param {String} data
334         * @param {Object} element
335         * @returns {Boolean} True
336         *
337         * @example Sample syntaxes conversions.
338         *      <with args="model">
339         *          <span/>
340         *      </with>
341         *      =>
342         *      <!-- ko with: model-->
343         *          <span/>
344         *      <!-- /ko -->
345         */
346        node: function (node, data, element) {
347            data = renderer.wrapArgs(data);
348
349            renderer.wrapNode(node, element.binding, data);
350            $(node).replaceWith(node.childNodes);
351
352            return true;
353        },
354
355        /**
356         * Base attribute handler. Replaces custom attributes with
357         * a corresponding knockouts' data binding.
358         *
359         * @param {HTMLElement} node - Node to be processed.
360         * @param {String} data - Data associated with a binding.
361         * @param {Object} attr - Attribute definition.
362         *
363         * @example Sample syntaxes conversions.
364         *      <div text="label"></div>
365         *      =>
366         *      <div data-bind="text: label"></div>
367         */
368        attribute: function (node, data, attr) {
369            data = renderer.wrapArgs(data);
370
371            renderer.bindings.add(node, attr.binding, data);
372            node.removeAttribute(attr.name);
373        },
374
375        /**
376         * Wraps provided node with a knockouts' comment tag.
377         *
378         * @param {HTMLElement} node - Node that will be wrapped.
379         * @param {String} data - Data associated with a binding.
380         * @param {Object} attr - Attribute definition.
381         *
382         * @example
383         *      <div outereach="data" class="test"></div>
384         *      =>
385         *      <!-- ko foreach: data -->
386         *          <div class="test"></div>
387         *      <!-- /ko -->
388         */
389        wrapAttribute: function (node, data, attr) {
390            data = renderer.wrapArgs(data);
391
392            renderer.wrapNode(node, attr.binding, data);
393            node.removeAttribute(attr.name);
394        }
395    };
396
397    renderer.bindings = {
398
399        /**
400         * Appends binding string to the current
401         * 'data-bind' attribute of provided node.
402         *
403         * @param {HTMLElement} node - DOM element whose 'data-bind' attribute will be extended.
404         * @param {String} name - Name of a binding.
405         * @param {String} data - Data associated with the binding.
406         */
407        add: function (node, name, data) {
408            var bindings = this.get(node);
409
410            if (bindings) {
411                bindings += ', ';
412            }
413
414            bindings += name;
415
416            if (data) {
417                bindings += ': ' + data;
418            }
419
420            this.set(node, bindings);
421        },
422
423        /**
424         * Extracts value of a 'data-bind' attribute from provided node.
425         *
426         * @param {HTMLElement} node - Node whose attribute to be extracted.
427         * @returns {String}
428         */
429        get: function (node) {
430            return node.getAttribute('data-bind') || '';
431        },
432
433        /**
434         * Sets 'data-bind' attribute of the specified node
435         * to the provided value.
436         *
437         * @param {HTMLElement} node - Node whose attribute will be altered.
438         * @param {String} bindings - New value of 'data-bind' attribute.
439         */
440        set: function (node, bindings) {
441            node.setAttribute('data-bind', bindings);
442        }
443    };
444
445    renderer
446        .addGlobal(renderer.processAttributes)
447        .addGlobal(renderer.processNodes);
448
449    /**
450     * Collection of default binding conversions.
451     */
452    preset = {
453        nodes: _.object([
454            'if',
455            'text',
456            'with',
457            'scope',
458            'ifnot',
459            'foreach',
460            'component'
461        ], Array.prototype),
462        attributes: _.object([
463            'css',
464            'attr',
465            'html',
466            'with',
467            'text',
468            'click',
469            'event',
470            'submit',
471            'enable',
472            'disable',
473            'options',
474            'visible',
475            'template',
476            'hasFocus',
477            'textInput',
478            'component',
479            'uniqueName',
480            'optionsText',
481            'optionsValue',
482            'checkedValue',
483            'selectedOptions'
484        ], Array.prototype)
485    };
486
487    _.extend(preset.attributes, {
488        if: renderer.handlers.wrapAttribute,
489        ifnot: renderer.handlers.wrapAttribute,
490        innerif: {
491            binding: 'if'
492        },
493        innerifnot: {
494            binding: 'ifnot'
495        },
496        outereach: {
497            binding: 'foreach',
498            handler: renderer.handlers.wrapAttribute
499        },
500        foreach: {
501            name: 'each'
502        },
503        value: {
504            name: 'ko-value'
505        },
506        style: {
507            name: 'ko-style'
508        },
509        checked: {
510            name: 'ko-checked'
511        },
512        disabled: {
513            name: 'ko-disabled',
514            binding: 'disable'
515        },
516        focused: {
517            name: 'ko-focused',
518            binding: 'hasFocus'
519        },
520
521        /**
522         * Custom 'render' attribute handler function. Wraps child elements
523         * of a node with knockout's 'ko template:' comment tag.
524         *
525         * @param {HTMLElement} node - Element to be processed.
526         * @param {String} data - Data specified in 'render' attribute of a node.
527         */
528        render: function (node, data) {
529            data = data || 'getTemplate()';
530            data = renderer.wrapArgs(data);
531
532            renderer.wrapChildren(node, 'template', data);
533            node.removeAttribute('render');
534        }
535    });
536
537    _.extend(preset.nodes, {
538        foreach: {
539            name: 'each'
540        },
541
542        /**
543         * Custom 'render' node handler function.
544         * Replaces node with knockout's 'ko template:' comment tag.
545         *
546         * @param {HTMLElement} node - Element to be processed.
547         * @param {String} data - Data specified in 'args' attribute of a node.
548         */
549        render: function (node, data) {
550            data = data || 'getTemplate()';
551            data = renderer.wrapArgs(data);
552
553            renderer.wrapNode(node, 'template', data);
554            $(node).replaceWith(node.childNodes);
555        }
556    });
557
558    _.each(preset.attributes, function (data, id) {
559        renderer.addAttribute(id, data);
560    });
561
562    _.each(preset.nodes, function (data, id) {
563        renderer.addNode(id, data);
564    });
565
566    return renderer;
567});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.