UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

707 lines (569 loc) • 25.4 kB
"use strict"; require("../../integration/jquery"); var $ = require("jquery"), domAdapter = require("../../core/dom_adapter"), eventsEngine = require("../../events/core/events_engine"), Class = require("../../core/class"), commonUtils = require("../../core/utils/common"), iteratorUtils = require("../../core/utils/iterator"), frameworkUtils = require("../utils"), layoutSets = require("./presets").layoutSets, EventsMixin = require("../../core/events_mixin"), errors = require("../errors"), domUtils = require("../../core/utils/dom"), when = require("../../core/utils/deferred").when, HIDDEN_BAG_ID = "__hidden-bag", TRANSITION_SELECTOR = ".dx-transition", CONTENT_SELECTOR = ".dx-content", DEFAULT_COMMAND_RENDER_STAGE = "onViewShown", CONTENT_RENDERED_EVENT_NAME = "dxcontentrendered.layoutController", PENDING_RENDERING_SELECTOR = ".dx-pending-rendering", PENDING_RENDERING_MANUAL_SELECTOR = ".dx-pending-rendering-manual", TransitionExecutorModule = require("../../animation/transition_executor/transition_executor"); require("./command_container"); require("./view_engine_components"); var transitionSelector = function transitionSelector(transitionName) { return ".dx-transition-" + transitionName; }; var DefaultLayoutController = Class.inherit({ ctor: function ctor(options) { options = options || {}; this.name = options.name || ""; this._layoutModel = options.layoutModel || {}; this._defaultPaneName = options.defaultPaneName || "content"; this._transitionDuration = options.transitionDuration === undefined ? 400 : options.transitionDuration; this._showViewFired = false; }, init: function init(options) { options = options || {}; this._visibleViews = {}; this._$viewPort = options.$viewPort || $("body"); this._commandManager = options.commandManager; this._viewEngine = options.viewEngine; this.transitionExecutor = new TransitionExecutorModule.TransitionExecutor(); this._prepareTemplates(); this._$viewPort.append(this.element()); this._hideElements(this.element()); if (options.templateContext) { this._templateContext = options.templateContext; this._proxiedTemplateContextChangedHandler = this._templateContextChangedHandler.bind(this); } }, ensureActive: function ensureActive(targetNode) { if (this._disabledState) { return this.enable(); } else { return this.activate(targetNode); } }, activate: function activate() { this._showViewFired = false; var $rootElement = this.element(); this._showElements($rootElement); this._attachRefreshViewRequiredHandler(); return $.Deferred().resolve().promise(); }, deactivate: function deactivate() { this._disabledState = false; this._showViewFired = false; this._releaseVisibleViews(); this._hideElements(this.element()); this._detachRefreshViewRequiredHandler(); return $.Deferred().resolve().promise(); }, enable: function enable() { this._disabledState = false; if (!this._showViewFired) { this._notifyShowing(); } this._showViewFired = false; return $.Deferred().resolve().promise(); }, disable: function disable() { this._disabledState = true; this._showViewFired = false; this._notifyHidden(); }, activeViewInfo: function activeViewInfo() { return this._visibleViews[this._defaultPaneName]; }, _fireViewEvents: function _fireViewEvents(eventName, views) { var that = this; views = views || this._visibleViews; iteratorUtils.each(views, function (index, viewInfo) { that.fireEvent(eventName, [viewInfo]); }); }, _notifyShowing: function _notifyShowing(views) { this._fireViewEvents("viewShowing", views); }, _notifyShown: function _notifyShown(views) { this._fireViewEvents("viewShown", views); }, _notifyHidden: function _notifyHidden(views) { this._fireViewEvents("viewHidden", views); }, _applyTemplate: function _applyTemplate($elements, model) { $elements.each(function (i, element) { frameworkUtils.templateProvider.applyTemplate(element, model); }); }, _releaseVisibleViews: function _releaseVisibleViews() { var that = this; iteratorUtils.each(this._visibleViews, function (index, viewInfo) { that._hideView(viewInfo); that._releaseView(viewInfo); }); this._visibleViews = {}; }, _templateContextChangedHandler: function _templateContextChangedHandler() { var that = this, viewsToShow = []; iteratorUtils.each(that._visibleViews, function (index, viewInfo) { if (viewInfo.currentViewTemplateId !== that._getViewTemplateId(viewInfo)) { viewsToShow.push(viewInfo); } }); when.apply($, iteratorUtils.map(viewsToShow, function (viewInfo) { return that.showView(viewInfo); })).done(function () { that._notifyShown(viewsToShow); }); }, _attachRefreshViewRequiredHandler: function _attachRefreshViewRequiredHandler() { if (this._templateContext) { this._templateContext.on("optionChanged", this._proxiedTemplateContextChangedHandler); } }, _detachRefreshViewRequiredHandler: function _detachRefreshViewRequiredHandler() { if (this._templateContextChanged) { this._templateContext.off("optionChanged", this._proxiedTemplateContextChangedHandler); } }, _getPreviousViewInfo: function _getPreviousViewInfo(viewInfo) { return this._visibleViews[this._getViewPaneName(viewInfo.viewTemplateInfo)]; }, _prepareTemplates: function _prepareTemplates() { var that = this; var $layoutTemplate = that._viewEngine.getLayoutTemplate(this._getLayoutTemplateName()); that._$layoutTemplate = $layoutTemplate; that._$mainLayout = that._createEmptyLayout(); that._showElements(that._$mainLayout); that._applyTemplate(that._$mainLayout, that._layoutModel); that._$navigationWidget = that._createNavigationWidget(); }, renderNavigation: function renderNavigation(navigationCommands) { this._clearNavigationWidget(); this._renderNavigationImpl(navigationCommands); }, _renderNavigationImpl: function _renderNavigationImpl(navigationCommands) { this._renderCommands(this._$mainLayout, navigationCommands); }, _createNavigationWidget: function _createNavigationWidget() { var containers = this._findCommandContainers(this._$mainLayout), result; iteratorUtils.each(containers, function (k, container) { if (container.option("id") === "global-navigation") { result = container.element(); return false; } }); return result; }, _clearNavigationWidget: function _clearNavigationWidget() { if (this._$navigationWidget) { this._commandManager.clearContainer(this._$navigationWidget.dxCommandContainer("instance")); } }, element: function element() { return this._$mainLayout; }, _getViewFrame: function _getViewFrame(viewInfo) { return this._$mainLayout; }, _getLayoutTemplateName: function _getLayoutTemplateName() { return this.name; }, _applyModelToTransitionElements: function _applyModelToTransitionElements($markup, model) { var that = this; this._getTransitionElements($markup).each(function (i, item) { that._applyTemplate($(item).children(), model); }); }, _createViewLayoutTemplate: function _createViewLayoutTemplate() { var that = this; var $viewLayoutTemplate = that._$layoutTemplate.clone(); this._hideElements($viewLayoutTemplate); return $viewLayoutTemplate; }, _createEmptyLayout: function _createEmptyLayout() { var that = this; var $result = that._$layoutTemplate.clone(); this._hideElements($result); this._getTransitionElements($result).empty(); $result.children(CONTENT_SELECTOR).remove(); return $result; }, _getTransitionElements: function _getTransitionElements($markup) { var $items = $markup.find(TRANSITION_SELECTOR).add($markup.filter(TRANSITION_SELECTOR)), result = []; for (var i = 0; i < $items.length; i++) { var $item = $items.eq(i); if ($item.parents(TRANSITION_SELECTOR).length === 0) { result.push($item.get(0)); } } return $(result); }, showView: function showView(viewInfo, direction) { direction = direction || "forward"; var that = this, previousViewInfo = that._getPreviousViewInfo(viewInfo), previousViewTemplateId = previousViewInfo === viewInfo ? previousViewInfo.currentViewTemplateId : undefined, result; this._showViewFired = true; this._updateCurrentViewTemplateId(viewInfo); if (previousViewTemplateId && previousViewTemplateId === viewInfo.currentViewTemplateId && viewInfo === previousViewInfo) { that.fireEvent("viewShowing", [viewInfo, direction]); result = $.Deferred().resolve().promise(); } else { that._ensureViewRendered(viewInfo); that.fireEvent("viewShowing", [viewInfo, direction]); result = this._showViewImpl(viewInfo, direction, previousViewTemplateId /* TODO Try to invent a better solution */).done(function () { that._onViewShown(viewInfo); }); } return result; }, disposeView: function disposeView(viewInfo) { this._clearRenderResult(viewInfo); }, _clearRenderResult: function _clearRenderResult(viewInfo) { if (viewInfo.renderResult) { viewInfo.renderResult.$markup.remove(); viewInfo.renderResult.$viewItems.remove(); delete viewInfo.renderResult; } }, _renderViewImpl: function _renderViewImpl($viewTemplate, viewInfo) { var that = this, allowedChildrenSelector = ".dx-command,.dx-content,script", $layout = this._createViewLayoutTemplate(), $viewItems, isSimplifiedMarkup = true, outOfContentItems = $(); if ($viewTemplate.children(allowedChildrenSelector).length === 0) { this._viewEngine._wrapViewDefaultContent($viewTemplate); } $viewItems = $viewTemplate.children(); this._applyModelToTransitionElements($layout, viewInfo.model); this._viewEngine.applyLayout($viewTemplate, $layout); $viewItems.each(function (i, item) { var $item = $(item); that._applyTemplate($item, viewInfo.model); if ($item.is(allowedChildrenSelector)) { isSimplifiedMarkup = false; } else { outOfContentItems = outOfContentItems.add($item); } }); if (outOfContentItems.length && !isSimplifiedMarkup) { throw errors.Error("E3014", outOfContentItems[0].outerHTML); } viewInfo.renderResult = viewInfo.renderResult || {}; viewInfo.renderResult.$viewItems = $viewItems; viewInfo.renderResult.$markup = $layout; }, _renderCommands: function _renderCommands($markup, commands) { var commandContainers = this._findCommandContainers($markup); return this._commandManager.renderCommandsToContainers(commands, commandContainers); }, _prepareViewCommands: function _prepareViewCommands(viewInfo) { var $viewItems = viewInfo.renderResult.$viewItems, viewCommands = this._commandManager.findCommands($viewItems), commandsToRenderMap = {}; viewInfo.commands = frameworkUtils.utils.mergeCommands(viewInfo.commands || [], viewCommands); viewInfo.commandsToRenderMap = commandsToRenderMap; iteratorUtils.each(viewInfo.commands, function (index, command) { var renderStage = command.option("renderStage") || DEFAULT_COMMAND_RENDER_STAGE, targetArray = commandsToRenderMap[renderStage] = commandsToRenderMap[renderStage] || []; targetArray.push(command); }); }, _applyViewCommands: function _applyViewCommands(viewInfo, renderStage) { renderStage = renderStage || DEFAULT_COMMAND_RENDER_STAGE; var commandsToRender = viewInfo.commandsToRenderMap[renderStage], $markup = viewInfo.renderResult.$markup, result; if (commandsToRender) { result = this._renderCommands($markup, commandsToRender); delete viewInfo.commandsToRenderMap[renderStage]; } else { result = $.Deferred().resolve().promise(); } return result; }, _findCommandContainers: function _findCommandContainers($markup) { // TODO remove this (do that on start in viewEngine) return domUtils.createComponents($markup, ["dxCommandContainer"]); }, _getViewTemplateId: function _getViewTemplateId(viewInfo) { var viewTemplateInstance = viewInfo.$viewTemplate ? viewInfo.$viewTemplate.dxView("instance") : this._viewEngine.getViewTemplateInfo(viewInfo.viewName); return viewTemplateInstance.getId(); }, _updateCurrentViewTemplateId: function _updateCurrentViewTemplateId(viewInfo) { viewInfo.currentViewTemplateId = this._getViewTemplateId(viewInfo); }, _ensureViewRendered: function _ensureViewRendered(viewInfo) { var $cachedMarkup = viewInfo.renderResult && viewInfo.renderResult.markupCache[viewInfo.currentViewTemplateId]; if ($cachedMarkup) { viewInfo.renderResult.$markup = $cachedMarkup; } else { this._renderView(viewInfo); viewInfo.renderResult.markupCache = viewInfo.renderResult.markupCache || {}; viewInfo.renderResult.markupCache[viewInfo.currentViewTemplateId] = viewInfo.renderResult.$markup; } }, _renderView: function _renderView(viewInfo) { var $viewTemplate = viewInfo.$viewTemplate || this._viewEngine.getViewTemplate(viewInfo.viewName); this._renderViewImpl($viewTemplate, viewInfo); this._prepareViewCommands(viewInfo); this._applyViewCommands(viewInfo, "onViewRendering"); this._appendViewToLayout(viewInfo); $viewTemplate.remove(); this._onRenderComplete(viewInfo); this.fireEvent("viewRendered", [viewInfo]); }, _prepareTransition: function _prepareTransition($element, targetPlaceholderName) { if ($element.children(".dx-content").length === 0) { $element.wrapInner("<div>"); $element.children().dxContent({ targetPlaceholder: targetPlaceholderName }); } }, _appendViewToLayout: function _appendViewToLayout(viewInfo) { var that = this, $viewFrame = that._getViewFrame(viewInfo), $markup = viewInfo.renderResult.$markup, $transitionContentElements = $(), animationItems = []; iteratorUtils.each($markup.find(".dx-content-placeholder"), function (index, el) { that._prepareTransition($(el), $(el).attr("data-dx-content-placeholder-name")); }); iteratorUtils.each(that._getTransitionElements($viewFrame), function (index, transitionElement) { var $transition = $(transitionElement), $viewElement = $markup.find(transitionSelector($transition.attr("data-dx-transition-name"))).children(), animationItem = { $element: $viewElement, animation: $transition.attr("data-dx-transition-type") }; animationItems.push(animationItem); $transition.append($viewElement); that._showViewElements($viewElement); domUtils.triggerShownEvent($viewElement); $transitionContentElements = $transitionContentElements.add($viewElement); }); that._$mainLayout.append(viewInfo.renderResult.$viewItems.filter(".dx-command")); $markup.remove(); viewInfo.renderResult.$markup = $transitionContentElements; viewInfo.renderResult.animationItems = animationItems; }, _onRenderComplete: function _onRenderComplete(viewInfo) {}, _onViewShown: function _onViewShown(viewInfo) { eventsEngine.trigger(domAdapter.getDocument(), "dx.viewchanged"); }, _enter: function _enter(animationItems, animationModifier) { var transitionExecutor = this.transitionExecutor; iteratorUtils.each(animationItems, function (index, item) { transitionExecutor.enter(item.$element, item.animation, animationModifier); }); }, _leave: function _leave(animationItems, animationModifier) { var transitionExecutor = this.transitionExecutor; iteratorUtils.each(animationItems, function (index, item) { transitionExecutor.leave(item.$element, item.animation, animationModifier); }); }, _doTransition: function _doTransition(oldViewInfo, newViewInfo, animationModifier) { if (oldViewInfo) { this._leave(oldViewInfo.renderResult.animationItems, animationModifier); } this._enter(newViewInfo.renderResult.animationItems, animationModifier); this._showView(newViewInfo); return this.transitionExecutor.start(); }, _showViewImpl: function _showViewImpl(viewInfo, direction, previousViewTemplateId) { var that = this, previousViewInfo = this._getPreviousViewInfo(viewInfo), animationModifier = { direction: direction }; if (previousViewInfo === viewInfo) { previousViewInfo = undefined; } if (!previousViewInfo) { animationModifier.duration = 0; animationModifier.delay = 0; } var d = $.Deferred(); that._doTransition(previousViewInfo, viewInfo, animationModifier).done(function () { that._changeView(viewInfo, previousViewTemplateId).done(function (result) { d.resolve(result); }); }); return d.promise(); }, _releaseView: function _releaseView(viewInfo) { this.fireEvent("viewReleased", [viewInfo]); }, _getReadyForRenderDeferredItems: function _getReadyForRenderDeferredItems(viewInfo) { return $.Deferred().resolve().promise(); }, _changeView: function _changeView(viewInfo, previousViewTemplateId) { var that = this; if (previousViewTemplateId) { that._hideView(viewInfo, previousViewTemplateId); } else { var previousViewInfo = that._getPreviousViewInfo(viewInfo); if (previousViewInfo && previousViewInfo !== viewInfo) { that._hideView(previousViewInfo); that._releaseView(previousViewInfo); } this._visibleViews[this._getViewPaneName(viewInfo.viewTemplateInfo)] = viewInfo; } this._subscribeToDeferredItems(viewInfo); var d = $.Deferred(); this._getReadyForRenderDeferredItems(viewInfo).done(function () { that._applyViewCommands(viewInfo).done(function () { that._renderDeferredItems(viewInfo.renderResult.$markup).done(function () { d.resolve(); }); }); }); return d.promise(); }, _subscribeToDeferredItems: function _subscribeToDeferredItems(viewInfo) { var that = this, $markup = viewInfo.renderResult.$markup; $markup.find(PENDING_RENDERING_SELECTOR).add($markup.filter(PENDING_RENDERING_SELECTOR)).each(function () { var eventData = { viewInfo: viewInfo, context: that }; $(this).on(CONTENT_RENDERED_EVENT_NAME, eventData, that._onDeferredContentRendered); }); }, _onDeferredContentRendered: function _onDeferredContentRendered(event) { var $element = $(event.target), viewInfo = event.data.viewInfo, that = event.data.context; $element.off(CONTENT_RENDERED_EVENT_NAME, that._onDeferredContentRendered); that._renderCommands($element, viewInfo.commands); }, _renderDeferredItems: function _renderDeferredItems($items) { var that = this, result = $.Deferred(); var $pendingItem = $items.find(PENDING_RENDERING_MANUAL_SELECTOR).add($items.filter(PENDING_RENDERING_MANUAL_SELECTOR)).first(); if ($pendingItem.length) { var render = $pendingItem.data("dx-render-delegate"); commonUtils.executeAsync(function () { render().done(function () { that._renderDeferredItems($items).done(function () { result.resolve(); }); }); }); } else { result.resolve(); } return result.promise(); }, _getViewPaneName: function _getViewPaneName(viewTemplateInfo) { return this._defaultPaneName; }, _hideElements: function _hideElements($elements) { // we can't use $.hide/show because of B250423, T173009 $elements.addClass("dx-fast-hidden"); }, _showElements: function _showElements($elements) { $elements.removeClass("dx-fast-hidden"); }, _hideViewElements: function _hideViewElements($elements) { this._patchIds($elements); this._disableInputs($elements); $elements.removeClass("dx-active-view").addClass("dx-inactive-view"); }, _hideView: function _hideView(viewInfo, templateId) { if (viewInfo.renderResult) { var $markupToHide = templateId === undefined ? viewInfo.renderResult.$markup : viewInfo.renderResult.markupCache[templateId]; this._hideViewElements($markupToHide); this.fireEvent("viewHidden", [viewInfo]); } }, _showViewElements: function _showViewElements($elements) { this._unPatchIds($elements); this._enableInputs($elements); $elements.removeClass("dx-inactive-view").addClass("dx-active-view"); this._skipAnimation($elements); }, _showView: function _showView(viewInfo) { if (viewInfo.renderResult) { this._showViewElements(viewInfo.renderResult.$markup); } }, _skipAnimation: function _skipAnimation($elements) { $elements.addClass("dx-skip-animation"); for (var i = 0; i < $elements.length; i++) { $elements.eq(i).css("transform"); // force css class to apply immediately } $elements.removeClass("dx-skip-animation"); }, _patchIds: function _patchIds($markup) { this._processIds($markup, function (id) { var result = id; if (id.indexOf(HIDDEN_BAG_ID) === -1) { result = HIDDEN_BAG_ID + "-" + id; } return result; }); }, _unPatchIds: function _unPatchIds($markup) { this._processIds($markup, function (id) { var result = id; if (id.indexOf(HIDDEN_BAG_ID) === 0) { result = id.substr(HIDDEN_BAG_ID.length + 1); } return result; }); }, _processIds: function _processIds($markup, process) { var elementsWithIds = $markup.find("[id]"); iteratorUtils.each(elementsWithIds, function (index, element) { var $el = $(element), id = $el.attr("id"); $el.attr("id", process(id)); }); }, _enableInputs: function _enableInputs($markup) { var $inputs = this._getInputs($markup).filter("[data-disabled='true']"); iteratorUtils.each($inputs, function (index, input) { $(input).removeAttr("disabled").removeAttr("data-disabled"); }); }, _disableInputs: function _disableInputs($markup) { var $inputs = this._getInputs($markup); $inputs = $inputs.filter(":not([disabled])").add($inputs.filter("[disabled=true]")); iteratorUtils.each($inputs, function (index, input) { $(input).attr({ "disabled": true, "data-disabled": true }); }); }, _getInputs: function _getInputs($markup) { return $markup.find("input, button, select, textarea"); } }).include(EventsMixin); layoutSets["default"] = layoutSets["default"] || []; layoutSets["default"].push({ controller: new DefaultLayoutController() }); exports.DefaultLayoutController = DefaultLayoutController; exports.layoutSets = layoutSets;