'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 inStoryView
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