'use strict'; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; /** * Licensed Materials - Property of IBM * IBM Cognos Products: dashboard * (C) Copyright IBM Corp. 2013, 2020 * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp. */ define(['../../lib/@waca/core-client/js/core-client/ui/core/Class', '../../view/features/content/contentViewDom/api/impl/ContentViewDomImpl', 'underscore', 'jquery', '../../app/util/EventChainLocal', './LayoutHelper', '../../lib/@waca/dashboard-common/dist/core/APIFactory', '../../api/deprecated/CanvasAPI', '../../canvas/DeprecatedCanvas', './authoring/interaction/Controller', './authoring/interaction/strategy/Common', '../../lib/@waca/core-client/js/core-client/utils/Deferred', '../../lib/@waca/core-client/js/core-client/utils/ClassFactory'], function (Class, ContentViewDomImpl, _, $, EventChainLocal, LayoutHelper, APIFactory, DeprecatedCanvasAPI, DeprecatedCanvas, Controller, CommonInteraction, Deferred, ClassFactory) { var LayoutController = null; // declaration LayoutController = Class.extend([], { init: function init(options) { LayoutController.inherited('init', this, arguments); this.contentFeatureLoader = options.contentFeatureLoader; //TODO - to be remove after refactoring this.services = options.services; this.appSettings = options.appSettings || {}; if (this.appSettings.options && this.appSettings.options.config) { this.config = this.appSettings.options.config; } else { this.config = {}; } // TODO - end this.dashboardApi = options.dashboardApi; this.canvas = options.canvas; this.glassContext = options.glassContext; this.transaction = this.dashboardApi.getFeature('Transaction'); this.contentTypeRegistry = this.dashboardApi.getFeature('ContentTypeRegistry'); this.isAuthoring = this.dashboardApi.getMode() === 'authoring'; this.id = _.uniqueId('layoutController'); this.$el = options.$el; this.viewsMap = {}; this.boardModel = options.boardModel; this.boardModuleFactory = options.boardModuleFactory; this.interactionControllerDfd = new Deferred(); this.widgetLoader = options.widgetLoader; this.canvasController = options.canvasController; this.logger = this.dashboardApi.getGlassCoreSvc('.Logger'); this.colorsService = this.dashboardApi.getFeature('Colors'); if (this.colorsService) { this.colorsService.on('theme:changed', this.onThemeChange, this); this.colorsService.on('colorSet:changed', this.onColorSetChanged, this); } this.topLayoutModel = this.boardModel.layout; this.topLayoutModel.on('change:layoutPositioning', this._onLayoutPositioning, this); this._setupViewModules(options.layoutExtensions || {}); this.canvasNotifier = options.canvasNotifier; this.interactionController = null; this.eventRouter = options.eventRouter; if (this.eventRouter) { this.eventRouter.on('canvas:selectWidget', this.selectWidget, this); this.eventRouter.on('canvas:deselectWidget', this.deselectWidget, this); this.eventRouter.on('scene:select', this.deselectAllWidgets, this); this.eventRouter.on('scene:next', this.deselectAllWidgets, this); this.eventRouter.on('scene:previous', this.deselectAllWidgets, this); } this.gatewayUrl = options.gatewayUrl; this.cdnUrl = options.cdnUrl; this._init(); // Create the deprecated canvas // This is the canvas implementation for all the deprecated APIs // It is is created in here because the deprecated APIs depend on the layout controller and the layout and widget views // The non deprecated canvas API is created earlier in the boardLoader. this.deprecatedCanvas = new DeprecatedCanvas(this.dashboardApi, this.boardModel, this.canvasController, this, this.widgetLoader); }, initialize: function initialize() { return this.pageReady; }, getDeprecatedCanvas: function getDeprecatedCanvas() { // Merge the public canvas api with the deprecated one // Eventually , the layout controller will stop creating the deprecated one onces all deprecated method are no longer used if (!this.mergedCanvasAPI) { this.mergedCanvasAPI = _.extend(this.deprecatedCanvas.getAPI(), this.canvas); } return this.mergedCanvasAPI; }, _setupViewModules: function _setupViewModules(layoutExtensions) { this.consumeViews = layoutExtensions.consumeViews || {}; this.authoringViews = layoutExtensions.authoringViews || {}; }, /** If the layout has 'subviews' (ie. tabs or scenes), return the id of the current one. */ getCurrentSubViewId: function getCurrentSubViewId() { var topView = this.getView(this.topLayoutModel.id).view; return topView.getSelectedSubViewId(); }, /** If the layout has 'subviews' (ie. tabs or scenes), return the title of the current one. */ getCurrentSubViewTitle: function getCurrentSubViewTitle() { var topView = this.getView(this.topLayoutModel.id).view; return topView.getSelectedSubViewTitle(); }, _animationHelper: function _animationHelper(el, property, value, duration) { return new Promise(function (resolve) { var transitionEndCallback = function transitionEndCallback() { el.style.transition = 'none'; el.removeEventListener('transitionend', transitionEndCallback); resolve(); }; el.addEventListener('transitionend', transitionEndCallback); el.style.transition = property + ' ' + duration + 'ms'; el.style[property] = value; // fail safe Promise.delay(duration + 100).then(transitionEndCallback); }); }, _init: function _init() { this._interactionControllerCreationPromise = this._createInteractionController().then(function () { var topView = this._getTopViewElement()[0]; if (topView) { $(topView).toggleClass('authoringMode', this.isAuthoring); } }.bind(this)); $(window).on('resize.layoutController' + this.id, this.onResize.bind(this)); // set global right-click context menu suppression this.fnOnContextMenu = this.onContextMenu.bind(this); window.oncontextmenu = this.fnOnContextMenu; }, createLayoutModules: function createLayoutModules() { var _this = this; this.pageReady = Promise.all([this._interactionControllerCreationPromise, this.createLayoutModule(this.topLayoutModel.id).then(function (layout) { if (layout) { return layout.renderContent(); } })]); // Mark the page when the render is complete for the top layout and all widget underneath it. this.onPageRenderComplete = this.whenPageRenderComplete(); this.onPageRenderComplete.then(function () { var topLayoutView = _this.getTopLayoutView(); if (topLayoutView) { topLayoutView.$el.attr('data-render', 'renderComplete'); } }); }, _getInteractionControllerInteractionModules: function _getInteractionControllerInteractionModules() { var paths = void 0; if (this.isAuthoring && !this.isEventGroupMode) { paths = ['dashboard-core/js/dashboard/layout/authoring/interaction/strategy/Authoring']; } else { paths = []; } return Promise.all(paths.map(function (path) { return ClassFactory.loadModule(path); })); }, _shouldEnableInteractions: function _shouldEnableInteractions() { return !(this.dashboardApi.getAppConfig('enableInteractions') === false); }, _createInteractionController: function _createInteractionController() { var _this2 = this; if (!this._shouldEnableInteractions() || this.interactionController) { return Promise.resolve(); } return this._loadInteractionControllerConfig().then(function (selectionOptions) { var interactionController = new Controller({ // TODO - to be removed after refactoring services: _this2.services, glassContext: _this2.glassContext, appSettings: _this2.appSettings, // TODO - end to be removed dashboardAPI: _this2.dashboardApi, toolbarConfig: _this2.dashboardApi.getAppConfig('toolbar'), transaction: _this2.transaction, canvas: _this2.canvas, boardModel: _this2.boardModel, layoutController: _this2, eventRouter: _this2.eventRouter, $el: _this2.$el, gatewayUrl: _this2.gatewayUrl, cdnUrl: _this2.cdnUrl, selectionOptions: selectionOptions }); // TODO temporary code to register interaction controller needed by the widget action features, e.g. GroupAction _this2.dashboardApi.getFeature('internal').registerDashboardSvc('InteractionController.internal', interactionController); _this2.defaultInteractions = [new CommonInteraction(_this2.logger, _this2.dashboardApi)]; _this2._setInteractionController(interactionController); return _this2._updateInteractionControllerInteractions(); }); }, _setInteractionController: function _setInteractionController(controller) { this.interactionController = controller; this.interactionControllerDfd.resolve(controller); }, getInteractionController: function getInteractionController() { return this.interactionControllerDfd.promise; }, _loadInteractionControllerConfig: function _loadInteractionControllerConfig() { var _ref = this.dashboardApi.getAppConfig('selection') || {}, deselectionSelector = _ref.deselectionSelector; if (deselectionSelector) { return new Promise(function (resolve, reject) { require([deselectionSelector], resolve, reject); }).then(function (module) { return { deselectionSelector: module }; }); } return Promise.resolve(); }, /** * Set the interaction controller strategy (authoring, consumption, event group). * * @return {Promise} Promise to be resolved when the strategy has been set */ _updateInteractionControllerInteractions: function _updateInteractionControllerInteractions() { var _this3 = this; if (!this.interactionController) { return Promise.resolve(); } return this._getInteractionControllerInteractionModules().then(function (Interactions) { var interactions = Interactions.map(function (Interaction) { return new Interaction(_this3.logger); }); var allInteractions = _this3.defaultInteractions.concat(interactions); return _this3.interactionController.applyInteractions(allInteractions).then(function () { _this3.dashboardApi.getFeature('.LifeCycleManager').invokeLifeCycleHandlers('dashboard.layout.interactions.ready'); }); }); }, /** * Moves to consume mode * * Removes authoring functionality that was added to the all consume views. * Views associated with static widgets remain (they are not deleted) */ changeToConsumeMode: function changeToConsumeMode() { var _this4 = this; this.isAuthoring = false; this.isEventGroupMode = false; return this._updateInteractionControllerInteractions().then(function () { _this4.removeAuthorModeFromLayout(_this4.topLayoutModel.id); _this4._getTopViewElement().removeClass('authoringMode').removeClass('eventGroupMode'); }); }, /** * Removes author behavior to specified view * * @param id - layout identifier */ removeAuthorModeFromLayout: function removeAuthorModeFromLayout(id) { var layoutModel = this.topLayoutModel.findModel(id); var view = $('#' + layoutModel.id, this.$el)[0]; if (view && view._layout && view._layout.authorHelper) { view._layout.authorHelper.destroy(); view._layout.authorHelper = undefined; if (view._layout.authorViewManager) { view._layout.authorViewManager.destroy(); view._layout.authorViewManager = undefined; } } if (layoutModel.items) { for (var i = 0, iLen = layoutModel.items.length; i < iLen; i++) { this.removeAuthorModeFromLayout(layoutModel.items[i].id); } } }, /** * Attaches author behavior * * @param options - {canvasNotifier: notifier obj} * @return promise to indicate when move to authoring is complete */ changeToAuthorMode: function changeToAuthorMode(options) { var _this5 = this; this.isAuthoring = true; this.isEventGroupMode = false; this.canvasNotifier = options.canvasNotifier; //start with top layout return this.addAuthorModetoLayout(this.topLayoutModel.id, null).then(function () { _this5._getTopViewElement().addClass('authoringMode').removeClass('eventGroupMode'); // initialize interaction controller strategy return _this5._updateInteractionControllerInteractions(); }); }, changeToEventGroupMode: function changeToEventGroupMode() { var _this6 = this; this.isAuthoring = true; this.isEventGroupMode = true; return this._updateInteractionControllerInteractions().then(function () { _this6.removeAuthorModeFromLayout(_this6.topLayoutModel.id); _this6._getTopViewElement().removeClass('authoringMode').addClass('eventGroupMode'); }); }, _getTopViewElement: function _getTopViewElement() { return this.$el.find('#' + this.topLayoutModel.id); }, /** * Attaches author behavior to specified view * * @param id - layout identifier * @param parentLayout - parent view for layout being moved to author * @return promise to indicate when move to authoring is complete */ addAuthorModetoLayout: function addAuthorModetoLayout(id, parentLayout) { var _this7 = this; var layoutModel = this.topLayoutModel.findModel(id); var node = $('#' + this.modelIdToNodeId(id), this.$el)[0]; var pageLayoutView = node && node._layout; if (!pageLayoutView) { // the layout view does not exist. This is the case for static widget and layouts like group layout. // Create layout module will take care of create the layout view and the author helper. return this.createLayoutModule(id, parentLayout).then(function (layout) { return layout.renderContent(); }); } if (pageLayoutView.authorHelper) { // Authoring has already been enabled for this view. return Promise.resolve(); } var options = { //TODO - remove after refactoring services: this.services, config: this.config, appSettings: this.appSettings, // TODO - end remove canvas: this.canvas, dashboardApi: this.dashboardApi, model: layoutModel, parentLayout: parentLayout, layoutController: this, canvasNotifier: this.canvasNotifier, consumeView: pageLayoutView, widgetChromeEventRouter: pageLayoutView.widgetChromeEventRouter, eventRouter: this.eventRouter }; var modules = this._getLayoutModule(layoutModel, options); return ClassFactory.loadModule(modules.sAuthorHelper).then(function (PageLayoutAuthor) { // The authoring view will set the authorHelper reference of the consumeView to itself new PageLayoutAuthor(options); pageLayoutView.renderContent(); //process children layouts var layoutModels = layoutModel.items; if (layoutModels && !pageLayoutView.preventAuthoringForChildren) { var promises = []; for (var i = 0, iLen = layoutModels.length; i < iLen; i++) { promises.push(_this7.addAuthorModetoLayout(layoutModels[i].id, pageLayoutView)); } return Promise.all(promises); } }); }, getView: function getView(modelId) { return this.viewsMap[modelId]; }, markViewAsReady: function markViewAsReady(view) { if (this.viewsMap[view.id]) { this.viewsMap[view.id].viewReadyDeferred.resolve(view); } }, /** * Create an instance of the layout module * * @param {string} pageId - layout page id * @param {View} parentLayout - parent view for layout being moved to author * @param {any} [additionalWidgetData] - data to be passed to the widget when the layout is created. * * @return {Promise} Promise to indicate when the creation is complete */ createLayoutModule: function createLayoutModule(pageId, parentLayout, additionalWidgetData) { var existingView = this.viewsMap[pageId] && this.viewsMap[pageId].view; if (existingView) { /* No need to create the view. We have an existing view associated with the layout id that is being created. This might happen after the following scenario: - Create layout model A - Create layout model B - Move layout model A to be child of B. The view creation is asynchronous. This means that the process of the creation of A & B will start but the move operation will happen before the view are created so the changes will not be reflected in the views. so we end up with creating the view A and the view B with another instance of View A. The scenario that will cause this is the following: - group 2 objects. - delete 1 of the grouped objects. - This will delete the object and remove the group - undo the change. */ var _promise = this.viewsMap[pageId].viewReady; _promise.then(function () { var nodeId = this.modelIdToNodeId(pageId); this.viewsMap[pageId].view.parentLayout = parentLayout; $(parentLayout.domNode).find('#' + nodeId).replaceWith(this.viewsMap[pageId].view.domNode); }.bind(this)); return _promise; } var deferred = new Deferred(); var page = this.topLayoutModel.findModel(pageId); var options = { // TODO - remove after refactoring services: this.services, config: this.config, appSettings: this.appSettings, // TODO - end remove canvas: this.canvas, content: this.canvas.getContent(pageId), dashboardApi: this.dashboardApi, eventRouter: this.eventRouter, model: page, layoutController: this, canvasNotifier: this.canvasNotifier, parentLayout: parentLayout, additionalWidgetData: additionalWidgetData, contentFeatureLoader: this.contentFeatureLoader }; var layoutModules = this._getLayoutModule(page, options); var sPageLayout = layoutModules.sPageLayout; var sAuthorHelper = layoutModules.sAuthorHelper; this.viewsMap[pageId] = { viewReadyDeferred: deferred, viewReady: deferred.promise }; var promise = void 0; if (sPageLayout) { var modules = ['dashboard-core/js/app/EventRouter']; if (sAuthorHelper) { modules.push(sAuthorHelper); } var pageLayout = void 0; if ((typeof sPageLayout === 'undefined' ? 'undefined' : _typeof(sPageLayout)) === 'object') { pageLayout = sPageLayout; } else { pageLayout = { path: sPageLayout, type: this.boardModuleFactory.getDefaultModuleType() }; } promise = Promise.all([this.loadLayout(pageLayout), Promise.all(modules.map(function (module) { return ClassFactory.loadModule(module); }))]).then(function (Modules) { // update the parent layout in the options // there is a case where by the time we load the module, the parent has changed // Today, this is the case of undo a delete of an object that belongs to a group. // the operation will create the object with the page as a parent, then move it to the group object var parentModel = options.model.getParent(); if (parentModel) { options.parentLayout = this.getLayoutView(parentModel.id); } var PageLayout = Modules[0]; var EventRouter = Modules[1][0]; var AuthorHelper = Modules[1][1]; if (page.type === 'widget' || this.contentTypeRegistry.isTypeRegistered(page.type)) { options.widgetChromeEventRouter = new EventRouter(); } var pageLayout = new PageLayout(options); if (AuthorHelper) { options.consumeView = pageLayout; // The authoring view will set the authorHelper reference of the consumeView to itself new AuthorHelper(options); } this.viewsMap[pageId].view = pageLayout; return pageLayout; }.bind(this)); } else { promise = Promise.resolve(); } return promise; }, loadLayout: function loadLayout(module) { return this.boardModuleFactory.make(module.type || this.boardModuleFactory.getDefaultModuleType()).then(function (boardModule) { return boardModule.load(module).then(function (moduleInstance) { return moduleInstance.module; }); }); }, removeView: function removeView(view) { if (this.viewsMap) { delete this.viewsMap[view.model.id]; } }, _getLayoutModule: function _getLayoutModule(layoutSpec, options) { var sPageLayout = void 0; var sPageLayoutAuthor = void 0; var modules = null; if (layoutSpec.type === 'widget' || layoutSpec.type === 'appwidget') { modules = this._getWidgetLayoutModule(layoutSpec, options); } else { sPageLayout = this.consumeViews[layoutSpec.type]; if (!sPageLayout) { // layout views like group do not have consumption views. // Use the base view for them in authoring mode sPageLayout = 'dashboard-core/js/dashboard/layout/views/LayoutBaseView'; } // else { // TODO: what do we do if we don't have a page layout? // } sPageLayoutAuthor = this.isAuthoring ? this.authoringViews[layoutSpec.type] || 'dashboard-core/js/dashboard/layout/authoring/views/LayoutBaseView' : undefined; modules = { sPageLayout: sPageLayout, sAuthorHelper: sPageLayoutAuthor }; } return modules; }, _getWidgetLayoutModule: function _getWidgetLayoutModule(layoutSpec, options) { var widgetModel = this.boardModel.getWidgetModel(layoutSpec.id); options.widgetModel = widgetModel; var type = widgetModel && widgetModel.type; options.widgetRegistry = this.widgetLoader.widgetRegistry[type]; var sPageLayout = options.widgetRegistry && options.widgetRegistry.layoutConsumeView; var sPageLayoutAuthor = null; if (this.isAuthoring) { if (!sPageLayout) { //static widgets don't have a view associated in consume mode //use base widget view for them sPageLayout = 'dashboard-core/js/dashboard/layout/views/Widget'; } if (options.widgetRegistry) { sPageLayoutAuthor = options.widgetRegistry.layoutAuthoringView; } } return { sPageLayout: sPageLayout, sAuthorHelper: sPageLayoutAuthor }; }, onThemeChange: function onThemeChange(payload) { if (payload.value !== payload.prevValue) { this.onResize({ reRender: true }); } this.eventRouter.trigger('properties:refreshPane', { focusSelector: '.dropDowntheme' }); }, _onLayoutPositioning: function _onLayoutPositioning(payload) { if (payload.sender === 'UndoRedoController') { this.eventRouter.trigger('properties:refreshPane'); } else { var layoutModels = this.topLayoutModel.findDescendantsWithType('genericPage'); for (var _iterator = layoutModels, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { var _ref2; if (_isArray) { if (_i >= _iterator.length) break; _ref2 = _iterator[_i++]; } else { _i = _iterator.next(); if (_i.done) break; _ref2 = _i.value; } var layoutModel = _ref2; layoutModel.set({ layoutPositioning: payload.value }, { payloadData: payload.data, sender: this.id }); } } }, onColorSetChanged: function onColorSetChanged() { this.colorsService.makeSureColorIsValidInModel({ model: this.topLayoutModel, propertyName: 'css', offset: 2, valuePrefix: 'fill-' }); this.colorsService.makeSureColorIsValidInModel({ model: this.topLayoutModel, propertyName: 'fillColor', offset: 2 }); // Don't use the same offset as the fillColor or our tab text color will end up being the same color as the background this.colorsService.makeSureColorIsValidInModel({ model: this.topLayoutModel, propertyName: 'tabTextColor', offset: 1 }); this.colorsService.makeSureColorIsValidInModel({ model: this.topLayoutModel, propertyName: 'tabSelectedLineColor', offset: 1 }); this.colorsService.makeSureColorIsValidInModel({ model: this.topLayoutModel, propertyName: 'tabBackgroundColor', offset: 1 }); }, onResize: function onResize(renderOptions) { if (this.$el.is(':visible')) { // We only resize if the view is visible. var topLayoutView = this.getTopLayoutView(); if (topLayoutView && topLayoutView.onResize) { topLayoutView.onResize(renderOptions); } } }, getTopLayoutView: function getTopLayoutView() { return this.getLayoutView(this.topLayoutModel.id); }, getTopLayoutViewWhenReady: function getTopLayoutViewWhenReady() { return this.getLayoutViewWhenReady(this.topLayoutModel.id); }, getSelectedNodes: function getSelectedNodes() { return this.interactionController.getSelectedNodes(); }, getSelectedWidgets: function getSelectedWidgets() { var selectedLayout = this.boardModel.getSelectedLayout(); var layout = this.topLayoutModel.findModel(selectedLayout); return layout ? layout.getSelectedWidgets() : []; }, getLayoutContentContainer: function getLayoutContentContainer() { return LayoutHelper.getLayoutContentContainer(this.topLayoutModel.id, null, this.dashboardApi); }, getLayoutContentContainerForDropTarget: function getLayoutContentContainerForDropTarget(dropTargetNode) { return LayoutHelper.getLayoutContentContainerForDropTarget(dropTargetNode, this.topLayoutModel, this.dashboardApi); }, /** * Find the last visible page to insert. For example, if you have this hierarchy of pages: Block/Tab/Absolute. * It will return the Absolute of the selected tab. * Will return the first availabe drop zone if a template. */ getLastVisiblePage: function getLastVisiblePage() { return LayoutHelper.getLastVisiblePage(this.topLayoutModel.id, null, this.dashboardApi); }, setPreferredAddOptions: function setPreferredAddOptions(addOptions) { return LayoutHelper.setPreferredAddOptions(addOptions, this.topLayoutModel, this.dashboardApi); }, setPreferredLocation: function setPreferredLocation(addOptions) { return LayoutHelper.setPreferredLocation(addOptions); }, destroy: function destroy(event) { // Ensure we are not removing the context menu if has been set by some other class. if (this.fnOnContextMenu && window.oncontextmenu === this.fnOnContextMenu) { this.fnOnContextMenu = null; window.oncontextmenu = null; } $(window).off('resize.layoutController' + this.id); if (this.colorsService) { this.colorsService.off('theme:changed', this.onThemeChange, this); } if (this.topLayoutModel) { this.topLayoutModel.off('change:layoutPositioning', this._onLayoutPositioning, this); // destroy the top layout view. this.destroyLayoutView(this.topLayoutModel.id, event); } if (this.interactionController) { this.interactionController.destroy(); } if (this.deprecatedCanvas) { this.deprecatedCanvas.destroy(); } for (var prop in this) { // clear everything referenced by this object in case someone is still holding on to it. if (Object.prototype.hasOwnProperty.call(this, prop)) { delete this[prop]; } } this._isDestroyed = true; }, destroyLayoutView: function destroyLayoutView(layoutId, event) { var viewEntry = this.viewsMap[layoutId]; viewEntry && viewEntry.view && viewEntry.view.destroy(event); }, /** * Return the node id that maps to the given model id * @param modelId * @returns */ modelIdToNodeId: function modelIdToNodeId(modelId) { return LayoutHelper.modelIdToNodeId(modelId); }, /** * Return the model id for a given node id * @param nodeId * @returns */ nodeIdToModelId: function nodeIdToModelId(nodeId) { return LayoutHelper.nodeIdToModelId(nodeId); }, /** * Return a promise that will be resolved when the layout view is created and ready * At this point, the widget module (if available) might not be loaded and ready yet. * * @param modelId - id or array of ids * * @returns {Promise} promise to be fulfilled when the layout is ready */ layoutReady: function layoutReady(modelId) { if (this._isDestroyed) { return Promise.reject('Layout Controller destroyed'); } var idArray = modelId; if (!_.isArray(idArray)) { idArray = [modelId]; } var views = _.pick.apply(_, [this.viewsMap].concat(idArray)); return Promise.all(_.pluck(views, 'viewReady')).then(function () { var layout = void 0; if (_.isArray(modelId)) { layout = _.pluck(views, 'view'); } else { layout = views[modelId] && views[modelId].view; } return layout; }); }, /** * Selects all layouts (groups, widgets, etc...) in the array of ids * @param layoutIds Array of ids * @param event The original event */ selectLayouts: function selectLayouts(layoutIds, event) { var promises = []; var selectionHandler = this.interactionController.selectionHandler; _.each(layoutIds, function (layoutId) { var promise = this.whenWidgetRenderComplete(layoutId).then(function (layout) { selectionHandler.selectNode(layout.domNode, event); }); promises.push(promise); }.bind(this)); return Promise.all(promises); }, /** * Return a promise that will be resolved when the widget module is created and ready * * @param modelId - id * */ widgetReady: function widgetReady(modelId) { return this.layoutReady(modelId).then(function (layout) { if (this.viewsMap[modelId] && this.viewsMap[modelId].view && this.viewsMap[modelId].view.widgetReady) { return this.viewsMap[modelId].view.widgetReady().then(function () /* widgetAPI */{ return layout; }); } else { // Some widget in consume mode do not have modules. return layout; } }.bind(this)); }, /** * Return a promise that will be resolved when the widget is rendered * * @param modelId - id * */ whenWidgetRenderComplete: function whenWidgetRenderComplete(modelId) { return this.layoutReady(modelId).then(function (layout) { if (this.viewsMap[modelId] && this.viewsMap[modelId].view && this.viewsMap[modelId].view.whenRenderComplete) { return this.viewsMap[modelId].view.whenRenderComplete().then(function () { return layout; }); } else { // Some widget in consume mode do not have modules. return layout; } }.bind(this)); }, /** * Wait until page render is complete * * @param {Layout} pageLayout - page layout * @return {Promise} promise resolved when the page render is complete */ whenPageRenderComplete: function whenPageRenderComplete(pageLayout) { return this.layoutReady(this.topLayoutModel.id).then(function () { var widgets = (pageLayout || this.topLayoutModel).findDescendantsWithType('widget'); var promises = []; _.each(widgets, function (widget) { promises.push(this.whenWidgetRenderComplete(widget.id)); }, this); return Promise.all(promises); }.bind(this)); }, /** * Return a promise that will be resolved when the widget is rendered * * @param modelId - identifier of widget the message is sent on behalf of * @param multiselect - flag indicating whether the selection is for multiple widgets * @param showContextBar - flag indicating whether to show the default context bar * */ selectWidget: function selectWidget(event) { var modelId = event.modelId; var multiselect = event.multiselect; var showContextBar = event.showContextBar; return this.widgetReady(modelId).then(function (layout) { //widget param in widgetReady function is also passed in (can be used at a later time) if (!multiselect) { this.deselectAllWidgets(); } var selectionHandler = this.interactionController.selectionHandler; //this prevents the context bar from appearing var eventChainLocal = new EventChainLocal(event); if (!showContextBar) { eventChainLocal.setProperty('preventDefaultContextBar', true); } if (selectionHandler) { selectionHandler.selectNode(layout.domNode, event); } }.bind(this)); }, deselectAllWidgets: function deselectAllWidgets() { if (this.interactionController) { this.interactionController.selectionHandler.deselectAll(); } }, deselectWidget: function deselectWidget(event) { var modelId = event.modelId; var layoutView = this.viewsMap[modelId] && this.viewsMap[modelId].view; var selectionHandler = this.interactionController.selectionHandler; if (!selectionHandler) { return Promise.resolve(); } if (layoutView && layoutView.domNode) { return Promise.resolve(selectionHandler.deselectNode(layoutView.domNode)); } else { return this.widgetReady(modelId).then(function (layout) { selectionHandler.deselectNode(layout.domNode); }.bind(this)); } }, moveLayout: function moveLayout(updateArray) { var _this8 = this; var transactionToken = this.transaction.startTransaction(); updateArray.forEach(function (info) { _this8.canvas.moveContent(info.parentId, [info.id], transactionToken); }); this.transaction.endTransaction(transactionToken); }, // NOTE: // getLayoutView and getLayoutViewWhenReady should be combined into one. // `getLayoutViewWhenReady` - was created as part of the conversion to bluebird and the operation became async instead of sync. // // We can't guarantee that the view is ready so to be safe, one should always call getLayoutViewWhenReady. // Should be fixed as part of a bigger revamp // getLayoutViewWhenReady: function getLayoutViewWhenReady(modelId) { var viewEntry = this.viewsMap[modelId]; if (!viewEntry) { return Promise.resolve(null); } return viewEntry.viewReady; }, getLayoutView: function getLayoutView(modelId) { var viewEntry = this.viewsMap[modelId]; return viewEntry && viewEntry.view; }, onContextMenu: function onContextMenu(event) { var nodeName = event.target.nodeName; var $target = $(event.target); return nodeName === 'INPUT' || nodeName === 'TEXTAREA' || $target.attr('contenteditable') === 'true' || $target.closest('[contenteditable="true"]').length ? true : false; }, getInteractionProperties: function getInteractionProperties() { return this.interactionController.getInteractionProperties(); }, hasMaximizedWidget: function hasMaximizedWidget() { var viewIds = Object.keys(this.viewsMap); for (var i = 0; i < viewIds.length; i++) { var view = this.getLayoutView(viewIds[i]); if (view && view.widgetAPI && view.widgetAPI.isWidgetMaximized && view.widgetAPI.isWidgetMaximized()) { return true; } } return false; }, /** * @param layoutWidgetId The layout id for the widget being processed. * @param {boolean} noPageContextMerge if true the pageContext will not be merged into the Widget Model * @returns the widgetModel which is attached to the pin for this layoutWidgetId. * The widgetModel for pinning can be overridden (or returned) by a widget * if it implements the 'getWidgetModelForPinning' API. Otherwise, the model is returned from the board spec. */ getLayoutWidgetModel: function getLayoutWidgetModel(layoutWidgetId, noPageContextMerge) { var widgetObj = this.widgetLoader.getWidget(layoutWidgetId); return widgetObj && widgetObj.getWidgetModelForPinning && !noPageContextMerge ? widgetObj.getWidgetModelForPinning() : this.boardModel.getWidgetModel(layoutWidgetId); } }); return LayoutController; }); //# sourceMappingURL=LayoutController.js.map