'use strict'; /* *+------------------------------------------------------------------------+ *| Licensed Materials - Property of IBM *| IBM Cognos Products: Dashboard *| (C) Copyright IBM Corp. 2015, 2021 *| *| US Government Users Restricted Rights - Use, duplication or disclosure *| restricted by GSA ADP Schedule Contract with IBM Corp. *+------------------------------------------------------------------------+ */ define(['./BaseView', 'jquery', 'underscore', '../services/ServicesHelper', '../../app/EventRouter', '../loader/BoardLoader', '../layout/authoring/interaction/ChangeModeAction', '../util/ButtonHideHelper', '../../app/util/ErrorUtils', '../../app/ui/dialogs/EditableDialog', '../../lib/@waca/core-client/js/core-client/utils/PerfUtils', '../../lib/@waca/core-client/js/core-client/ui/KeyCodes', '../services/CanvasExtensions', '../../api/impl/Dashboard', '../../features/dashboard/undoRedo/api/impl/UndoRedo', '../../lib/@waca/dashboard-common/dist/core/APIFactory', '../../lib/@waca/dashboard-common/dist/utils/EventChainLocal', '../../app/nls/StringResources', '../../lib/@waca/dashboard-common/dist/utils/DialogBlocker', '../../extension/Extensions', '../widgets/WidgetRegistry', '../../dashboard/model/BoardModelFactory', '../../lib/@waca/dashboard-common/dist/utils/MemUtil', '../../api/core/glass/GlassDashboardFactory'], function (BaseView, $, _, ServicesHelper, EventRouter, BoardLoader, ChangeModeAction, ButtonHideHelper, ErrorUtils, EditableDialog, PerfUtils, KeyCodes, CanvasExtensions, DashboardController, UndoRedo, APIFactory, EventChainLocal, StringResources, DialogBlocker, Extensions, WidgetRegistry, BoardModelFactory, MemUtil, GlassDashboardFactory) { var BaseBoardView = BaseView.extend({ buttonIds: { UNDO: 'com.ibm.bi.dashboard.undo', REDO: 'com.ibm.bi.dashboard.redo' }, init: function init(options) { BaseBoardView.inherited('init', this, arguments); this.dashboardApi = null; this.initialize(options); if (typeof window.__getDashboardAPI === 'undefined') { window.__getDashboardAPI = function () { return window.__glassAppController.Glass.getCurrentContentView().getDashboardApi(); }; } this.firstRender = true; }, initialize: function initialize(options) { this._resolveOptions(options); _.extend(this, options); this.options = options || {}; var dirtyOption = String(options._isDirty).toLowerCase() === 'true'; this.options['isDirtyOption'] = dirtyOption; this._pressed = {}; this._boardLoaderClass = options.boardLoaderClass || BoardLoader; this.eventRouter = new EventRouter(); this._buttonHideHelper = options._buttonHideHelper || ButtonHideHelper; this._splitterItems = []; this._modifiedDataset = []; this.upgradesCollectionPath = 'dashboard/upgrades'; this.logger = options.glassContext.getCoreSvc('.Logger'); this.$content = null; this.widgetRegistry = options.widgetRegistry || new WidgetRegistry(); this._createController(options); this._prepareForRendering(options); }, /** * Creates services and extensions required for rendering the dashboard * @param {object} options consist of attributes required to create services & extensions for rendering dashboard */ _prepareForRendering: function _prepareForRendering(options) { this._createServices(); // merge the containerApp (API container) configuration with the content options config if (options.containerAppOptions) { _.extend(this.options.options.config, options.containerAppOptions.configuration); } // Allow preprocessKeyDown to be both overwritten by an extending class directly or via a call to super if (options.preprocessKeyDown) this.preprocessKeyDown = options.preprocessKeyDown; this.specHelper = null; this.extensions = options.extensions || new Extensions(this.dashboardApi); this.boardModelFactory = options.boardModelFactory; this.dashboardFactory = new GlassDashboardFactory({ glassContext: this.glassContext }); }, _createController: function _createController(options) { var DashboardControllerClass = options.overrideDashboardController ? options.overrideDashboardController : DashboardController; this.dashboardController = options.dashboardController || new DashboardControllerClass({ glassContext: this.glassContext, view: this, services: this.services, featureLoader: this.featureLoader, eventRouter: this.eventRouter, appSettings: this.getContent({ dirty: this.options.isDirtyOption }), widgetRegistry: this.widgetRegistry }); if (this.dashboardApi) { this.dashboardApi = APIFactory.refreshAPI(this.dashboardApi, [this.dashboardController]); this.internalDashboardApi = APIFactory.refreshAPI(this.internalDashboardApi, [this.dashboardController, this.dashboardController.internalImpl]); } else { this.dashboardApi = this.dashboardController.getAPI(); this.internalDashboardApi = this.dashboardController.getInternalAPI(); } }, getApplicationLabel: function getApplicationLabel() { return StringResources.get('dashboard'); }, getContentNode: function getContentNode() { if (this.$content === null) { var $contentNode = this.$('.pageViewContent', this.el); if ($contentNode.length === 0) { $contentNode = $('
').prependTo(this.el); } this.$content = $contentNode; } return this.$content; }, /* * Used to a reset dashboard. Save a copy of the board spec as a string. */ setSavedInstance: function setSavedInstance(specString) { this.savedInstance = specString; }, getSavedInstance: function getSavedInstance() { return this.savedInstance; }, /** * Add an object that needs to be reloaded when the dashboard view is reloaded. * This is used to reload the data sources panel, properties when the dashboard is reloaded (e.g. relink) * @param {string} id A name for the reloadable object * @param {object} reloadable An object that has the "reload" method. The "reload" method is required on the object */ addReloadableObject: function addReloadableObject(id, reloadable) { if (!this.reloadables) { this.reloadables = {}; } // Only add the reloadable if it has the "reload" method if (reloadable.reload && typeof reloadable.reload === 'function') { this.reloadables[id] = reloadable; } }, /* * remove and object that was previously added to the reload list */ removeReloadableObject: function removeReloadableObject(id) { if (this.reloadables) { delete this.reloadables[id]; } }, /** * Important: use a collection to determine if the DatasetExecutionService needs to be setup * a better glass API might be required */ _registerDatasetExecution: function _registerDatasetExecution() { var _this = this; return this.glassContext.appController.findCollection('com.ibm.bi.dashboard.services').then(function (collection) { var services = collection || []; var setDatasetHandler = false; var i = 0; while (i < services.length && setDatasetHandler === false) { setDatasetHandler = 'com.ibm.bi.dashboard.services.datasetExecutionService' === services[i].id; i++; } if (setDatasetHandler) { return _this.glassContext.getSvc('.DatasetExecutionService').then(function (DatasetExecutionService) { _this.datasetRefreshEventHandler = DatasetExecutionService.on('loadComplete', _this._onDatasetRefresh.bind(_this)); }); } }); }, _resolveOptions: function _resolveOptions(options) { // EmbedAPI param: objRef // set a objRef as a boardId options.boardId = options.boardId || options.objRef; // EmbedAPI param: action // convert action to isAuthoringMode flag options.isAuthoringMode = this.hasAuthoringCapability() && (String(options.isAuthoringMode).toLowerCase() === 'true' || options.action === 'edit'); }, // this method will be called by glass isDirty: function isDirty() { var dashboardState = this.dashboardApi.getFeature('DashboardState'); return dashboardState ? dashboardState.getUiState().dirty : this.options.isDirtyOption; }, setPermissions: function setPermissions(permissions) { this.permissions = permissions; }, isNew: function isNew() { return !this.getBoardId(); }, canAuthor: function canAuthor() { return _.indexOf(this.permissions, 'write') !== -1; }, getBoardId: function getBoardId() { return this.boardModel && this.boardModel.id; }, /** * @returns an object containing dashboard information (isDirty, dashboard id and dashboard search path) */ getBoardInfo: function getBoardInfo() { return { isDirty: this.isDirty(), boardId: this.getBoardId(), searchPath: this.boardModel.searchPath, type: 'exploration' }; }, /** * This method is called by glass to get the content view type which, in turn, be used to match the sharing action handlers. * It is overridden in StoryView where the 'storytelling' is returned to match its corresponding action handler. */ getType: function getType() { return 'exploration'; }, getLoadedWidget: function getLoadedWidget(id) { var _boardLoader = this.boardLoader; _boardLoader = _boardLoader === undefined ? {} : _boardLoader; var widgetLoader = _boardLoader.widgetLoader; if (widgetLoader) { var widget = widgetLoader.getWidget(id); if (widget) { return widget.getAPI(); } } }, getContent: function getContent() { var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var state = _.pick(this.options, 'id', 'objRef', 'options', 'subView', 'filters'); state.isAuthoringMode = this.isAuthoringMode; if (this.options.boardId) { state.boardId = this.options.boardId; } // the glass checks the content id for the cache. This needs to be revisited after the glass cache cleanup if (state.objRef) { state.id = state.objRef; } this.id = state.id; if (this.boardModel && this.boardModel.id) { state.boardId = this.boardModel.id; state.id = this.boardModel.id; state.objRef = this.boardModel.id; this.id = this.boardModel.id; } var isBookmark = options && options.mode === this.glassContext.bookmarkMode; // save a version of the spec when we are not requesting the state for a bookmark if (!isBookmark) { state.boardSpec = this.options.boardSpec; if (this._isDashboardFeatureLoaded('DashboardState')) { state._isDirty = this.dashboardApi.getFeature('DashboardState').getUiState().dirty; } else { state._isDirty = options.dirty; } if (this._isDashboardFeatureLoaded('Serializer')) { state.boardSpec = this.dashboardApi.getFeature('Serializer').toJSON(); } } return state; }, _isDashboardFeatureLoaded: function _isDashboardFeatureLoaded(featureName) { if (this.dashboardApi) { return !!this.dashboardApi.getFeature(featureName); } return false; }, preprocessKeyDown: function preprocessKeyDown(e) { return e; }, // overwriteable function, implemented in child classes /** * @param keyArr {Number | String | Array of numbers/string} * @return {Boolean} returns true if and only if specified keys are pressed (ignores cmd/ctrl keys) */ isPressed: function isPressed(keyArr) { var _this2 = this; var getPressedKeyCodes = _.filter(_.keys(this._pressed), function (key) { return _this2._pressed[key] && [KeyCodes.LEFT_WINDOW_KEY, KeyCodes.CTRL, KeyCodes.CAPSLOCK, //filter out CapsLock because Chrome/FireFox only fire keydown event for CapsLock on KeyCodes.F11 //filter out F11 for FireFox, it only fire keydown event for f11 ].indexOf(parseInt(key)) == -1; }); var normalizedKeyArr = keyArr.length ? _.map(keyArr, function (n) { return String(n); }) : [String(keyArr)]; return _.isEqual(getPressedKeyCodes, normalizedKeyArr); }, /** * @return {Boolean} returns true if and only if any text is selected */ isTextSelected: function isTextSelected() { return window.getSelection().toString() !== ''; }, getECLProp: function getECLProp(e, key) { var eventProps = new EventChainLocal(e); return eventProps.getProperty(key); }, hasAuthoringCapability: function hasAuthoringCapability() { return true; }, _processKeyDown: function _processKeyDown(e) { var _this3 = this; e = this.preprocessKeyDown(e); if (e.target.tagName && e.target.tagName.match(/input|textarea|select/igm)) { return; } if ($(e.target).closest('[contentEditable=true]').length > 0) { return; } // for FireFox; // in FireFox, CapsLock only fire keydown event, no keyup event, so key code 20 will never be remove by _processKeyUp() // should be removed when user turn Caps off (like Chrome) if (e.keyCode == KeyCodes.CAPSLOCK && this._pressed[KeyCodes.CAPSLOCK]) { delete this._pressed[KeyCodes.CAPSLOCK]; } else { this._pressed[e.keyCode] = true; } var getECLProp = function getECLProp() { return _this3.getECLProp(e, 'doNotHandle'); }; var canHandleKey = function canHandleKey(arr) { if (_this3.isPressed(arr) && !getECLProp()) { e.preventDefault(); return true; } return false; }; if ((e.ctrlKey || e.metaKey) && !DialogBlocker.isShowingDialogBlocker()) { if (canHandleKey(KeyCodes.V)) { if (this.isAuthorMode()) { // paste functionality this.doPaste(); } } else if (canHandleKey(KeyCodes.S)) { if (this.hasAuthoringCapability()) { this.doSave(); } } else if (canHandleKey(KeyCodes.Y) || canHandleKey([KeyCodes.SHIFT, KeyCodes.Z])) { this.doRedo(); } else if (canHandleKey(KeyCodes.Z)) { this.doUndo(); } else if (canHandleKey([KeyCodes.Q, KeyCodes.FORWARDSLASH])) { //brings up message box, used to help debugging if (this.hasAuthoringCapability()) { this._openBoardSpecificationDialog(); } } else if (canHandleKey([KeyCodes.Q, KeyCodes.L])) { //log the Rave test spec to the console window.gLogRaveTestSpec = window.gLogRaveTestSpec !== true; } else if (!this.isTextSelected() && canHandleKey(KeyCodes.C)) { // copy functionality. Does not work for consumer only user when cosumer feature is on if (this.hasAuthoringCapability()) { this.doCopy(); } } else if (canHandleKey([KeyCodes.Q, KeyCodes.COMMA])) { //activate error details window.dashboardErrorDetailsEnabled = true; this.eventRouter.trigger('widget:onDetailErrors'); } } // - The meta key in iOS blockkey up events so in the case of cmd-z/cmd-shift-z/cmd-y/cmd-s //the key will never actually be release causing the s, y, and z to be sticky. This is why //in case of the meta key they need to be release here. // - for multi-key combo possibilities hold off on manually triggering keyup if (e.metaKey && e.keyCode !== KeyCodes.Q && e.keyCode !== KeyCodes.SHIFT) { delete this._pressed[e.keyCode]; } }, _openBoardSpecificationDialog: function _openBoardSpecificationDialog() { new EditableDialog({ sType: 'editableInfo', sTitle: 'Board Specification', sValue: JSON.stringify(this.dashboardApi.getFeature('Serializer').toJSON(), null, 4), actionHandler: this._updateBoardModelFromDialog.bind(this), updateable: true }).open(); }, /** * Handler function for board model dialog to get and parse new board spec, which then updates the canvas to match the board spec * @returns True if JSON is valid and false if not */ _updateBoardModelFromDialog: function _updateBoardModelFromDialog(newBoardModelString) { // Parse JSON of board model spec the user entered. Do not handle if it is bad JSON try { this.reloadFromJSONSpec(JSON.parse(newBoardModelString)); } catch (err) { this.logger.debug('Invalid JSON provided', err, this); this.glassContext.appController.showToast(this.stringService.get('invalidJSONResponse'), { 'type': 'error', 'preventDuplicates': true }); return false; } return true; }, doCopy: function doCopy() { return this.boardController.copyPasteController.doCopy(); }, doPaste: function doPaste() { return this.boardController.copyPasteController.doPaste(); }, _processKeyUp: function _processKeyUp() { this._pressed = {}; }, doSave: function doSave() { var savePlugin = this.glassContext.appController.findPlugin('com.ibm.bi.dashboard.saveMenu'); if (savePlugin) { savePlugin.defaultButton.$el.trigger('click'); } }, doUndo: function doUndo() { var popover = $('.popover'); if (popover.length > 0) { popover.popover('hide'); // remove popovers, they aren't updated on undo/redo and aren't cleared when clicking in the Glass buttons. } this.boardController.undoRedoController.undo(); }, doRedo: function doRedo() { var popover = $('.popover'); if (popover.length > 0) { popover.popover('hide'); // remove popovers, they aren't updated on undo/redo and aren't cleared when clicking in the Glass buttons. } this.boardController.undoRedoController.redo(); }, _onWidgetMaximize: function _onWidgetMaximize() { this._buttonHideHelper.changeMode(this.glassContext, 'widgetMaximized'); }, _onWidgetRestore: function _onWidgetRestore() { this._buttonHideHelper.changeMode(this.glassContext, this.dashboardController.getMode()); }, /** * Toggles the event group mode. * @param {String} mode The optional mode to explicitly toggle to (default: 'authoring') */ toggleEventGroupMode: function toggleEventGroupMode(mode) { var isEventGroupViewMode = this.isEventGroupViewMode; if (isEventGroupViewMode) { if (mode !== 'eventGroups') { // this._buttonHideHelper.changeMode(this.glassContext, mode || 'authoring'); return this.turnOffEventGroupView(mode); } else { // we are already in eventGroup mode return Promise.resolve(); } } else { this._buttonHideHelper.changeMode(this.glassContext, mode); return this.turnOnEventGroupView(); } }, /** * Toggles between authoring & consume mode */ toggleMode: function toggleMode(options) { var _this4 = this; // clear data sources cache var dataSourcesSvc = this.dashboardApi.getFeature('dataSources.deprecated'); if (dataSourcesSvc) { dataSourcesSvc.clearShapingCache(); } return (this.isAuthoringMode ? this.turnOffEditMode() : this.turnOnEditMode()).then(function () { _this4._finishModeChange(options); }); }, _addChangeModeActionToUndoRedoStack: function _addChangeModeActionToUndoRedoStack(options) { if (!options || options.sender !== 'UndoRedoController') { this.boardController.undoRedoController.addToUndoRedoStack(new ChangeModeAction(this, { glassContext: this.glassContext })); } }, _triggerWindowResize: function _triggerWindowResize() { try { $(window).resize(); } catch (e) { console.error(e); } }, _finishModeChange: function _finishModeChange() { var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; // We are switching to authoring mode. this.eventRouter.trigger('mode:change', { authoring: this.isAuthoringMode, isStoryMode: this.options.isStoryMode }); // dont add if options.addToUndoRedoStack is false if (options.addToUndoRedoStack !== false) { // avoid infinite undo this._addChangeModeActionToUndoRedoStack(options); } // Resize to redraw the visualization because the canvas will change in size between consumption and edit mode // This also re-renders the grid this._triggerWindowResize(); }, isAuthorMode: function isAuthorMode() { return !!this.isAuthoringMode; }, turnOnEditMode: function turnOnEditMode() { var _this5 = this; this.isAuthoringMode = true; return this.lifeCycleManager.invokeLifeCycleHandlers('pre:mode.edit').then(this.boardController.changeToAuthorMode.bind(this.boardController)).then(function () { // check for group view mode in authoring mode var isEventGroupViewMode = _this5.isEventGroupViewMode; if (isEventGroupViewMode) { return _this5.turnOnEventGroupView().then(_this5.applyModeToGlassPlugins.bind(_this5)); } else { _this5.applyModeToGlassPlugins(); } }).then(this.lifeCycleManager.invokeLifeCycleHandlers.bind(this.lifeCycleManager, 'post:mode.edit')); }, turnOffEditMode: function turnOffEditMode() { this.isAuthoringMode = false; return this.lifeCycleManager.invokeLifeCycleHandlers('pre:mode.consume').then(this.boardController.changeToConsumeMode.bind(this.boardController)).then(this.applyModeToGlassPlugins.bind(this)).then(this.lifeCycleManager.invokeLifeCycleHandlers.bind(this.lifeCycleManager, 'post:mode.consume')); }, turnOnEventGroupView: function turnOnEventGroupView() { var _this6 = this; this.isEventGroupViewMode = true; this.isAuthoringMode = true; this.boardController.undoRedoController.setMode('eventGroups'); return this.boardController.changeToEventGroupMode().then(function () { _this6.applyModeToGlassPlugins(); }); }, turnOffEventGroupView: function turnOffEventGroupView(mode) { var _this7 = this; this.isEventGroupViewMode = false; this.boardController.undoRedoController.setMode(null); var consumptionMode = mode === 'consumption'; return (consumptionMode ? this.turnOffEditMode() : this.turnOnEditMode()).then(function () { return _this7._finishModeChange({ addToUndoRedoStack: consumptionMode }); }); }, applyModeToGlassPlugins: function applyModeToGlassPlugins() { if (this.isAuthoringMode) { if (this.isEventGroupViewMode) { this._buttonHideHelper.changeMode(this.glassContext, 'connections', this.options); } else { this._buttonHideHelper.changeMode(this.glassContext, 'authoring', this.options); } } else { this._buttonHideHelper.changeMode(this.glassContext, 'consume', this.options); } }, _createServices: function _createServices() { this.servicesHelper = new ServicesHelper({ glassContext: this.glassContext, eventRouter: this.eventRouter, appSettings: this.getContent(), logger: this.logger, dashboardAPI: this.dashboardApi, internalDashboardAPI: this.internalDashboardApi }); var dashboardServices = this.servicesHelper.createServices(); this.stringService = dashboardServices.stringService; this.lifeCycleManager = dashboardServices.lifeCycleManager; this.dnDManager = dashboardServices.dnDManager; this.services = dashboardServices.serviceRegistry; this.featureLoader = dashboardServices.featureLoader; this.dashboardController.setServices(this.services); this.dashboardController.setFeatureLoader(this.featureLoader); }, getDeprecatedCanvas: function getDeprecatedCanvas() { if (this.boardController) { return this.boardController.getDeprecatedCanvas(); } return null; }, getDeprecatedCanvasWhenReady: function getDeprecatedCanvasWhenReady() { var _this8 = this; if (this._boardRendered) { return this._boardRendered.then(function () { return _this8.boardController.getDeprecatedCanvas(); }); } return Promise.reject(new Error('Unable to getCanvas . Board is not rendered yet.')); }, /** * NOTE: This is overridden in other components such as Explore. * * @returns whether the user has the dashboard capability or not. */ hasCapability: function hasCapability() { return ErrorUtils.hasCapability(this.glassContext); }, _registerBootstrapFeatures: function _registerBootstrapFeatures() { var featureLoader = this.dashboardController.featureLoader; return this.dashboardFactory.getBootstrapFeatures().then(function (features) { Object.keys(features).forEach(function (featureName) { featureLoader.registerFeature(featureName, features[featureName], undefined, true); }); }); }, render: function render() { var _this9 = this; BaseBoardView.inherited('render', this, arguments); this.$el.addClass('boardPageView'); // Todo -- lifecycle_cleanup -- we shouldn't have to do this // This will create the content node .. if not done here, the toolbar will be at the bottom of the page // because it will be created before the content node this.getContentNode(); if (!this.hasCapability()) { return Promise.reject({ code: 'noCapability', message: this.stringService.get('noDashboardCapability') }); } this.setupKeyboardHandlers(); this.ajaxSvc = this.glassContext.getCoreSvc('.Ajax'); var isAuthoringMode = this.isAuthoringMode; this.eventRouter.on('widget:maximize', this._onWidgetMaximize, this); this.eventRouter.on('widget:restore', this._onWidgetRestore, this); PerfUtils.createPerformanceMark({ 'component': 'dashboard', 'name': 'LoadBoard', 'state': 'start' }); this.boardModelFactory = this.boardModelFactory || new BoardModelFactory({ dashboardAPI: this.dashboardApi, glassContext: this.glassContext, boardId: this.boardId, objectUrl: this.objectUrl, boardSpec: this.boardSpec, widgetRegistry: this.widgetRegistry, extensions: this.extensions, stringResources: this.dashboardApi.getDashboardCoreSvc('.StringResources'), eventRouter: this.eventRouter, logger: this.logger, createLayoutModel: this.createLayoutModel }); this._boardRendered = this._registerBootstrapFeatures().then(this.extensions.load.bind(this.extensions)).then(this.widgetRegistry.initialize.bind(this.widgetRegistry, this.dashboardApi)).then(this.boardModelFactory.createBoardModel.bind(this.boardModelFactory)).then(function (boardModelInfo) { _this9.boardModel = boardModelInfo.boardModel; _this9.boardSpec = boardModelInfo.boardSpec; _this9.boardId = boardModelInfo.boardId; }).then(this.glassContext.appController.findCollection.bind(this.glassContext.appController, 'com.ibm.bi.dashboard.templates')).then(function (templateItems) { return _this9.glassContext.appController.findCollection('com.ibm.bi.dashboard.contentTypes').then(function (contentTypes) { return _this9.servicesHelper.createRuntimeServices({ dashboardApi: _this9.getDashboardApi(), collections: { templates: templateItems, contentTypes: contentTypes } }); }); }).then(this._registerDatasetExecution.bind(this)).then(function () { _this9.boardLoader = new _this9._boardLoaderClass({ // beging - To be removed glassContext: _this9.glassContext, isAuthoringMode: isAuthoringMode, services: _this9.services, ajaxSvc: _this9.ajaxSvc, appSettings: _this9.getContent(), // end - To be removed dashboardApi: _this9.getDashboardApi(), eventRouter: _this9.eventRouter, setDirty: _this9.boardModel.isUpgraded, //TOOO maybe remove boardSpec: _this9.boardSpec, boardModel: _this9.boardModel, el: _this9.getContentNode()[0], $viewEl: _this9.$el, widgetRegistry: _this9.widgetRegistry, gatewayUrl: 'v1', cdnUrl: _this9.getCDNUrl(), featureLoader: _this9.featureLoader, extensions: _this9.extensions, dashboardController: _this9.dashboardController }); return _this9.boardLoader.loadBoard(); }).then(function () { return _this9.boardLoader.getCanvasController().then(function (canvasController) { _this9.boardController = canvasController; }); }).then(function () { _this9.canUndo = false; _this9.canRedo = false; _this9.applyModeToGlassPlugins(); _this9._registerUndoRedoListener(_this9.buttonIds.UNDO); _this9._registerUndoRedoListener(_this9.buttonIds.REDO); _this9.boardModel.on('change:name', _this9._onNameChange, _this9); _this9.boardModel.on('all', _this9._onAllModelEvents, _this9); _this9.dashboardApi.getCanvas().on('all', _this9._manageDirtyFlagForCanvasEvents.bind(_this9)); _this9.boardController.onPageRenderComplete().then(function () { PerfUtils.createPerformanceMark({ 'component': 'dashboard', 'name': 'LoadBoard', 'state': 'end' }); }); if (_this9.containerAppOptions && _this9.containerAppOptions.callbacks) { _this9.containerAppOptions.callbacks.resolve(_this9.getDashboardApi()); } _this9.canvasExtensions = new CanvasExtensions({ topNode: _this9.getCanvasExtensionsTopNode(), bottomNode: _this9.getCanvasExtensionsBottomNode(), splitterItems: _this9._splitterItems, canvasExtensions: _this9.extensions.getCanvasExtensions() || [], dashboardApi: _this9.getDashboardApi(), services: _this9.services, eventRouter: _this9.eventRouter, appSettings: _this9.getContent(), handlers: { getParentSize: function getParentSize() { return { height: _this9.$el.height(), width: _this9.$el.width() }; }, isAuthorMode: _this9.isAuthorMode.bind(_this9), getSplitterState: _this9.getSplitterState.bind(_this9) } }); _this9.services.register('.CanvasExtensions', _this9.canvasExtensions); _this9.services.register('.CopyPasteController', _this9.boardController.copyPasteController); _this9.featureLoader.registerFeature('UndoRedo', new UndoRedo(_this9.boardController.undoRedoController)); if (_this9.firstRender) { var dashboardState = _this9.dashboardApi.getFeature('DashboardState'); dashboardState.setDirty(_this9.options.isDirtyOption); _this9.firstRender = false; } // The dashboard is not saved yet if (!_this9.getBoardId() || _this9.boardLoader.setDirty) { var _dashboardState = _this9.dashboardApi.getFeature('DashboardState'); _dashboardState.setDirty(true); if (!_this9.getBoardId()) { _this9.title = _this9.stringService.get('defaultName'); } _this9.boardLoader.setDirty = false; } else { _this9.title = _this9.getTitle(); } _this9.dashboardApi.getFeature('DashboardState').setActive(true); _this9.logger.debug('Authored View created', _this9.options, _this9); _this9.onCanvasReady(); // Waiting for pageReady causes issues with the glass not showing the page at the right time. return _this9.boardController.onPageReady().then(function () { if (_this9.sources) { var dataSourcesFeature = _this9.dashboardApi.getFeature('DataSources'); _this9.sources.forEach(function (source) { return dataSourcesFeature.addDataSource(source, false, { silent: true, payloadData: { skipUndoRedo: true } }); }); // Once we've added the initial source to the dataSources feature, clear the property so we don't readd them during a relink _this9.options.sources = _this9.sources = null; } var printFeature = _this9.dashboardApi.getFeature('Print'); var dashboardPrint = _this9.dashboardApi.getFeature('DashboardPrint'); printFeature.registerContent('tab', dashboardPrint); printFeature.registerContent('page', dashboardPrint); }).then(function () { return _this9.canvasExtensions.render(); }).then(function () { return undefined; }); }); return this._boardRendered.catch(function (e) { if (_this9.logger) { _this9.logger.log('An error occured while rendering the baseBoardView'); _this9.logger.error(e); } throw e; }); }, getSplitterState: function getSplitterState() { // to be implemented by the concrete class }, _onNameChange: function _onNameChange(event) { this.trigger('change:title', event); this.title = event.value; }, _onAllModelEvents: function _onAllModelEvents(event) { // This is done so that the edit button does not trigger the dirty flag var isUndoRedo = event && (event.stack === 'undo' || event.stack === 'redo'); var isRuntimeChange = event && event.data && event.data.runtimeOnly; var isEventGroupsChange = !this.isAuthoringMode && event && event.eventName && event.eventName === 'change:eventGroups'; var skipUndoRedo = event && event.data && event.data.skipUndoRedo; var dashboardState = this.dashboardApi.getFeature('DashboardState'); if (!isRuntimeChange && !isEventGroupsChange) { var _dashboardState$getUi = dashboardState.getUiState(), dirty = _dashboardState$getUi.dirty; if (!isUndoRedo && !dirty && !skipUndoRedo) { dashboardState.setDirty(true); } else if (isUndoRedo) { var isUndoRedoDirty = this.boardController.undoRedoController.isDirty(); if (dirty && !isUndoRedoDirty) { dashboardState.setDirty(false); } else if (!dirty && isUndoRedoDirty) { dashboardState.setDirty(true); } } } }, _manageDirtyFlagForCanvasEvents: function _manageDirtyFlagForCanvasEvents(event) { if (event.info && event.info.supportsUndoRedo && event.context && event.context.undoRedo !== true) { if (!this.isDirty()) { var dashboardState = this.dashboardApi.getFeature('DashboardState'); dashboardState.setDirty(true); } } }, getCanvasExtensionsTopNode: function getCanvasExtensionsTopNode() { if (!this.$top) { var $parentNode = this.getContentNode(); var $top = $('.canvasExtensionTop', $parentNode); if ($top.length === 0) { $top = $('
'); $parentNode.prepend($top); } this.$top = $top; } return this.$top; }, getCanvasExtensionsBottomNode: function getCanvasExtensionsBottomNode() { if (!this.$bottom) { var $bottom = $('.canvasExtensionBottom', this.el); if ($bottom.length === 0) { $bottom = $('
'); this.$el.append($bottom); } this.$bottom = $bottom; } return this.$bottom; }, getDashboardApi: function getDashboardApi() { return this.dashboardApi; }, onCanvasReady: function onCanvasReady() { // to be implemented by the concrete class }, _registerUndoRedoListener: function _registerUndoRedoListener(id) { var plugin = this.glassContext.appController.findPlugin(id); if (plugin) { this.boardController.undoRedoController.addListener(id, plugin); } }, remove: function remove() { BaseBoardView.inherited('remove', this, arguments); this.teardownKeyboardHandlers(); this.eventRouter.off('widget:maximize', this._onWidgetMaximize, this); this.eventRouter.off('widget:restore', this._onWidgetRestore, this); // Destroy the view before the services and APIs try { this.boardLoader.destroyViews(); } catch (e) { if (this.logger) { this.logger.error(e); } } try { this.boardLoader.destroyCanvasAPI(); } catch (e) { if (this.logger) { this.logger.error(e); } } if (this.datasetRefreshEventHandler) { this.datasetRefreshEventHandler.remove(); } this.boardLoader = null; this.dashboardController.destroy(); MemUtil.destroy(this); }, getTitle: function getTitle() { return this.boardModel && this.boardModel.get('name') || ''; }, setTitle: function setTitle(title) { this.title = title; this.trigger('change:title', { value: title }); }, openDatasetpane: function openDatasetpane() { var plugin = this.glassContext.appController.findPlugin('com.ibm.bi.dashboard.dataSources.sourcesBtn'); if (plugin && !plugin.isPressed()) { plugin.$el.trigger('click', { expandFirstEntry: true }); } }, setupKeyboardHandlers: function setupKeyboardHandlers() { if (!this._keyDownBind) { this._keyDownBind = this._processKeyDown.bind(this); this._keyUpBind = this._processKeyUp.bind(this); this._windowBlur = this._processKeyUp.bind(this); $(document).on('keydown', this._keyDownBind); $(document).on('keyup', this._keyUpBind); $(window).on('blur', this._windowBlur); } }, teardownKeyboardHandlers: function teardownKeyboardHandlers() { $(document).off('keydown', this._keyDownBind); $(document).off('keyup', this._keyUpBind); $(window).off('blur', this._windowBlur); this._keyDownBind = null; this._keyUpBind = null; this._windowBlur = null; }, _onDatasetRefresh: function _onDatasetRefresh(payload) { this._modifiedDataset.push(payload.id); }, /** * Called by the glass when the view is deactivated (e.g. switching to a different view) */ deactivate: function deactivate() { // CADBC-892 dashboard state may not be available in hardnav scenarios var dashboardState = this.dashboardApi.getFeature('DashboardState'); dashboardState && dashboardState.setActive(false); if (this.boardLoader) { this.boardLoader.deactivate(); } this.teardownKeyboardHandlers(); if (this.canvasExtensions) { this.canvasExtensionState = this.canvasExtensions.getState(); } this.dashboardApi.triggerDashboardEvent('dashboard:deactivate'); return BaseBoardView.inherited('deactivate', this, arguments); }, /** * Called by the glass just before the view is being shown * @param {Object} content - content object from Glass. { perspective: xx, id: xx, content:xx } */ activate: function activate(content) { var _this10 = this; this.dashboardApi.getFeature('DashboardState').setActive(true); if (!this.boardLoader) { this.setupKeyboardHandlers(); return BaseBoardView.inherited('activate', this, [content]); } var always = function always() { var promise = _this10.boardLoader.activate(); if (!promise || !promise.then) { promise = Promise.resolve(); } return promise.then(function () { _this10.dashboardApi.triggerDashboardEvent('dashboard:show'); return _this10.setupKeyboardHandlers(); }); }; return BaseBoardView.inherited('activate', this, [content]).then(function () { // Before activating the dashboard view, check if any data source is changed, in which case we need to // reload metadata and re-execute widgets var result = void 0; if (_this10._modifiedDataset.length > 0) { result = _this10._refreshDataSource().then(function () { _this10._modifiedDataset = []; }); } else { result = Promise.resolve(); } _this10.canvasExtensions.setState(_this10.canvasExtensionState); return result; }).then(always).catch(always); }, /** * Refresh datasources that are referenced in dashboard and were modified */ _refreshDataSource: function _refreshDataSource() { var _this11 = this; var dataSourcesSvc = this.dashboardApi.getFeature('dataSources.deprecated'); var sourcesCollection = dataSourcesSvc.getSourcesCollection(); var aDataSources = sourcesCollection.getSources(); var datasourcePromises = []; _.each(aDataSources, function (source) { if (_this11._modifiedDataset.indexOf(source.getAssetId()) >= 0) { datasourcePromises.push(source.reloadMetadata()); } }); return Promise.all(datasourcePromises); }, _removeForReload: function _removeForReload() { this.teardownKeyboardHandlers(); this.boardLoader.destroyViews(); this.boardLoader.destroyCanvasAPI(); this.boardLoader = null; if (this.datasetRefreshEventHandler) { this.datasetRefreshEventHandler.remove(); } if (this.services) { this.services.destroy(); } this.dashboardApi.destroy(); this.dashboardApi = null; this.boardModel = null; this.$el.empty(); this.$bottom = null; this.$top = null; }, reloadFromJSONSpec: function reloadFromJSONSpec(JSONSpec) { var _this12 = this; var extOptions = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var isAuthoringMode = extOptions.isAuthoringMode, _extOptions$clearDirt = extOptions.clearDirtyFlag, clearDirtyFlag = _extOptions$clearDirt === undefined ? false : _extOptions$clearDirt; this.dashboardController.toggleProperties(false); this._removeForReload(); var options = this.options; options.boardSpec = JSONSpec; options.isAuthoringMode = typeof isAuthoringMode !== 'undefined' ? isAuthoringMode : this.isAuthoringMode; if (extOptions.boardId) { options.boardId = extOptions.boardId; //Reload from JSON Spec with passed in board ID } this.initialize(options); return this.render().then(function () { if (_this12.reloadables) { Object.values(_this12.reloadables).filter(function (reloadable) { return typeof reloadable.reload === 'function'; }).forEach(function (reloadable) { return reloadable.reload(); }); } _this12.setFocus(); // Reset the flag, otherwise the current flag state propagates var dashboardState = _this12.dashboardApi.getFeature('DashboardState'); if (clearDirtyFlag && !_this12.boardLoader.setDirty) { dashboardState.setDirty(false); } else { dashboardState.setDirty(true); } return _this12.dashboardApi; }); }, /** * Temp until glass provides us with an official API to know when we're visible. * @return {[type]} [description] */ setFocus: function setFocus() { var _this13 = this; BaseBoardView.inherited('setFocus', this, arguments); this.lifeCycleManager.invokeLifeCycleHandlers('pre:dashboard.focus').then(this.boardLoader.getCanvasController.bind(this.boardLoader)).then(function (canvasController) { return canvasController.layoutController.getTopLayoutViewWhenReady(); }).then(function (layoutView) { // Call onShow which will do nothing if they had already rendered or make them render correctly if (layoutView) { layoutView.onShow(); } return _this13.lifeCycleManager.invokeLifeCycleHandlers('post:dashboard.focus'); }).catch(function (err) { _this13.logger.error('Failed to setFocus', err, _this13); }); } }); return BaseBoardView; }); //# sourceMappingURL=BaseBoardView.js.map