BaseBoardView.js 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194
  1. 'use strict';
  2. /*
  3. *+------------------------------------------------------------------------+
  4. *| Licensed Materials - Property of IBM
  5. *| IBM Cognos Products: Dashboard
  6. *| (C) Copyright IBM Corp. 2015, 2021
  7. *|
  8. *| US Government Users Restricted Rights - Use, duplication or disclosure
  9. *| restricted by GSA ADP Schedule Contract with IBM Corp.
  10. *+------------------------------------------------------------------------+
  11. */
  12. 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) {
  13. var BaseBoardView = BaseView.extend({
  14. buttonIds: {
  15. UNDO: 'com.ibm.bi.dashboard.undo',
  16. REDO: 'com.ibm.bi.dashboard.redo'
  17. },
  18. init: function init(options) {
  19. BaseBoardView.inherited('init', this, arguments);
  20. this.dashboardApi = null;
  21. this.initialize(options);
  22. if (typeof window.__getDashboardAPI === 'undefined') {
  23. window.__getDashboardAPI = function () {
  24. return window.__glassAppController.Glass.getCurrentContentView().getDashboardApi();
  25. };
  26. }
  27. this.firstRender = true;
  28. },
  29. initialize: function initialize(options) {
  30. this._resolveOptions(options);
  31. _.extend(this, options);
  32. this.options = options || {};
  33. var dirtyOption = String(options._isDirty).toLowerCase() === 'true';
  34. this.options['isDirtyOption'] = dirtyOption;
  35. this._pressed = {};
  36. this._boardLoaderClass = options.boardLoaderClass || BoardLoader;
  37. this.eventRouter = new EventRouter();
  38. this._buttonHideHelper = options._buttonHideHelper || ButtonHideHelper;
  39. this._splitterItems = [];
  40. this._modifiedDataset = [];
  41. this.upgradesCollectionPath = 'dashboard/upgrades';
  42. this.logger = options.glassContext.getCoreSvc('.Logger');
  43. this.$content = null;
  44. this.widgetRegistry = options.widgetRegistry || new WidgetRegistry();
  45. this._createController(options);
  46. this._prepareForRendering(options);
  47. },
  48. /**
  49. * Creates services and extensions required for rendering the dashboard
  50. * @param {object} options consist of attributes required to create services & extensions for rendering dashboard
  51. */
  52. _prepareForRendering: function _prepareForRendering(options) {
  53. this._createServices();
  54. // merge the containerApp (API container) configuration with the content options config
  55. if (options.containerAppOptions) {
  56. _.extend(this.options.options.config, options.containerAppOptions.configuration);
  57. }
  58. // Allow preprocessKeyDown to be both overwritten by an extending class directly or via a call to super
  59. if (options.preprocessKeyDown) this.preprocessKeyDown = options.preprocessKeyDown;
  60. this.specHelper = null;
  61. this.extensions = options.extensions || new Extensions(this.dashboardApi);
  62. this.boardModelFactory = options.boardModelFactory;
  63. this.dashboardFactory = new GlassDashboardFactory({
  64. glassContext: this.glassContext
  65. });
  66. },
  67. _createController: function _createController(options) {
  68. var DashboardControllerClass = options.overrideDashboardController ? options.overrideDashboardController : DashboardController;
  69. this.dashboardController = options.dashboardController || new DashboardControllerClass({
  70. glassContext: this.glassContext,
  71. view: this,
  72. services: this.services,
  73. featureLoader: this.featureLoader,
  74. eventRouter: this.eventRouter,
  75. appSettings: this.getContent({
  76. dirty: this.options.isDirtyOption
  77. }),
  78. widgetRegistry: this.widgetRegistry
  79. });
  80. if (this.dashboardApi) {
  81. this.dashboardApi = APIFactory.refreshAPI(this.dashboardApi, [this.dashboardController]);
  82. this.internalDashboardApi = APIFactory.refreshAPI(this.internalDashboardApi, [this.dashboardController, this.dashboardController.internalImpl]);
  83. } else {
  84. this.dashboardApi = this.dashboardController.getAPI();
  85. this.internalDashboardApi = this.dashboardController.getInternalAPI();
  86. }
  87. },
  88. getApplicationLabel: function getApplicationLabel() {
  89. return StringResources.get('dashboard');
  90. },
  91. getContentNode: function getContentNode() {
  92. if (this.$content === null) {
  93. var $contentNode = this.$('.pageViewContent', this.el);
  94. if ($contentNode.length === 0) {
  95. $contentNode = $('<div class="pageViewContent"></div>').prependTo(this.el);
  96. }
  97. this.$content = $contentNode;
  98. }
  99. return this.$content;
  100. },
  101. /*
  102. * Used to a reset dashboard. Save a copy of the board spec as a string.
  103. */
  104. setSavedInstance: function setSavedInstance(specString) {
  105. this.savedInstance = specString;
  106. },
  107. getSavedInstance: function getSavedInstance() {
  108. return this.savedInstance;
  109. },
  110. /**
  111. * Add an object that needs to be reloaded when the dashboard view is reloaded.
  112. * This is used to reload the data sources panel, properties when the dashboard is reloaded (e.g. relink)
  113. * @param {string} id A name for the reloadable object
  114. * @param {object} reloadable An object that has the "reload" method. The "reload" method is required on the object
  115. */
  116. addReloadableObject: function addReloadableObject(id, reloadable) {
  117. if (!this.reloadables) {
  118. this.reloadables = {};
  119. }
  120. // Only add the reloadable if it has the "reload" method
  121. if (reloadable.reload && typeof reloadable.reload === 'function') {
  122. this.reloadables[id] = reloadable;
  123. }
  124. },
  125. /*
  126. * remove and object that was previously added to the reload list
  127. */
  128. removeReloadableObject: function removeReloadableObject(id) {
  129. if (this.reloadables) {
  130. delete this.reloadables[id];
  131. }
  132. },
  133. /**
  134. * Important: use a collection to determine if the DatasetExecutionService needs to be setup
  135. * a better glass API might be required
  136. */
  137. _registerDatasetExecution: function _registerDatasetExecution() {
  138. var _this = this;
  139. return this.glassContext.appController.findCollection('com.ibm.bi.dashboard.services').then(function (collection) {
  140. var services = collection || [];
  141. var setDatasetHandler = false;
  142. var i = 0;
  143. while (i < services.length && setDatasetHandler === false) {
  144. setDatasetHandler = 'com.ibm.bi.dashboard.services.datasetExecutionService' === services[i].id;
  145. i++;
  146. }
  147. if (setDatasetHandler) {
  148. return _this.glassContext.getSvc('.DatasetExecutionService').then(function (DatasetExecutionService) {
  149. _this.datasetRefreshEventHandler = DatasetExecutionService.on('loadComplete', _this._onDatasetRefresh.bind(_this));
  150. });
  151. }
  152. });
  153. },
  154. _resolveOptions: function _resolveOptions(options) {
  155. // EmbedAPI param: objRef
  156. // set a objRef as a boardId
  157. options.boardId = options.boardId || options.objRef;
  158. // EmbedAPI param: action
  159. // convert action to isAuthoringMode flag
  160. options.isAuthoringMode = this.hasAuthoringCapability() && (String(options.isAuthoringMode).toLowerCase() === 'true' || options.action === 'edit');
  161. },
  162. // this method will be called by glass
  163. isDirty: function isDirty() {
  164. var dashboardState = this.dashboardApi.getFeature('DashboardState');
  165. return dashboardState ? dashboardState.getUiState().dirty : this.options.isDirtyOption;
  166. },
  167. setPermissions: function setPermissions(permissions) {
  168. this.permissions = permissions;
  169. },
  170. isNew: function isNew() {
  171. return !this.getBoardId();
  172. },
  173. canAuthor: function canAuthor() {
  174. return _.indexOf(this.permissions, 'write') !== -1;
  175. },
  176. getBoardId: function getBoardId() {
  177. return this.boardModel && this.boardModel.id;
  178. },
  179. /**
  180. * @returns an object containing dashboard information (isDirty, dashboard id and dashboard search path)
  181. */
  182. getBoardInfo: function getBoardInfo() {
  183. return {
  184. isDirty: this.isDirty(),
  185. boardId: this.getBoardId(),
  186. searchPath: this.boardModel.searchPath,
  187. type: 'exploration'
  188. };
  189. },
  190. /**
  191. * This method is called by glass to get the content view type which, in turn, be used to match the sharing action handlers.
  192. * It is overridden in <code>StoryView</code> where the 'storytelling' is returned to match its corresponding action handler.
  193. */
  194. getType: function getType() {
  195. return 'exploration';
  196. },
  197. getLoadedWidget: function getLoadedWidget(id) {
  198. var _boardLoader = this.boardLoader;
  199. _boardLoader = _boardLoader === undefined ? {} : _boardLoader;
  200. var widgetLoader = _boardLoader.widgetLoader;
  201. if (widgetLoader) {
  202. var widget = widgetLoader.getWidget(id);
  203. if (widget) {
  204. return widget.getAPI();
  205. }
  206. }
  207. },
  208. getContent: function getContent() {
  209. var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  210. var state = _.pick(this.options, 'id', 'objRef', 'options', 'subView', 'filters');
  211. state.isAuthoringMode = this.isAuthoringMode;
  212. if (this.options.boardId) {
  213. state.boardId = this.options.boardId;
  214. }
  215. // the glass checks the content id for the cache. This needs to be revisited after the glass cache cleanup
  216. if (state.objRef) {
  217. state.id = state.objRef;
  218. }
  219. this.id = state.id;
  220. if (this.boardModel && this.boardModel.id) {
  221. state.boardId = this.boardModel.id;
  222. state.id = this.boardModel.id;
  223. state.objRef = this.boardModel.id;
  224. this.id = this.boardModel.id;
  225. }
  226. var isBookmark = options && options.mode === this.glassContext.bookmarkMode;
  227. // save a version of the spec when we are not requesting the state for a bookmark
  228. if (!isBookmark) {
  229. state.boardSpec = this.options.boardSpec;
  230. if (this._isDashboardFeatureLoaded('DashboardState')) {
  231. state._isDirty = this.dashboardApi.getFeature('DashboardState').getUiState().dirty;
  232. } else {
  233. state._isDirty = options.dirty;
  234. }
  235. if (this._isDashboardFeatureLoaded('Serializer')) {
  236. state.boardSpec = this.dashboardApi.getFeature('Serializer').toJSON();
  237. }
  238. }
  239. return state;
  240. },
  241. _isDashboardFeatureLoaded: function _isDashboardFeatureLoaded(featureName) {
  242. if (this.dashboardApi) {
  243. return !!this.dashboardApi.getFeature(featureName);
  244. }
  245. return false;
  246. },
  247. preprocessKeyDown: function preprocessKeyDown(e) {
  248. return e;
  249. }, // overwriteable function, implemented in child classes
  250. /**
  251. * @param keyArr {Number | String | Array of numbers/string}
  252. * @return {Boolean} returns true if and only if specified keys are pressed (ignores cmd/ctrl keys)
  253. */
  254. isPressed: function isPressed(keyArr) {
  255. var _this2 = this;
  256. var getPressedKeyCodes = _.filter(_.keys(this._pressed), function (key) {
  257. 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
  258. KeyCodes.F11 //filter out F11 for FireFox, it only fire keydown event for f11
  259. ].indexOf(parseInt(key)) == -1;
  260. });
  261. var normalizedKeyArr = keyArr.length ? _.map(keyArr, function (n) {
  262. return String(n);
  263. }) : [String(keyArr)];
  264. return _.isEqual(getPressedKeyCodes, normalizedKeyArr);
  265. },
  266. /**
  267. * @return {Boolean} returns true if and only if any text is selected
  268. */
  269. isTextSelected: function isTextSelected() {
  270. return window.getSelection().toString() !== '';
  271. },
  272. getECLProp: function getECLProp(e, key) {
  273. var eventProps = new EventChainLocal(e);
  274. return eventProps.getProperty(key);
  275. },
  276. hasAuthoringCapability: function hasAuthoringCapability() {
  277. return true;
  278. },
  279. _processKeyDown: function _processKeyDown(e) {
  280. var _this3 = this;
  281. e = this.preprocessKeyDown(e);
  282. if (e.target.tagName && e.target.tagName.match(/input|textarea|select/igm)) {
  283. return;
  284. }
  285. if ($(e.target).closest('[contentEditable=true]').length > 0) {
  286. return;
  287. }
  288. // for FireFox;
  289. // in FireFox, CapsLock only fire keydown event, no keyup event, so key code 20 will never be remove by _processKeyUp()
  290. // should be removed when user turn Caps off (like Chrome)
  291. if (e.keyCode == KeyCodes.CAPSLOCK && this._pressed[KeyCodes.CAPSLOCK]) {
  292. delete this._pressed[KeyCodes.CAPSLOCK];
  293. } else {
  294. this._pressed[e.keyCode] = true;
  295. }
  296. var getECLProp = function getECLProp() {
  297. return _this3.getECLProp(e, 'doNotHandle');
  298. };
  299. var canHandleKey = function canHandleKey(arr) {
  300. if (_this3.isPressed(arr) && !getECLProp()) {
  301. e.preventDefault();
  302. return true;
  303. }
  304. return false;
  305. };
  306. if ((e.ctrlKey || e.metaKey) && !DialogBlocker.isShowingDialogBlocker()) {
  307. if (canHandleKey(KeyCodes.V)) {
  308. if (this.isAuthorMode()) {
  309. // paste functionality
  310. this.doPaste();
  311. }
  312. } else if (canHandleKey(KeyCodes.S)) {
  313. if (this.hasAuthoringCapability()) {
  314. this.doSave();
  315. }
  316. } else if (canHandleKey(KeyCodes.Y) || canHandleKey([KeyCodes.SHIFT, KeyCodes.Z])) {
  317. this.doRedo();
  318. } else if (canHandleKey(KeyCodes.Z)) {
  319. this.doUndo();
  320. } else if (canHandleKey([KeyCodes.Q, KeyCodes.FORWARDSLASH])) {
  321. //brings up message box, used to help debugging
  322. if (this.hasAuthoringCapability()) {
  323. this._openBoardSpecificationDialog();
  324. }
  325. } else if (canHandleKey([KeyCodes.Q, KeyCodes.L])) {
  326. //log the Rave test spec to the console
  327. window.gLogRaveTestSpec = window.gLogRaveTestSpec !== true;
  328. } else if (!this.isTextSelected() && canHandleKey(KeyCodes.C)) {
  329. // copy functionality. Does not work for consumer only user when cosumer feature is on
  330. if (this.hasAuthoringCapability()) {
  331. this.doCopy();
  332. }
  333. } else if (canHandleKey([KeyCodes.Q, KeyCodes.COMMA])) {
  334. //activate error details
  335. window.dashboardErrorDetailsEnabled = true;
  336. this.eventRouter.trigger('widget:onDetailErrors');
  337. }
  338. }
  339. // - The meta key in iOS blockkey up events so in the case of cmd-z/cmd-shift-z/cmd-y/cmd-s
  340. //the key will never actually be release causing the s, y, and z to be sticky. This is why
  341. //in case of the meta key they need to be release here.
  342. // - for multi-key combo possibilities hold off on manually triggering keyup
  343. if (e.metaKey && e.keyCode !== KeyCodes.Q && e.keyCode !== KeyCodes.SHIFT) {
  344. delete this._pressed[e.keyCode];
  345. }
  346. },
  347. _openBoardSpecificationDialog: function _openBoardSpecificationDialog() {
  348. new EditableDialog({
  349. sType: 'editableInfo',
  350. sTitle: 'Board Specification',
  351. sValue: JSON.stringify(this.dashboardApi.getFeature('Serializer').toJSON(), null, 4),
  352. actionHandler: this._updateBoardModelFromDialog.bind(this),
  353. updateable: true
  354. }).open();
  355. },
  356. /**
  357. * Handler function for board model dialog to get and parse new board spec, which then updates the canvas to match the board spec
  358. * @returns True if JSON is valid and false if not
  359. */
  360. _updateBoardModelFromDialog: function _updateBoardModelFromDialog(newBoardModelString) {
  361. // Parse JSON of board model spec the user entered. Do not handle if it is bad JSON
  362. try {
  363. this.reloadFromJSONSpec(JSON.parse(newBoardModelString));
  364. } catch (err) {
  365. this.logger.debug('Invalid JSON provided', err, this);
  366. this.glassContext.appController.showToast(this.stringService.get('invalidJSONResponse'), {
  367. 'type': 'error',
  368. 'preventDuplicates': true
  369. });
  370. return false;
  371. }
  372. return true;
  373. },
  374. doCopy: function doCopy() {
  375. return this.boardController.copyPasteController.doCopy();
  376. },
  377. doPaste: function doPaste() {
  378. return this.boardController.copyPasteController.doPaste();
  379. },
  380. _processKeyUp: function _processKeyUp() {
  381. this._pressed = {};
  382. },
  383. doSave: function doSave() {
  384. var savePlugin = this.glassContext.appController.findPlugin('com.ibm.bi.dashboard.saveMenu');
  385. if (savePlugin) {
  386. savePlugin.defaultButton.$el.trigger('click');
  387. }
  388. },
  389. doUndo: function doUndo() {
  390. var popover = $('.popover');
  391. if (popover.length > 0) {
  392. popover.popover('hide'); // remove popovers, they aren't updated on undo/redo and aren't cleared when clicking in the Glass buttons.
  393. }
  394. this.boardController.undoRedoController.undo();
  395. },
  396. doRedo: function doRedo() {
  397. var popover = $('.popover');
  398. if (popover.length > 0) {
  399. popover.popover('hide'); // remove popovers, they aren't updated on undo/redo and aren't cleared when clicking in the Glass buttons.
  400. }
  401. this.boardController.undoRedoController.redo();
  402. },
  403. _onWidgetMaximize: function _onWidgetMaximize() {
  404. this._buttonHideHelper.changeMode(this.glassContext, 'widgetMaximized');
  405. },
  406. _onWidgetRestore: function _onWidgetRestore() {
  407. this._buttonHideHelper.changeMode(this.glassContext, this.dashboardController.getMode());
  408. },
  409. /**
  410. * Toggles the event group mode.
  411. * @param {String} mode The optional mode to explicitly toggle to (default: 'authoring')
  412. */
  413. toggleEventGroupMode: function toggleEventGroupMode(mode) {
  414. var isEventGroupViewMode = this.isEventGroupViewMode;
  415. if (isEventGroupViewMode) {
  416. if (mode !== 'eventGroups') {
  417. // this._buttonHideHelper.changeMode(this.glassContext, mode || 'authoring');
  418. return this.turnOffEventGroupView(mode);
  419. } else {
  420. // we are already in eventGroup mode
  421. return Promise.resolve();
  422. }
  423. } else {
  424. this._buttonHideHelper.changeMode(this.glassContext, mode);
  425. return this.turnOnEventGroupView();
  426. }
  427. },
  428. /**
  429. * Toggles between authoring & consume mode
  430. */
  431. toggleMode: function toggleMode(options) {
  432. var _this4 = this;
  433. // clear data sources cache
  434. var dataSourcesSvc = this.dashboardApi.getFeature('dataSources.deprecated');
  435. if (dataSourcesSvc) {
  436. dataSourcesSvc.clearShapingCache();
  437. }
  438. return (this.isAuthoringMode ? this.turnOffEditMode() : this.turnOnEditMode()).then(function () {
  439. _this4._finishModeChange(options);
  440. });
  441. },
  442. _addChangeModeActionToUndoRedoStack: function _addChangeModeActionToUndoRedoStack(options) {
  443. if (!options || options.sender !== 'UndoRedoController') {
  444. this.boardController.undoRedoController.addToUndoRedoStack(new ChangeModeAction(this, {
  445. glassContext: this.glassContext
  446. }));
  447. }
  448. },
  449. _triggerWindowResize: function _triggerWindowResize() {
  450. try {
  451. $(window).resize();
  452. } catch (e) {
  453. console.error(e);
  454. }
  455. },
  456. _finishModeChange: function _finishModeChange() {
  457. var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  458. // We are switching to authoring mode.
  459. this.eventRouter.trigger('mode:change', {
  460. authoring: this.isAuthoringMode,
  461. isStoryMode: this.options.isStoryMode
  462. });
  463. // dont add if options.addToUndoRedoStack is false
  464. if (options.addToUndoRedoStack !== false) {
  465. // avoid infinite undo
  466. this._addChangeModeActionToUndoRedoStack(options);
  467. }
  468. // Resize to redraw the visualization because the canvas will change in size between consumption and edit mode
  469. // This also re-renders the grid
  470. this._triggerWindowResize();
  471. },
  472. isAuthorMode: function isAuthorMode() {
  473. return !!this.isAuthoringMode;
  474. },
  475. turnOnEditMode: function turnOnEditMode() {
  476. var _this5 = this;
  477. this.isAuthoringMode = true;
  478. return this.lifeCycleManager.invokeLifeCycleHandlers('pre:mode.edit').then(this.boardController.changeToAuthorMode.bind(this.boardController)).then(function () {
  479. // check for group view mode in authoring mode
  480. var isEventGroupViewMode = _this5.isEventGroupViewMode;
  481. if (isEventGroupViewMode) {
  482. return _this5.turnOnEventGroupView().then(_this5.applyModeToGlassPlugins.bind(_this5));
  483. } else {
  484. _this5.applyModeToGlassPlugins();
  485. }
  486. }).then(this.lifeCycleManager.invokeLifeCycleHandlers.bind(this.lifeCycleManager, 'post:mode.edit'));
  487. },
  488. turnOffEditMode: function turnOffEditMode() {
  489. this.isAuthoringMode = false;
  490. 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'));
  491. },
  492. turnOnEventGroupView: function turnOnEventGroupView() {
  493. var _this6 = this;
  494. this.isEventGroupViewMode = true;
  495. this.isAuthoringMode = true;
  496. this.boardController.undoRedoController.setMode('eventGroups');
  497. return this.boardController.changeToEventGroupMode().then(function () {
  498. _this6.applyModeToGlassPlugins();
  499. });
  500. },
  501. turnOffEventGroupView: function turnOffEventGroupView(mode) {
  502. var _this7 = this;
  503. this.isEventGroupViewMode = false;
  504. this.boardController.undoRedoController.setMode(null);
  505. var consumptionMode = mode === 'consumption';
  506. return (consumptionMode ? this.turnOffEditMode() : this.turnOnEditMode()).then(function () {
  507. return _this7._finishModeChange({
  508. addToUndoRedoStack: consumptionMode
  509. });
  510. });
  511. },
  512. applyModeToGlassPlugins: function applyModeToGlassPlugins() {
  513. if (this.isAuthoringMode) {
  514. if (this.isEventGroupViewMode) {
  515. this._buttonHideHelper.changeMode(this.glassContext, 'connections', this.options);
  516. } else {
  517. this._buttonHideHelper.changeMode(this.glassContext, 'authoring', this.options);
  518. }
  519. } else {
  520. this._buttonHideHelper.changeMode(this.glassContext, 'consume', this.options);
  521. }
  522. },
  523. _createServices: function _createServices() {
  524. this.servicesHelper = new ServicesHelper({
  525. glassContext: this.glassContext,
  526. eventRouter: this.eventRouter,
  527. appSettings: this.getContent(),
  528. logger: this.logger,
  529. dashboardAPI: this.dashboardApi,
  530. internalDashboardAPI: this.internalDashboardApi
  531. });
  532. var dashboardServices = this.servicesHelper.createServices();
  533. this.stringService = dashboardServices.stringService;
  534. this.lifeCycleManager = dashboardServices.lifeCycleManager;
  535. this.dnDManager = dashboardServices.dnDManager;
  536. this.services = dashboardServices.serviceRegistry;
  537. this.featureLoader = dashboardServices.featureLoader;
  538. this.dashboardController.setServices(this.services);
  539. this.dashboardController.setFeatureLoader(this.featureLoader);
  540. },
  541. getDeprecatedCanvas: function getDeprecatedCanvas() {
  542. if (this.boardController) {
  543. return this.boardController.getDeprecatedCanvas();
  544. }
  545. return null;
  546. },
  547. getDeprecatedCanvasWhenReady: function getDeprecatedCanvasWhenReady() {
  548. var _this8 = this;
  549. if (this._boardRendered) {
  550. return this._boardRendered.then(function () {
  551. return _this8.boardController.getDeprecatedCanvas();
  552. });
  553. }
  554. return Promise.reject(new Error('Unable to getCanvas . Board is not rendered yet.'));
  555. },
  556. /**
  557. * NOTE: This is overridden in other components such as Explore.
  558. *
  559. * @returns whether the user has the dashboard capability or not.
  560. */
  561. hasCapability: function hasCapability() {
  562. return ErrorUtils.hasCapability(this.glassContext);
  563. },
  564. _registerBootstrapFeatures: function _registerBootstrapFeatures() {
  565. var featureLoader = this.dashboardController.featureLoader;
  566. return this.dashboardFactory.getBootstrapFeatures().then(function (features) {
  567. Object.keys(features).forEach(function (featureName) {
  568. featureLoader.registerFeature(featureName, features[featureName], undefined, true);
  569. });
  570. });
  571. },
  572. render: function render() {
  573. var _this9 = this;
  574. BaseBoardView.inherited('render', this, arguments);
  575. this.$el.addClass('boardPageView');
  576. // Todo -- lifecycle_cleanup -- we shouldn't have to do this
  577. // This will create the content node .. if not done here, the toolbar will be at the bottom of the page
  578. // because it will be created before the content node
  579. this.getContentNode();
  580. if (!this.hasCapability()) {
  581. return Promise.reject({
  582. code: 'noCapability',
  583. message: this.stringService.get('noDashboardCapability')
  584. });
  585. }
  586. this.setupKeyboardHandlers();
  587. this.ajaxSvc = this.glassContext.getCoreSvc('.Ajax');
  588. var isAuthoringMode = this.isAuthoringMode;
  589. this.eventRouter.on('widget:maximize', this._onWidgetMaximize, this);
  590. this.eventRouter.on('widget:restore', this._onWidgetRestore, this);
  591. PerfUtils.createPerformanceMark({
  592. 'component': 'dashboard',
  593. 'name': 'LoadBoard',
  594. 'state': 'start'
  595. });
  596. this.boardModelFactory = this.boardModelFactory || new BoardModelFactory({
  597. dashboardAPI: this.dashboardApi,
  598. glassContext: this.glassContext,
  599. boardId: this.boardId,
  600. objectUrl: this.objectUrl,
  601. boardSpec: this.boardSpec,
  602. widgetRegistry: this.widgetRegistry,
  603. extensions: this.extensions,
  604. stringResources: this.dashboardApi.getDashboardCoreSvc('.StringResources'),
  605. eventRouter: this.eventRouter,
  606. logger: this.logger,
  607. createLayoutModel: this.createLayoutModel
  608. });
  609. 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) {
  610. _this9.boardModel = boardModelInfo.boardModel;
  611. _this9.boardSpec = boardModelInfo.boardSpec;
  612. _this9.boardId = boardModelInfo.boardId;
  613. }).then(this.glassContext.appController.findCollection.bind(this.glassContext.appController, 'com.ibm.bi.dashboard.templates')).then(function (templateItems) {
  614. return _this9.glassContext.appController.findCollection('com.ibm.bi.dashboard.contentTypes').then(function (contentTypes) {
  615. return _this9.servicesHelper.createRuntimeServices({
  616. dashboardApi: _this9.getDashboardApi(),
  617. collections: {
  618. templates: templateItems,
  619. contentTypes: contentTypes
  620. }
  621. });
  622. });
  623. }).then(this._registerDatasetExecution.bind(this)).then(function () {
  624. _this9.boardLoader = new _this9._boardLoaderClass({
  625. // beging - To be removed
  626. glassContext: _this9.glassContext,
  627. isAuthoringMode: isAuthoringMode,
  628. services: _this9.services,
  629. ajaxSvc: _this9.ajaxSvc,
  630. appSettings: _this9.getContent(),
  631. // end - To be removed
  632. dashboardApi: _this9.getDashboardApi(),
  633. eventRouter: _this9.eventRouter,
  634. setDirty: _this9.boardModel.isUpgraded,
  635. //TOOO maybe remove
  636. boardSpec: _this9.boardSpec,
  637. boardModel: _this9.boardModel,
  638. el: _this9.getContentNode()[0],
  639. $viewEl: _this9.$el,
  640. widgetRegistry: _this9.widgetRegistry,
  641. gatewayUrl: 'v1',
  642. cdnUrl: _this9.getCDNUrl(),
  643. featureLoader: _this9.featureLoader,
  644. extensions: _this9.extensions,
  645. dashboardController: _this9.dashboardController
  646. });
  647. return _this9.boardLoader.loadBoard();
  648. }).then(function () {
  649. return _this9.boardLoader.getCanvasController().then(function (canvasController) {
  650. _this9.boardController = canvasController;
  651. });
  652. }).then(function () {
  653. _this9.canUndo = false;
  654. _this9.canRedo = false;
  655. _this9.applyModeToGlassPlugins();
  656. _this9._registerUndoRedoListener(_this9.buttonIds.UNDO);
  657. _this9._registerUndoRedoListener(_this9.buttonIds.REDO);
  658. _this9.boardModel.on('change:name', _this9._onNameChange, _this9);
  659. _this9.boardModel.on('all', _this9._onAllModelEvents, _this9);
  660. _this9.dashboardApi.getCanvas().on('all', _this9._manageDirtyFlagForCanvasEvents.bind(_this9));
  661. _this9.boardController.onPageRenderComplete().then(function () {
  662. PerfUtils.createPerformanceMark({
  663. 'component': 'dashboard',
  664. 'name': 'LoadBoard',
  665. 'state': 'end'
  666. });
  667. });
  668. if (_this9.containerAppOptions && _this9.containerAppOptions.callbacks) {
  669. _this9.containerAppOptions.callbacks.resolve(_this9.getDashboardApi());
  670. }
  671. _this9.canvasExtensions = new CanvasExtensions({
  672. topNode: _this9.getCanvasExtensionsTopNode(),
  673. bottomNode: _this9.getCanvasExtensionsBottomNode(),
  674. splitterItems: _this9._splitterItems,
  675. canvasExtensions: _this9.extensions.getCanvasExtensions() || [],
  676. dashboardApi: _this9.getDashboardApi(),
  677. services: _this9.services,
  678. eventRouter: _this9.eventRouter,
  679. appSettings: _this9.getContent(),
  680. handlers: {
  681. getParentSize: function getParentSize() {
  682. return {
  683. height: _this9.$el.height(),
  684. width: _this9.$el.width()
  685. };
  686. },
  687. isAuthorMode: _this9.isAuthorMode.bind(_this9),
  688. getSplitterState: _this9.getSplitterState.bind(_this9)
  689. }
  690. });
  691. _this9.services.register('.CanvasExtensions', _this9.canvasExtensions);
  692. _this9.services.register('.CopyPasteController', _this9.boardController.copyPasteController);
  693. _this9.featureLoader.registerFeature('UndoRedo', new UndoRedo(_this9.boardController.undoRedoController));
  694. if (_this9.firstRender) {
  695. var dashboardState = _this9.dashboardApi.getFeature('DashboardState');
  696. dashboardState.setDirty(_this9.options.isDirtyOption);
  697. _this9.firstRender = false;
  698. }
  699. // The dashboard is not saved yet
  700. if (!_this9.getBoardId() || _this9.boardLoader.setDirty) {
  701. var _dashboardState = _this9.dashboardApi.getFeature('DashboardState');
  702. _dashboardState.setDirty(true);
  703. if (!_this9.getBoardId()) {
  704. _this9.title = _this9.stringService.get('defaultName');
  705. }
  706. _this9.boardLoader.setDirty = false;
  707. } else {
  708. _this9.title = _this9.getTitle();
  709. }
  710. _this9.dashboardApi.getFeature('DashboardState').setActive(true);
  711. _this9.logger.debug('Authored View created', _this9.options, _this9);
  712. _this9.onCanvasReady();
  713. // Waiting for pageReady causes issues with the glass not showing the page at the right time.
  714. return _this9.boardController.onPageReady().then(function () {
  715. if (_this9.sources) {
  716. var dataSourcesFeature = _this9.dashboardApi.getFeature('DataSources');
  717. _this9.sources.forEach(function (source) {
  718. return dataSourcesFeature.addDataSource(source, false, {
  719. silent: true,
  720. payloadData: {
  721. skipUndoRedo: true
  722. }
  723. });
  724. });
  725. // Once we've added the initial source to the dataSources feature, clear the property so we don't readd them during a relink
  726. _this9.options.sources = _this9.sources = null;
  727. }
  728. var printFeature = _this9.dashboardApi.getFeature('Print');
  729. var dashboardPrint = _this9.dashboardApi.getFeature('DashboardPrint');
  730. printFeature.registerContent('tab', dashboardPrint);
  731. printFeature.registerContent('page', dashboardPrint);
  732. }).then(function () {
  733. return _this9.canvasExtensions.render();
  734. }).then(function () {
  735. return undefined;
  736. });
  737. });
  738. return this._boardRendered.catch(function (e) {
  739. if (_this9.logger) {
  740. _this9.logger.log('An error occured while rendering the baseBoardView');
  741. _this9.logger.error(e);
  742. }
  743. throw e;
  744. });
  745. },
  746. getSplitterState: function getSplitterState() {
  747. // to be implemented by the concrete class
  748. },
  749. _onNameChange: function _onNameChange(event) {
  750. this.trigger('change:title', event);
  751. this.title = event.value;
  752. },
  753. _onAllModelEvents: function _onAllModelEvents(event) {
  754. // This is done so that the edit button does not trigger the dirty flag
  755. var isUndoRedo = event && (event.stack === 'undo' || event.stack === 'redo');
  756. var isRuntimeChange = event && event.data && event.data.runtimeOnly;
  757. var isEventGroupsChange = !this.isAuthoringMode && event && event.eventName && event.eventName === 'change:eventGroups';
  758. var skipUndoRedo = event && event.data && event.data.skipUndoRedo;
  759. var dashboardState = this.dashboardApi.getFeature('DashboardState');
  760. if (!isRuntimeChange && !isEventGroupsChange) {
  761. var _dashboardState$getUi = dashboardState.getUiState(),
  762. dirty = _dashboardState$getUi.dirty;
  763. if (!isUndoRedo && !dirty && !skipUndoRedo) {
  764. dashboardState.setDirty(true);
  765. } else if (isUndoRedo) {
  766. var isUndoRedoDirty = this.boardController.undoRedoController.isDirty();
  767. if (dirty && !isUndoRedoDirty) {
  768. dashboardState.setDirty(false);
  769. } else if (!dirty && isUndoRedoDirty) {
  770. dashboardState.setDirty(true);
  771. }
  772. }
  773. }
  774. },
  775. _manageDirtyFlagForCanvasEvents: function _manageDirtyFlagForCanvasEvents(event) {
  776. if (event.info && event.info.supportsUndoRedo && event.context && event.context.undoRedo !== true) {
  777. if (!this.isDirty()) {
  778. var dashboardState = this.dashboardApi.getFeature('DashboardState');
  779. dashboardState.setDirty(true);
  780. }
  781. }
  782. },
  783. getCanvasExtensionsTopNode: function getCanvasExtensionsTopNode() {
  784. if (!this.$top) {
  785. var $parentNode = this.getContentNode();
  786. var $top = $('.canvasExtensionTop', $parentNode);
  787. if ($top.length === 0) {
  788. $top = $('<div class="canvasExtensionTop"></div>');
  789. $parentNode.prepend($top);
  790. }
  791. this.$top = $top;
  792. }
  793. return this.$top;
  794. },
  795. getCanvasExtensionsBottomNode: function getCanvasExtensionsBottomNode() {
  796. if (!this.$bottom) {
  797. var $bottom = $('.canvasExtensionBottom', this.el);
  798. if ($bottom.length === 0) {
  799. $bottom = $('<div class="canvasExtensionBottom"></div>');
  800. this.$el.append($bottom);
  801. }
  802. this.$bottom = $bottom;
  803. }
  804. return this.$bottom;
  805. },
  806. getDashboardApi: function getDashboardApi() {
  807. return this.dashboardApi;
  808. },
  809. onCanvasReady: function onCanvasReady() {
  810. // to be implemented by the concrete class
  811. },
  812. _registerUndoRedoListener: function _registerUndoRedoListener(id) {
  813. var plugin = this.glassContext.appController.findPlugin(id);
  814. if (plugin) {
  815. this.boardController.undoRedoController.addListener(id, plugin);
  816. }
  817. },
  818. remove: function remove() {
  819. BaseBoardView.inherited('remove', this, arguments);
  820. this.teardownKeyboardHandlers();
  821. this.eventRouter.off('widget:maximize', this._onWidgetMaximize, this);
  822. this.eventRouter.off('widget:restore', this._onWidgetRestore, this);
  823. // Destroy the view before the services and APIs
  824. try {
  825. this.boardLoader.destroyViews();
  826. } catch (e) {
  827. if (this.logger) {
  828. this.logger.error(e);
  829. }
  830. }
  831. try {
  832. this.boardLoader.destroyCanvasAPI();
  833. } catch (e) {
  834. if (this.logger) {
  835. this.logger.error(e);
  836. }
  837. }
  838. if (this.datasetRefreshEventHandler) {
  839. this.datasetRefreshEventHandler.remove();
  840. }
  841. this.boardLoader = null;
  842. this.dashboardController.destroy();
  843. MemUtil.destroy(this);
  844. },
  845. getTitle: function getTitle() {
  846. return this.boardModel && this.boardModel.get('name') || '';
  847. },
  848. setTitle: function setTitle(title) {
  849. this.title = title;
  850. this.trigger('change:title', { value: title });
  851. },
  852. openDatasetpane: function openDatasetpane() {
  853. var plugin = this.glassContext.appController.findPlugin('com.ibm.bi.dashboard.dataSources.sourcesBtn');
  854. if (plugin && !plugin.isPressed()) {
  855. plugin.$el.trigger('click', {
  856. expandFirstEntry: true
  857. });
  858. }
  859. },
  860. setupKeyboardHandlers: function setupKeyboardHandlers() {
  861. if (!this._keyDownBind) {
  862. this._keyDownBind = this._processKeyDown.bind(this);
  863. this._keyUpBind = this._processKeyUp.bind(this);
  864. this._windowBlur = this._processKeyUp.bind(this);
  865. $(document).on('keydown', this._keyDownBind);
  866. $(document).on('keyup', this._keyUpBind);
  867. $(window).on('blur', this._windowBlur);
  868. }
  869. },
  870. teardownKeyboardHandlers: function teardownKeyboardHandlers() {
  871. $(document).off('keydown', this._keyDownBind);
  872. $(document).off('keyup', this._keyUpBind);
  873. $(window).off('blur', this._windowBlur);
  874. this._keyDownBind = null;
  875. this._keyUpBind = null;
  876. this._windowBlur = null;
  877. },
  878. _onDatasetRefresh: function _onDatasetRefresh(payload) {
  879. this._modifiedDataset.push(payload.id);
  880. },
  881. /**
  882. * Called by the glass when the view is deactivated (e.g. switching to a different view)
  883. */
  884. deactivate: function deactivate() {
  885. // CADBC-892 dashboard state may not be available in hardnav scenarios
  886. var dashboardState = this.dashboardApi.getFeature('DashboardState');
  887. dashboardState && dashboardState.setActive(false);
  888. if (this.boardLoader) {
  889. this.boardLoader.deactivate();
  890. }
  891. this.teardownKeyboardHandlers();
  892. if (this.canvasExtensions) {
  893. this.canvasExtensionState = this.canvasExtensions.getState();
  894. }
  895. this.dashboardApi.triggerDashboardEvent('dashboard:deactivate');
  896. return BaseBoardView.inherited('deactivate', this, arguments);
  897. },
  898. /**
  899. * Called by the glass just before the view is being shown
  900. * @param {Object} content - content object from Glass. { perspective: xx, id: xx, content:xx }
  901. */
  902. activate: function activate(content) {
  903. var _this10 = this;
  904. this.dashboardApi.getFeature('DashboardState').setActive(true);
  905. if (!this.boardLoader) {
  906. this.setupKeyboardHandlers();
  907. return BaseBoardView.inherited('activate', this, [content]);
  908. }
  909. var always = function always() {
  910. var promise = _this10.boardLoader.activate();
  911. if (!promise || !promise.then) {
  912. promise = Promise.resolve();
  913. }
  914. return promise.then(function () {
  915. _this10.dashboardApi.triggerDashboardEvent('dashboard:show');
  916. return _this10.setupKeyboardHandlers();
  917. });
  918. };
  919. return BaseBoardView.inherited('activate', this, [content]).then(function () {
  920. // Before activating the dashboard view, check if any data source is changed, in which case we need to
  921. // reload metadata and re-execute widgets
  922. var result = void 0;
  923. if (_this10._modifiedDataset.length > 0) {
  924. result = _this10._refreshDataSource().then(function () {
  925. _this10._modifiedDataset = [];
  926. });
  927. } else {
  928. result = Promise.resolve();
  929. }
  930. _this10.canvasExtensions.setState(_this10.canvasExtensionState);
  931. return result;
  932. }).then(always).catch(always);
  933. },
  934. /**
  935. * Refresh datasources that are referenced in dashboard and were modified
  936. */
  937. _refreshDataSource: function _refreshDataSource() {
  938. var _this11 = this;
  939. var dataSourcesSvc = this.dashboardApi.getFeature('dataSources.deprecated');
  940. var sourcesCollection = dataSourcesSvc.getSourcesCollection();
  941. var aDataSources = sourcesCollection.getSources();
  942. var datasourcePromises = [];
  943. _.each(aDataSources, function (source) {
  944. if (_this11._modifiedDataset.indexOf(source.getAssetId()) >= 0) {
  945. datasourcePromises.push(source.reloadMetadata());
  946. }
  947. });
  948. return Promise.all(datasourcePromises);
  949. },
  950. _removeForReload: function _removeForReload() {
  951. this.teardownKeyboardHandlers();
  952. this.boardLoader.destroyViews();
  953. this.boardLoader.destroyCanvasAPI();
  954. this.boardLoader = null;
  955. if (this.datasetRefreshEventHandler) {
  956. this.datasetRefreshEventHandler.remove();
  957. }
  958. if (this.services) {
  959. this.services.destroy();
  960. }
  961. this.dashboardApi.destroy();
  962. this.dashboardApi = null;
  963. this.boardModel = null;
  964. this.$el.empty();
  965. this.$bottom = null;
  966. this.$top = null;
  967. },
  968. reloadFromJSONSpec: function reloadFromJSONSpec(JSONSpec) {
  969. var _this12 = this;
  970. var extOptions = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  971. var isAuthoringMode = extOptions.isAuthoringMode,
  972. _extOptions$clearDirt = extOptions.clearDirtyFlag,
  973. clearDirtyFlag = _extOptions$clearDirt === undefined ? false : _extOptions$clearDirt;
  974. this.dashboardController.toggleProperties(false);
  975. this._removeForReload();
  976. var options = this.options;
  977. options.boardSpec = JSONSpec;
  978. options.isAuthoringMode = typeof isAuthoringMode !== 'undefined' ? isAuthoringMode : this.isAuthoringMode;
  979. if (extOptions.boardId) {
  980. options.boardId = extOptions.boardId; //Reload from JSON Spec with passed in board ID
  981. }
  982. this.initialize(options);
  983. return this.render().then(function () {
  984. if (_this12.reloadables) {
  985. Object.values(_this12.reloadables).filter(function (reloadable) {
  986. return typeof reloadable.reload === 'function';
  987. }).forEach(function (reloadable) {
  988. return reloadable.reload();
  989. });
  990. }
  991. _this12.setFocus();
  992. // Reset the flag, otherwise the current flag state propagates
  993. var dashboardState = _this12.dashboardApi.getFeature('DashboardState');
  994. if (clearDirtyFlag && !_this12.boardLoader.setDirty) {
  995. dashboardState.setDirty(false);
  996. } else {
  997. dashboardState.setDirty(true);
  998. }
  999. return _this12.dashboardApi;
  1000. });
  1001. },
  1002. /**
  1003. * Temp until glass provides us with an official API to know when we're visible.
  1004. * @return {[type]} [description]
  1005. */
  1006. setFocus: function setFocus() {
  1007. var _this13 = this;
  1008. BaseBoardView.inherited('setFocus', this, arguments);
  1009. this.lifeCycleManager.invokeLifeCycleHandlers('pre:dashboard.focus').then(this.boardLoader.getCanvasController.bind(this.boardLoader)).then(function (canvasController) {
  1010. return canvasController.layoutController.getTopLayoutViewWhenReady();
  1011. }).then(function (layoutView) {
  1012. // Call onShow which will do nothing if they had already rendered or make them render correctly
  1013. if (layoutView) {
  1014. layoutView.onShow();
  1015. }
  1016. return _this13.lifeCycleManager.invokeLifeCycleHandlers('post:dashboard.focus');
  1017. }).catch(function (err) {
  1018. _this13.logger.error('Failed to setFocus', err, _this13);
  1019. });
  1020. }
  1021. });
  1022. return BaseBoardView;
  1023. });
  1024. //# sourceMappingURL=BaseBoardView.js.map