'use strict'; /* * Licensed Materials - Property of IBM * IBM Cognos Products: BI Cloud (C) Copyright IBM Corp. 2014, 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', './layout/LayoutController', './UndoRedoController', './CopyPasteController', './contentpane/PropertiesManager', '../app/EventRouter', '../api/impl/LegacyViewControllers', 'jquery', 'underscore'], function (Class, LayoutController, UndoRedoController, CopyPasteController, PropertiesManager, EventRouter, LegacyViewControllers, $, _) { return Class.extend({ _MAX_DATASET_REFRESH_RATE: 5000, // ms _datasetRefreshInfo: {}, init: function init(options) { this.$el = options.$el; // TODO - to be removed after refactoring this.glassContext = options.glassContext; this.services = options.services; // TODO - end to be removed this.dashboardApi = options.dashboardApi; this.model = options.boardModel; this.widgetLoader = options.widgetLoader; // TODO remove this.loader -- it is here because it is referenced by other components (e.g. explore) this.loader = options.widgetLoader; this.boardLoader = options.boardLoader; this._extensions = {}; this.logger = this.dashboardApi.getGlassCoreSvc('.Logger'); this.addRemoveNotifier = new EventRouter(); this.addRemoveNotifier.on('widget:removeDone', this.onWidgetRemoveDone, this); this.undoRedoController = new UndoRedoController(this.model, { logger: this.logger, canvas: this.boardLoader.getCanvas(), transaction: this.dashboardApi.getFeature('Transaction') }); this.layoutController = new LayoutController({ // TODO - to be removed after refactoring glassContext: this.glassContext, services: this.services, appSettings: options.appSettings, // TODO - end to be removed contentFeatureLoader: options.contentFeatureLoader, dashboardApi: this.dashboardApi, eventRouter: options.eventRouter, $el: this.$el, boardModel: this.model, boardModuleFactory: options.boardModuleFactory, widgetLoader: this.widgetLoader, canvasNotifier: this.addRemoveNotifier, layoutExtensions: options.layoutExtensions, gatewayUrl: options.gatewayUrl, cdnUrl: options.cdnUrl, canvasController: this, canvas: this.boardLoader.getCanvas() }); this.layoutController.createLayoutModules(); var legacyViewControllers = new LegacyViewControllers({ layoutController: this.layoutController }); this.dashboardApi.getFeature('FeatureRegistry.internal').registerFeature('LegacyViewControllers', legacyViewControllers); // TODO: should be gone. We should use the canvas API instead this.dashboardApi.registerDashboardSvc('boardService', this.getDeprecatedCanvas()); this.eventRouter = options.eventRouter; // register properties manager this.propertiesManager = new PropertiesManager({ 'glassContext': this.glassContext, 'canvasController': this }); this.registerExtension('propertiesManager', this.propertiesManager); this.dashboardApi.registerDashboardSvc('propertiesManager', this.propertiesManager); this.copyPasteController = new CopyPasteController({ layoutController: this.layoutController, dashboardApi: this.dashboardApi, logger: this.logger, model: this.model, api: this.getDeprecatedCanvas(), type: this.dashboardApi.getType().toUpperCase() }); this.model.on('change:datasetShaping', function (eventInfo) { // Do not refresh the canvas if the event was calculation add or remove // if (eventInfo && ( 'add' === eventInfo.name || 'remove' === eventInfo.name ) // && eventInfo.origCollectionEvent && 'change:calculations' === eventInfo.origCollectionEvent.eventName) { // return; // } this.model.datasetShaping.each(function (dataSetShapingModel) { dataSetShapingModel.eventInfo = eventInfo; this._onChangeShapingSpec(dataSetShapingModel); }.bind(this)); }.bind(this)); $(document).on('keydown', this._onDocumentKeyDown); // TODO: for now, an object like this is more than sufficient. // if there is a growing need for a feature registry, we'll adjust. this._features = {}; }, initialize: function initialize() { if (this.layoutController != null) { return this.layoutController.initialize(); } return Promise.reject(); }, /** * Execute a callback. The method will only execute the callback if the object is not destroyed * Typpically used in a promise callback because we might have destroyed the object before the promise was resolved. * @param {Function} callback */ _prepareAsyncCallback: function _prepareAsyncCallback(callback) { return function () { if (!this._destroyed) { return callback.apply(undefined, arguments); } return Promise.reject('The live widget object was destroyed'); }.bind(this); }, getDeprecatedCanvas: function getDeprecatedCanvas() { if (this.layoutController != null) { return this.layoutController.getDeprecatedCanvas(); } return null; }, destroy: function destroy() { this._destroyed = true; // destroy the layout controller before the loader // If we have widgets, this will trigger a loader widget unload call. // layoutController.destroy() is also called by the new LegacyViewController feature when // the features are all destroyed. this.layoutController.destroy(); this._features = null; this.widgetLoader = null; // TODO remove this.loader -- it is here because it is referenced by other components (e.g. explore) this.loader = null; this.boardLoader = undefined; this.layoutController = null; if (this.addRemoveNotifier) { this.addRemoveNotifier.off(); this.addRemoveNotifier = null; } if (this.eventRouter) { this.eventRouter.off(); this.eventRouter = null; } if (this.undoRedoController) { this.undoRedoController.clearStack(); this.undoRedoController = null; } _.each(this._datasetRefreshInfo, function (value, datasetId) { this._stopDatasetRefresh(datasetId); }); $(document).off('keydown', this._onDocumentKeyDown); }, _getSelectedWidget: function _getSelectedWidget() { var currentSelectedWidget = this.$el.find('.widget.nodeSelected:visible'); if (currentSelectedWidget.length) { return this.widgetLoader.getWidget(currentSelectedWidget[0]._layout.id); } return null; }, // Copied this whole method from LayoutBaseView.js (authoring) // Previously we used the dashboard.getDeprecatedCanvas().addWidget(...) // method but a gating/regression defect for 11.1.7 came up for keyboard // while mouse use worked fine. They go on different paths and this change // is a hack as it just duplicates code to make things work similarly. // TODO: Consolidate the different ways we add content to the canvas - don't duplicate code _addWidget: function _addWidget(widgetSpec, isTouch) { var _this = this; var transaction = this.dashboardApi.getFeature('Transaction'); var token = transaction.startTransaction(); var spec = widgetSpec.model, parentId = widgetSpec.parentId, layoutProperties = widgetSpec.layoutProperties; var content = { containerId: parentId, properties: layoutProperties.style, spec: spec, type: 'widget.' + spec.type }; return this.dashboardApi.getCanvas().addContent(content, token).then(function (content) { transaction.endTransaction(token); return _this.layoutController.whenWidgetRenderComplete(content.getId()); }).then(function (layout) { var selectionHandler = _this.layoutController.interactionController.selectionHandler; selectionHandler.deselectAll(); selectionHandler.selectNode(layout.domNode, { isTouch: isTouch }); }); }, addDataItemsOrAddWidget: function addDataItemsOrAddWidget(columnInfo, isTouch) { var _this2 = this; // Use DataWidget's accepts and onDrop to build up the visualization // since they already have the proper logic to compare datasets. var widget = this._getSelectedWidget(); if (widget && widget.accepts && widget.accepts(columnInfo)) { return Promise.resolve(widget.onDrop(columnInfo)); } return this.getWidgetSpecFromDragObject(columnInfo).then(function (widgetSpec) { return _this2._addWidget(widgetSpec, isTouch); }); }, getWidgetSpecFromDragObject: function getWidgetSpecFromDragObject(dragObject) { var layoutProperties = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var widgetSpec = { parentId: this.id, layoutProperties: layoutProperties }; var canvasDnD = this.dashboardApi.getFeature('CanvasDnD'); return canvasDnD.onDrop(dragObject).then(function (newModel) { if (newModel) { widgetSpec.model = newModel; } return widgetSpec; }); }, /** * adds a pin and selects the added pin after it is done. * * @param {object} pinInfo - the description of the pin to add * @param {Boolean} isTouch - true if it is touch */ addPin: function addPin(pinInfo, isTouch) { var id = this.getDeprecatedCanvas().addFragment(pinInfo); this.getDeprecatedCanvas().selectWidget(id, { isTouch: isTouch }); }, onWidgetRemoveDone: function onWidgetRemoveDone(event) { if (this.widgetLoader.isLoaded(event.id)) { this.widgetLoader.unLoadWidget(event.id, undefined, event); } }, /** * Enables authoring functionality * * If page is loading, it will resume layout controller move to authoring after its done loading * * @return promise to indicate when move to authoring is complete */ changeToAuthorMode: function changeToAuthorMode() { return this.layoutController.pageReady.then(this._prepareAsyncCallback(this.layoutController.changeToAuthorMode.bind(this.layoutController, { canvasNotifier: this.addRemoveNotifier }))); }, /** * Changes to consume mode */ changeToConsumeMode: function changeToConsumeMode() { return this.layoutController.changeToConsumeMode(); }, changeToEventGroupMode: function changeToEventGroupMode() { return this.layoutController.pageReady.then(this._prepareAsyncCallback(this.layoutController.changeToEventGroupMode.bind(this.layoutController))); }, /** Used to register providers in extensions. If the extension isn't available yet the provider information will be cached and passed to the extension once it's created. @param {string} extensionId - The ID of the extension to register the provider @param {handler} object - The object which will be registered as a provider in the extension **/ addProviderForExtension: function addProviderForExtension(extensionId, handler) { var extension = this.getExtension(extensionId); if (extension) { if (extension.addProvider) { extension.addProvider(handler); } else { this.logger.debug('Extension with id ' + extensionId + ' does not have a "addProvider" method'); } } else { if (!this._extensionProviders) { this._extensionProviders = {}; } if (!this._extensionProviders[extensionId]) { this._extensionProviders[extensionId] = []; } this._extensionProviders[extensionId].push(handler); } }, /** Registers an extension object in the canvas controller. Your extension should implement the 'addProvider' method if you want to let providers register to your extension @param {string} id - The id of the extension @param {object} extension - The extension object **/ registerExtension: function registerExtension(id, extension) { this._extensions[id] = extension; if (this._extensionProviders && this._extensionProviders[id]) { if (extension.addProvider) { this._extensionProviders[id].forEach(function (provider) { extension.addProvider(provider); }); } else { this.logger.debug('Extension with id ' + id + ' does not have a "addProvider" method'); } } }, getExtension: function getExtension(id) { return this._extensions[id] || null; }, /* * Overwrites the document event handler that catches backspace key press and triggers a back * navigation and instead trigger a delete action (unless target is an input block) */ _onDocumentKeyDown: function _onDocumentKeyDown(e) { if (e && e.keyCode === 8 && !e.metaKey && !e.shiftKey) { var target = e.srcElement || e.target; if (!target.tagName.toLowerCase().match(/input|textarea|select/igm) && target.getAttribute('contentEditable') !== 'true') { e.preventDefault(); } } }, onPageRenderComplete: function onPageRenderComplete() { return this.layoutController.onPageRenderComplete; }, onPageReady: function onPageReady() { return this.layoutController.pageReady; }, /** * Get canvas feature * * @param {string} id - canvas feature id * * @return {Promise} Promise resolved with the feature or with undefined if feature does not exist */ getFeature: function getFeature(id) { var _this3 = this; return new Promise(function (resolve, reject) { if (_this3._features[id]) { resolve(_this3._features[id]); } else { reject('Feature not found'); } }); }, /** * Register canvas feature * * @param {string} id - canvas feature id * @param {Feature} feature - canvas feature * * @return {Promise} Promise resolved when the registration is complete */ registerFeature: function registerFeature(id, feature) { this._features[id] = feature; return Promise.resolve(); } }); }); //# sourceMappingURL=CanvasController.js.map