PageSourceSearch

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

js springerpub.com collected 2026-09-24 08:47:42 UTC 9,120 bytes, 252 lines download raw bytes

1/**
2 * Copyright © Magento, Inc. All rights reserved.
3 * See COPYING.txt for license details.
4 */
5define([
6    'jquery',
7    'ko',
8    'underscore',
9    './observable_source',
10    './renderer',
11    '../../logger/console-logger'
12], function ($, ko, _, Source, renderer, consoleLogger) {
13    'use strict';
14
15    var RemoteTemplateEngine,
16        NativeTemplateEngine = ko.nativeTemplateEngine,
17        sources = {};
18
19    /**
20     * Remote template engine class. Is used to be able to load remote templates via knockout template binding.
21     */
22    RemoteTemplateEngine = function () {
23        // Instance reference for closure.
24        var engine = this,
25        // Decorate the builtin Knockout "template" binding to track synchronous template renders.
26        origUpdate = ko.bindingHandlers.template.update;
27
28        /**
29         * Counter to track the number of currently running render tasks (both synchronous and asynchronous).
30         * @type {Number}
31         * @private
32         */
33        this._rendersOutstanding = 0;
34
35        /**
36         * Use a jQuery object as an event bus (but any event emitter with on/off/emit methods could work)
37         * @type {jQuery}
38         * @private
39         */
40        this._events = $(this);
41
42        /**
43         * Rendered templates
44         * @type {Object}
45         * @private
46         */
47        this._templatesRendered = {};
48
49        /*eslint-disable no-unused-vars*/
50        /**
51         * Decorate update method
52         *
53         * @param {HTMLElement} element
54         * @param {Function} valueAccessor
55         * @param {Object} allBindings
56         * @param {Object} viewModel
57         * @param {ko.bindingContext} bindingContext
58         * @returns {*}
59         */
60        ko.bindingHandlers.template.update = function (element, valueAccessor, allBindings, viewModel, bindingContext) {
61            /*eslint-enable no-unused-vars*/
62            var options = ko.utils.peekObservable(valueAccessor()),
63                templateName,
64                isSync,
65                updated;
66
67            if (typeof options === 'object') {
68                if (options.templateEngine && options.templateEngine !== engine) {
69                    return origUpdate.apply(this, arguments);
70                }
71
72                if (!options.name) {
73                    consoleLogger.error('Could not find template name', options);
74                }
75                templateName = options.name;
76            } else if (typeof options === 'string') {
77                templateName = options;
78            } else {
79                consoleLogger.error('Could not build a template binding', options);
80            }
81            engine._trackRender(templateName);
82            isSync = engine._hasTemplateLoaded(templateName);
83            updated = origUpdate.apply(this, arguments);
84
85            if (isSync) {
86                engine._releaseRender(templateName, 'sync');
87            }
88
89            return updated;
90        };
91    };
92
93    /**
94     * Creates unique template identifier based on template name and it's extenders (optional)
95     * @param  {String} templateName
96     * @return {String} - unique template identifier
97     */
98    function createTemplateIdentifier(templateName) {
99        return templateName;
100    }
101
102    RemoteTemplateEngine.prototype = new NativeTemplateEngine;
103    RemoteTemplateEngine.prototype.constructor = RemoteTemplateEngine;
104
105    /**
106     * When an asynchronous render task begins, increment the internal counter for tracking when renders are complete.
107     * @private
108     */
109    RemoteTemplateEngine.prototype._trackRender = function (templateName) {
110        var rendersForTemplate = this._templatesRendered[templateName] !== undefined ?
111            this._templatesRendered[templateName] : 0;
112
113        this._rendersOutstanding++;
114        this._templatesRendered[templateName] = rendersForTemplate + 1;
115        this._resolveRenderWaits();
116    };
117
118    /**
119     * When an asynchronous render task ends, decrement the internal counter for tracking when renders are complete.
120     * @private
121     */
122    RemoteTemplateEngine.prototype._releaseRender = function (templateName) {
123        var rendersForTemplate = this._templatesRendered[templateName];
124
125        this._rendersOutstanding--;
126        this._templatesRendered[templateName] = rendersForTemplate - 1;
127        this._resolveRenderWaits();
128    };
129
130    /**
131     * Check to see if renders are complete and trigger events for listeners.
132     * @private
133     */
134    RemoteTemplateEngine.prototype._resolveRenderWaits = function () {
135        if (this._rendersOutstanding === 0) {
136            this._events.triggerHandler('finishrender');
137        }
138    };
139
140    /**
141     * Get a promise for the end of the current run of renders, both sync and async.
142     * @return {jQueryPromise} - promise that resolves when render completes
143     */
144    RemoteTemplateEngine.prototype.waitForFinishRender = function () {
145        var defer = $.Deferred();
146
147        this._events.one('finishrender', defer.resolve);
148
149        return defer.promise();
150    };
151
152    /**
153     * Returns true if this template has already been asynchronously loaded and will be synchronously rendered.
154     * @param {String} templateName
155     * @returns {Boolean}
156     * @private
157     */
158    RemoteTemplateEngine.prototype._hasTemplateLoaded = function (templateName) {
159        // Sources object will have cached template once makeTemplateSource has run
160        return sources.hasOwnProperty(templateName);
161    };
162
163    /**
164     * Overrided method of native knockout template engine.
165     * Caches template after it's unique name and renders in once.
166     * If template name is not typeof string, delegates work to knockout.templateSources.anonymousTemplate.
167     * @param  {*} template
168     * @param  {HTMLElement} templateDocument - document
169     * @param  {Object} options - options, passed to template binding
170     * @param  {ko.bindingContext} bindingContext
171     * @returns {TemplateSource} Object with methods 'nodes' and 'data'.
172     */
173    RemoteTemplateEngine.prototype.makeTemplateSource = function (template, templateDocument, options, bindingContext) {
174        var engine = this,
175            source,
176            templateId;
177
178        if (typeof template === 'string') {
179            templateId = createTemplateIdentifier(template);
180            source = sources[templateId];
181
182            if (!source) {
183                source = new Source(template);
184                source.requestedBy = bindingContext.$data.name;
185                sources[templateId] = source;
186
187                consoleLogger.info('templateStartLoading', {
188                    template: templateId,
189                    component: bindingContext.$data.name
190                });
191
192                renderer.render(template).then(function (rendered) {
193                    consoleLogger.info('templateLoadedFromServer', {
194                        template: templateId,
195                        component: bindingContext.$data.name
196                    });
197                    source.nodes(rendered);
198                    engine._releaseRender(templateId, 'async');
199                }).fail(function () {
200                    consoleLogger.error('templateLoadingFail', {
201                        template: templateId,
202                        component: bindingContext.$data.name
203                    });
204                });
205            }
206
207            if (source.requestedBy !== bindingContext.$data.name) {
208                consoleLogger.info('templateLoadedFromCache', {
209                    template: templateId,
210                    component: bindingContext.$data.name
211                });
212            }
213
214            return source;
215        } else if (template.nodeType === 1 || template.nodeType === 8) {
216            source = new ko.templateSources.anonymousTemplate(template);
217
218            return source;
219        }
220
221        throw new Error('Unknown template type: ' + template);
222    };
223
224    /**
225     * Overrided method of native knockout template engine.
226     * Should return array of html elements.
227     * @param  {TemplateSource} templateSource - object with methods 'nodes' and 'data'.
228     * @return {Array} - array of html elements
229     */
230    RemoteTemplateEngine.prototype.renderTemplateSource = function (templateSource) {
231        var nodes = templateSource.nodes();
232
233        return ko.utils.cloneNodes(nodes);
234    };
235
236    /**
237     * Overrided method of native knockout template engine.
238     * Created in order to invoke makeTemplateSource method with custom set of params.
239     * @param  {*} template - template identifier
240     * @param  {ko.bindingContext} bindingContext
241     * @param  {Object} options - options, passed to template binding
242     * @param  {HTMLElement} templateDocument - document
243     * @return {Array} - array of html elements
244     */
245    RemoteTemplateEngine.prototype.renderTemplate = function (template, bindingContext, options, templateDocument) {
246        var templateSource = this.makeTemplateSource(template, templateDocument, options, bindingContext);
247
248        return this.renderTemplateSource(templateSource);
249    };
250
251    return new RemoteTemplateEngine;
252});

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.