UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

335 lines (334 loc) • 13.7 kB
/** * DevExtreme (framework/html/view_engine.js) * Version: 18.1.3 * Build date: Tue May 15 2018 * * Copyright (c) 2012 - 2018 Developer Express Inc. ALL RIGHTS RESERVED * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/ */ "use strict"; require("../../integration/jquery"); var $ = require("jquery"), version = require("../../core/version"), window = require("../../core/utils/window").getWindow(), Class = require("../../core/class"), Callbacks = require("../../core/utils/callbacks"), commonUtils = require("../../core/utils/common"), each = require("../../core/utils/iterator").each, inArray = require("../../core/utils/array").inArray, errors = require("../errors"), domUtils = require("../../core/utils/dom"), when = require("../../core/utils/deferred").when, ajax = require("../../core/utils/ajax"), _VIEW_ROLE = "dxView", _LAYOUT_ROLE = "dxLayout", MARKUP_TEMPLATE_MARKER = "MarkupTemplate:"; require("./view_engine_components"); var ViewEngine = Class.inherit({ ctor: function(options) { options = options || {}; this.$root = options.$root; this.device = options.device || {}; this.dataOptionsAttributeName = options.dataOptionsAttributeName || "data-options"; this._templateMap = {}; this._pendingViewContainer = null; this.markupLoaded = Callbacks(); this._templateContext = options.templateContext; this._$skippedMarkup = $(); if (void 0 !== options.templatesVersion && options.templateCacheStorage && this._isReleaseVersion()) { this._templateCacheEnabled = true; this._templatesVersion = "v_" + options.templatesVersion; this._templateCacheStorage = options.templateCacheStorage; this._templateCacheKey = "dxTemplateCache_" + version + "_" + JSON.stringify(this.device) } }, _isReleaseVersion: function() { return !/http:\/\/localhost/.test(window.location.href) }, _enumerateTemplates: function(processFn) { var that = this; each(that._templateMap, function(name, templatesByRoleMap) { each(templatesByRoleMap, function(role, templates) { each(templates, function(index, template) { processFn(template) }) }) }) }, _findComponent: function(name, role) { var components = (this._templateMap[name] || {})[role] || [], filter = this._templateContext && this._templateContext.option() || {}; components = this._filterTemplates(filter, components); this._checkMatchedTemplates(components); return components[0] }, _findTemplate: function(name, role) { var component = this._findComponent(name, role); if (!component) { this._clearCache(); throw errors.Error("E3013", role, name) } var $result, $template = component.element(); if (!component._isStaticComponentsCreated) { domUtils.createComponents($template, ["dxContent", "dxContentPlaceholder", "dxTransition"]); component._isStaticComponentsCreated = true } $result = $template.clone().removeClass("dx-hidden"); return $result }, _clearCache: function() { if (this._templateCacheEnabled) { this._templateCacheStorage.removeItem(this._templateCacheKey) } }, _loadTemplatesFromMarkupCore: function($markup) { var that = this; if ($markup.find("[data-dx-role]").length) { throw errors.Error("E3019") } that.markupLoaded.fire({ markup: $markup }); var components = domUtils.createComponents($markup, [_VIEW_ROLE, _LAYOUT_ROLE]); each(components, function(index, component) { var $element = component.element(); $element.addClass("dx-hidden"); that._registerTemplateComponent(component); component.element().detach() }); var $skipped = $markup.filter("script"); $skipped.appendTo(that.$root); that._$skippedMarkup = that._$skippedMarkup.add($skipped) }, _registerTemplateComponent: function(component) { var role = component.NAME, options = component.option(), templateName = options.name, componentsByRoleMap = this._templateMap[templateName] || {}; componentsByRoleMap[role] = componentsByRoleMap[role] || []; componentsByRoleMap[role].push(component); this._templateMap[templateName] = componentsByRoleMap }, _applyPartialViews: function($render) { var that = this; domUtils.createComponents($render, ["dxViewPlaceholder"]); each($render.find(".dx-view-placeholder"), function() { var $partialPlaceholder = $(this); if ($partialPlaceholder.children().length) { return } var viewName = $partialPlaceholder.data("dxViewPlaceholder").option("viewName"), $view = that._findTemplate(viewName, _VIEW_ROLE); that._applyPartialViews($view); $partialPlaceholder.append($view); $view.removeClass("dx-hidden") }) }, _ajaxImpl: function() { return ajax.sendRequest.apply($, arguments) }, _loadTemplatesFromURL: function(url) { var that = this, winPhonePrefix = this._getWinPhonePrefix(), deferred = $.Deferred(); url = winPhonePrefix + url; this._ajaxImpl({ url: url, dataType: "html" }).done(function(data) { that._loadTemplatesFromMarkupCore(domUtils.createMarkupFromString(data)); deferred.resolve() }).fail(function(jqXHR, textStatus, errorThrown) { var error = errors.Error("E3021", url, errorThrown); deferred.reject(error) }); return deferred.promise() }, _getWinPhonePrefix: function() { if (window.location.protocol.indexOf("wmapp") >= 0) { return window.location.protocol + "www/" } return "" }, _loadExternalTemplates: function() { var tasks = [], that = this; $("head").find("link[rel='dx-template']").each(function(index, link) { var task = that._loadTemplatesFromURL($(link).attr("href")); tasks.push(task) }); return when.apply($, tasks) }, _processTemplates: function() { var that = this; each(that._templateMap, function(name, templatesByRoleMap) { each(templatesByRoleMap, function(role, templates) { that._filterTemplatesByDevice(templates) }) }); that._enumerateTemplates(function(template) { that._applyPartialViews(template.element()) }) }, _filterTemplatesByDevice: function(components) { var filteredComponents = this._filterTemplates(this.device, components); each(components, function(index, component) { if (inArray(component, filteredComponents) < 0) { component.element().remove() } }); components.length = 0; components.push.apply(components, filteredComponents) }, _filterTemplates: function(filter, components) { return commonUtils.findBestMatches(filter, components, function(component) { return component.option() }) }, _checkMatchedTemplates: function(bestMatches) { if (bestMatches.length > 1) { var message = ""; each(bestMatches, function(index, match) { message += match.element().attr("data-options") + "\r\n" }); throw errors.Error("E3020", message, JSON.stringify(this.device)) } }, _wrapViewDefaultContent: function($viewTemplate) { $viewTemplate.wrapInner('<div class="dx-full-height"></div>'); $viewTemplate.children().eq(0).dxContent({ targetPlaceholder: "content" }) }, _initDefaultLayout: function() { this._$defaultLayoutTemplate = $('<div class="dx-full-height" data-options="dxLayout : { name: \'default\' } "> \n <div class="dx-full-height" data-options="dxContentPlaceholder : { name: \'content\' } " ></div> \n</div>'); domUtils.createComponents(this._$defaultLayoutTemplate) }, _getDefaultLayoutTemplate: function() { return this._$defaultLayoutTemplate.clone() }, applyLayout: function($view, $layout) { if (void 0 === $layout || 0 === $layout.length) { $layout = this._getDefaultLayoutTemplate() } if (0 === $view.children(".dx-content").length) { this._wrapViewDefaultContent($view) } var $toMerge = $().add($layout).add($view); var $placeholderContents = $toMerge.find(".dx-content"); each($placeholderContents, function() { var $placeholderContent = $(this); var placeholderId = $placeholderContent.attr("data-dx-target-placeholder-id"); var $placeholder = $toMerge.find(".dx-content-placeholder-" + placeholderId); $placeholder.empty(); $placeholder.append($placeholderContent) }); for (var i = $placeholderContents.length; i >= 0; i--) { var $item = $placeholderContents.eq(i); if (!$item.is(".dx-content-placeholder .dx-content")) { $item.remove() } } return $layout }, _loadTemplatesFromCache: function() { if (!this._templateCacheEnabled) { return } var cache; var fromJSONInterceptor = function(key, value) { if ("string" === typeof value && 0 === value.indexOf(MARKUP_TEMPLATE_MARKER)) { var data = JSON.parse(value.substr(MARKUP_TEMPLATE_MARKER.length)), type = data.type, options = data.options, $markup = domUtils.createMarkupFromString(data.markup); options.fromCache = true; return $markup[type](options)[type]("instance") } else { if ("skippedMarkup" === key) { return $("<div>").append(domUtils.createMarkupFromString(value)).contents() } } return value }; var toParse = this._templateCacheStorage.getItem(this._templateCacheKey); if (toParse) { try { var cacheContainer = JSON.parse(toParse, fromJSONInterceptor); cache = cacheContainer[this._templatesVersion] } catch (e) { this._clearCache() } } if (!cache) { return } this._templateMap = cache.templates; this.$root.append(cache.skippedMarkup); return true }, _putTemplatesToCache: function() { if (!this._templateCacheEnabled) { return } var toJSONInterceptor = function(key, value) { if (value && value.element) { return MARKUP_TEMPLATE_MARKER + JSON.stringify({ markup: value.element().prop("outerHTML"), options: value.option(), type: value.NAME }) } else { if ("skippedMarkup" === key) { return $("<div>").append(value.clone()).html() } } return value }; var cacheContainer = {}; cacheContainer[this._templatesVersion] = { templates: this._templateMap, skippedMarkup: this._$skippedMarkup }; this._templateCacheStorage.setItem(this._templateCacheKey, JSON.stringify(cacheContainer, toJSONInterceptor, 4)) }, init: function() { var that = this; this._initDefaultLayout(); if (!this._loadTemplatesFromCache()) { that._loadTemplatesFromMarkupCore(that.$root.children()); return this._loadExternalTemplates().done(function() { that._processTemplates(); that._putTemplatesToCache() }) } else { return $.Deferred().resolve().promise() } }, getViewTemplate: function(viewName) { return this._findTemplate(viewName, _VIEW_ROLE) }, getViewTemplateInfo: function(name) { return this._findComponent(name, _VIEW_ROLE) }, getLayoutTemplate: function(layoutName) { if (!layoutName) { return this._getDefaultLayoutTemplate() } return this._findTemplate(layoutName, _LAYOUT_ROLE) }, getLayoutTemplateInfo: function(name) { return this._findComponent(name, _LAYOUT_ROLE) }, loadTemplates: function(source) { var result; if ("string" === typeof source) { result = this._loadTemplatesFromURL(source) } else { this._loadTemplatesFromMarkupCore(source); result = $.Deferred().resolve().promise() } return result.done(this._processTemplates.bind(this)) } }); exports.ViewEngine = ViewEngine;