| Current Path : /home/rtorresani/www/vendor/magento/module-ui/view/base/web/js/lib/knockout/template/ |
| Current File : //home/rtorresani/www/vendor/magento/module-ui/view/base/web/js/lib/knockout/template/engine.js |
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
define([
'jquery',
'ko',
'underscore',
'./observable_source',
'./renderer',
'../../logger/console-logger'
], function ($, ko, _, Source, renderer, consoleLogger) {
'use strict';
var RemoteTemplateEngine,
NativeTemplateEngine = ko.nativeTemplateEngine,
sources = {};
/**
* Remote template engine class. Is used to be able to load remote templates via knockout template binding.
*/
RemoteTemplateEngine = function () {
// Instance reference for closure.
var engine = this,
// Decorate the builtin Knockout "template" binding to track synchronous template renders.
origUpdate = ko.bindingHandlers.template.update;
/**
* Counter to track the number of currently running render tasks (both synchronous and asynchronous).
* @type {Number}
* @private
*/
this._rendersOutstanding = 0;
/**
* Use a jQuery object as an event bus (but any event emitter with on/off/emit methods could work)
* @type {jQuery}
* @private
*/
this._events = $(this);
/**
* Rendered templates
* @type {Object}
* @private
*/
this._templatesRendered = {};
/*eslint-disable no-unused-vars*/
/**
* Decorate update method
*
* @param {HTMLElement} element
* @param {Function} valueAccessor
* @param {Object} allBindings
* @param {Object} viewModel
* @param {ko.bindingContext} bindingContext
* @returns {*}
*/
ko.bindingHandlers.template.update = function (element, valueAccessor, allBindings, viewModel, bindingContext) {
/*eslint-enable no-unused-vars*/
var options = ko.utils.peekObservable(valueAccessor()),
templateName,
isSync,
updated;
if (typeof options === 'object') {
if (options.templateEngine && options.templateEngine !== engine) {
return origUpdate.apply(this, arguments);
}
if (!options.name) {
consoleLogger.error('Could not find template name', options);
}
templateName = options.name;
} else if (typeof options === 'string') {
templateName = options;
} else {
consoleLogger.error('Could not build a template binding', options);
}
engine._trackRender(templateName);
isSync = engine._hasTemplateLoaded(templateName);
updated = origUpdate.apply(this, arguments);
if (isSync) {
engine._releaseRender(templateName, 'sync');
}
return updated;
};
};
/**
* Creates unique template identifier based on template name and it's extenders (optional)
* @param {String} templateName
* @return {String} - unique template identifier
*/
function createTemplateIdentifier(templateName) {
return templateName;
}
RemoteTemplateEngine.prototype = new NativeTemplateEngine;
RemoteTemplateEngine.prototype.constructor = RemoteTemplateEngine;
/**
* When an asynchronous render task begins, increment the internal counter for tracking when renders are complete.
* @private
*/
RemoteTemplateEngine.prototype._trackRender = function (templateName) {
var rendersForTemplate = this._templatesRendered[templateName] !== undefined ?
this._templatesRendered[templateName] : 0;
this._rendersOutstanding++;
this._templatesRendered[templateName] = rendersForTemplate + 1;
this._resolveRenderWaits();
};
/**
* When an asynchronous render task ends, decrement the internal counter for tracking when renders are complete.
* @private
*/
RemoteTemplateEngine.prototype._releaseRender = function (templateName) {
var rendersForTemplate = this._templatesRendered[templateName];
this._rendersOutstanding--;
this._templatesRendered[templateName] = rendersForTemplate - 1;
this._resolveRenderWaits();
};
/**
* Check to see if renders are complete and trigger events for listeners.
* @private
*/
RemoteTemplateEngine.prototype._resolveRenderWaits = function () {
if (this._rendersOutstanding === 0) {
this._events.triggerHandler('finishrender');
}
};
/**
* Get a promise for the end of the current run of renders, both sync and async.
* @return {jQueryPromise} - promise that resolves when render completes
*/
RemoteTemplateEngine.prototype.waitForFinishRender = function () {
var defer = $.Deferred();
this._events.one('finishrender', defer.resolve);
return defer.promise();
};
/**
* Returns true if this template has already been asynchronously loaded and will be synchronously rendered.
* @param {String} templateName
* @returns {Boolean}
* @private
*/
RemoteTemplateEngine.prototype._hasTemplateLoaded = function (templateName) {
// Sources object will have cached template once makeTemplateSource has run
return sources.hasOwnProperty(templateName);
};
/**
* Overrided method of native knockout template engine.
* Caches template after it's unique name and renders in once.
* If template name is not typeof string, delegates work to knockout.templateSources.anonymousTemplate.
* @param {*} template
* @param {HTMLElement} templateDocument - document
* @param {Object} options - options, passed to template binding
* @param {ko.bindingContext} bindingContext
* @returns {TemplateSource} Object with methods 'nodes' and 'data'.
*/
RemoteTemplateEngine.prototype.makeTemplateSource = function (template, templateDocument, options, bindingContext) {
var engine = this,
source,
templateId;
if (typeof template === 'string') {
templateId = createTemplateIdentifier(template);
source = sources[templateId];
if (!source) {
source = new Source(template);
source.requestedBy = bindingContext.$data.name;
sources[templateId] = source;
consoleLogger.info('templateStartLoading', {
template: templateId,
component: bindingContext.$data.name
});
renderer.render(template).then(function (rendered) {
consoleLogger.info('templateLoadedFromServer', {
template: templateId,
component: bindingContext.$data.name
});
source.nodes(rendered);
engine._releaseRender(templateId, 'async');
}).fail(function () {
consoleLogger.error('templateLoadingFail', {
template: templateId,
component: bindingContext.$data.name
});
});
}
if (source.requestedBy !== bindingContext.$data.name) {
consoleLogger.info('templateLoadedFromCache', {
template: templateId,
component: bindingContext.$data.name
});
}
return source;
} else if (template.nodeType === 1 || template.nodeType === 8) {
source = new ko.templateSources.anonymousTemplate(template);
return source;
}
throw new Error('Unknown template type: ' + template);
};
/**
* Overrided method of native knockout template engine.
* Should return array of html elements.
* @param {TemplateSource} templateSource - object with methods 'nodes' and 'data'.
* @return {Array} - array of html elements
*/
RemoteTemplateEngine.prototype.renderTemplateSource = function (templateSource) {
var nodes = templateSource.nodes();
return ko.utils.cloneNodes(nodes);
};
/**
* Overrided method of native knockout template engine.
* Created in order to invoke makeTemplateSource method with custom set of params.
* @param {*} template - template identifier
* @param {ko.bindingContext} bindingContext
* @param {Object} options - options, passed to template binding
* @param {HTMLElement} templateDocument - document
* @return {Array} - array of html elements
*/
RemoteTemplateEngine.prototype.renderTemplate = function (template, bindingContext, options, templateDocument) {
var templateSource = this.makeTemplateSource(template, templateDocument, options, bindingContext);
return this.renderTemplateSource(templateSource);
};
return new RemoteTemplateEngine;
});