WidgetLoader.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687
  1. 'use strict';
  2. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  3. /**
  4. * Licensed Materials - Property of IBM
  5. * IBM Cognos Products: BI Cloud (C) Copyright IBM Corp. 2014, 2021
  6. * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
  7. */
  8. define(['../../lib/@waca/core-client/js/core-client/utils/PerfUtils', '../../lib/@waca/core-client/js/core-client/utils/Deferred', 'underscore', 'jquery', '../widgets/PropertiesUtil', '../widgets/error/ErrorView', '../EventChannelRouterHelper', '../layout/LayoutHelper'], function (PerfUtils, Deferred, _, $, PropertiesUtil, ErrorView, ChannelRouterHelper, LayoutHelper) {
  9. var ERRORCODE = {
  10. 'LOAD': 20,
  11. 'UNLOAD': 21,
  12. 'WIDGET_ALREADY_LOADED': 22,
  13. 'RENDER': 23,
  14. 'UNLOADING_A_LOADING_WIDGET': 24
  15. };
  16. var _trackingId = 0;
  17. var WidgetLoader = function () {
  18. function WidgetLoader(params) {
  19. _classCallCheck(this, WidgetLoader);
  20. this.ERROR = ERRORCODE;
  21. /**
  22. * Hash to keep track of the widgets that are in the process of being loaded
  23. *
  24. * @type {Object}
  25. */
  26. this.loadingWidgets = {};
  27. this.failedWidgets = {};
  28. this.eventGroups = null;
  29. this.boardModel = null;
  30. this._initializeParams(params);
  31. //TODO use a namespace prefix/suffix for dom nodes for widgets ??
  32. this.registerEvents();
  33. }
  34. WidgetLoader.prototype._initializeParams = function _initializeParams(params) {
  35. // Used to keep track of the widget API response promise (i.e. getWidget(id))
  36. this.widgetReadyPromises = {};
  37. // Used to tack request for the widget API before the widget is loaded
  38. this.pendingWidgetResolveRequests = {};
  39. this.services = params.services;
  40. this.appSettings = params.appSettings;
  41. this.contentFeatureLoader = params.contentFeatureLoader;
  42. this.dashboardApi = params.dashboardApi;
  43. this.canvas = params.canvas;
  44. this.eventRouter = params.eventRouter;
  45. this.logger = this.dashboardApi.getGlassCoreSvc('.Logger');
  46. this.colorsService = this.dashboardApi.getFeature('Colors');
  47. this.widgetRegistry = params.widgetRegistry;
  48. this.loadedWidgets = params.loadedWidgets;
  49. if (params.boardModel) {
  50. this.eventGroups = params.boardModel.eventGroups;
  51. this.boardModel = params.boardModel;
  52. this.topLayoutModel = params.boardModel.layout;
  53. }
  54. this.channelRouterHelper = new ChannelRouterHelper({
  55. eventRouter: this.eventRouter
  56. });
  57. };
  58. WidgetLoader.prototype.registerEvents = function registerEvents() {
  59. this.eventGroups.on('change:id', this.onEventGroupIdChange, this);
  60. this.eventGroups.on('change:widgetIds', this.onWidgetIdsChange, this);
  61. if (this.boardModel) {
  62. this.boardModel.on('change:eventGroups', this.onEventGroupsChange, this);
  63. }
  64. };
  65. WidgetLoader.prototype.onEventGroupIdChange = function onEventGroupIdChange(event) {
  66. var options = {
  67. sender: event.sender,
  68. payloadData: {
  69. undoRedoTransactionId: event.data.undoRedoTransactionId,
  70. transactionToken: event.data.transactionToken
  71. }
  72. };
  73. var eventRouter = this.channelRouterHelper.getChannelRouter(event.model.id);
  74. _.each(event.model.widgetIds, function (widgetId) {
  75. if (this.loadedWidgets[widgetId]) {
  76. this.loadedWidgets[widgetId].setEventRouter(eventRouter, options);
  77. }
  78. }.bind(this));
  79. };
  80. WidgetLoader.prototype.onWidgetIdsChange = function onWidgetIdsChange(event) {
  81. var options = {
  82. sender: event.sender,
  83. payloadData: {
  84. undoRedoTransactionId: event.data.undoRedoTransactionId,
  85. transactionToken: event.data.transactionToken
  86. }
  87. };
  88. // process the widgets that are added or removed first
  89. var changedWidgets, newEventRouter;
  90. if (event.prevValue.length > event.value.length) {
  91. // widgets are removed from group
  92. changedWidgets = _.difference(event.prevValue, event.value);
  93. newEventRouter = null;
  94. } else {
  95. // widgets are added to group
  96. changedWidgets = _.difference(event.value, event.prevValue);
  97. newEventRouter = this.channelRouterHelper.getChannelRouter(event.model.id);
  98. }
  99. _.each(changedWidgets, function (widgetId) {
  100. if (this.loadedWidgets[widgetId]) {
  101. this.loadedWidgets[widgetId].setEventRouter(newEventRouter, options);
  102. }
  103. }.bind(this));
  104. // process the existing widgets
  105. var remainingWidgets = this.dashboardApi.getFeature('EventGroups').getContentIdList(event.model.id);
  106. var eventRouter = this.channelRouterHelper.getChannelRouter(event.model.id);
  107. _.each(remainingWidgets, function (widgetId) {
  108. //check here to see if the widget(s) in the event groups are not deleted
  109. if (this.loadedWidgets[widgetId] && this.canvas.getContent(widgetId)) {
  110. //TODO: not sure why we have to set event router here. Need to revisit this.
  111. this.loadedWidgets[widgetId].setEventRouter(eventRouter, options);
  112. }
  113. }.bind(this));
  114. };
  115. WidgetLoader.prototype.onEventGroupsChange = function onEventGroupsChange() {
  116. var _this = this;
  117. var event = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  118. if (event.name === 'add') {
  119. var value = event.value || {};
  120. if (value.widgetIds instanceof Array) {
  121. var eventRouter = this.channelRouterHelper.getChannelRouter(event.value.id);
  122. value.widgetIds.forEach(function (id) {
  123. if (_this.loadedWidgets[id]) {
  124. _this.loadedWidgets[id].setEventRouter(eventRouter, { silent: true });
  125. }
  126. });
  127. }
  128. }
  129. };
  130. WidgetLoader.prototype.registerWidgetToEventGroup = function registerWidgetToEventGroup(widgetId, transactionId) {
  131. var widget = this.getWidget(widgetId);
  132. if (widget) {
  133. widget.registerEventGroup(transactionId);
  134. }
  135. };
  136. WidgetLoader.prototype.unregisterWidgetFromEventGroup = function unregisterWidgetFromEventGroup(widgetId, transactionId, options) {
  137. this.eventGroups.removeWidgetsFromGroup([widgetId], {
  138. payloadData: {
  139. undoRedoTransactionId: transactionId,
  140. skipUndoRedo: options.data && options.data.skipUndoRedo
  141. },
  142. sender: options.sender
  143. });
  144. };
  145. WidgetLoader.prototype.loadWidget = function loadWidget(widgetId, widgetSpec, domNode, cbOnLoad, cbOnError, transactionId) {
  146. var trackingId = _trackingId;
  147. _trackingId++;
  148. this.logger.debug('Widget load start (' + trackingId + ')', widgetId, widgetSpec, domNode);
  149. return this._invokeLifeCycleHandlers('pre:widget.load', {
  150. modelId: widgetId,
  151. widgetSpec: widgetSpec
  152. }).then(function () {
  153. var result;
  154. if (!this.loadedWidgets[widgetId] && this.loadingWidgets[widgetId]) {
  155. //If loadWidget is called while already loading a widget,
  156. //save callbacks for execution when the widget is loaded (and exit).
  157. this.loadingWidgets[widgetId].cbOnLoad.push(cbOnLoad);
  158. this.loadingWidgets[widgetId].cbOnError.push(cbOnError);
  159. } else if (widgetId && widgetSpec) {
  160. var widget = this.loadedWidgets[widgetId];
  161. if (!widget && !this.loadingWidgets[widgetId]) {
  162. try {
  163. result = this._loadWidget(widgetId, widgetSpec, domNode, cbOnLoad, cbOnError, trackingId, transactionId);
  164. } catch (e) {
  165. this._onError(cbOnError, ERRORCODE.LOAD, widgetId, e, trackingId);
  166. result = Promise.reject(e);
  167. }
  168. } else {
  169. this._onError(cbOnError, ERRORCODE.WIDGET_ALREADY_LOADED, widgetId, null, trackingId);
  170. result = Promise.reject(new Error('Widget already loaded'));
  171. }
  172. }
  173. return result;
  174. }.bind(this)).catch(this.logger.error.bind(this.logger));
  175. };
  176. WidgetLoader.prototype.unLoadWidget = function unLoadWidget(widgetId, cbOnError) {
  177. var payload = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
  178. if (widgetId) {
  179. var transactionId = payload.data ? payload.data.undoRedoTransactionId : undefined;
  180. this.logger.debug('Unloading widget', widgetId);
  181. if (this.loadingWidgets[widgetId]) {
  182. // Shouldn't unload a widget until it's loaded fully
  183. this._onError(cbOnError, ERRORCODE.UNLOADING_A_LOADING_WIDGET, widgetId);
  184. return;
  185. }
  186. var widget = this.loadedWidgets[widgetId];
  187. this.unregisterWidgetFromEventGroup(widgetId, transactionId, payload);
  188. if (widget && widget.destroy) {
  189. try {
  190. widget.destroy(transactionId);
  191. } catch (e) {
  192. this._onError(cbOnError, ERRORCODE.UNLOAD, widgetId, e);
  193. } finally {
  194. this.loadedWidgets[widgetId] = null;
  195. this.widgetReadyPromises[widgetId] = null;
  196. }
  197. }
  198. var el = document.getElementById(widgetId);
  199. if (el && el.getElementsByClassName('widgetContentWrapper').length) {
  200. el.getElementsByClassName('widgetContentWrapper')[0].innerHTML = '';
  201. }
  202. }
  203. };
  204. /**
  205. * Set the state of a widget to 'loading' by adding an entry into the loadingWidgets map by widgetId.
  206. * This entry is also responsible for ensuring that, when multiple calls to loadWidget on the same id
  207. * occur, callbacks are not lost...they will be executed postLoad.
  208. * @param widgetId The id of the widget to set into loading state.
  209. */
  210. WidgetLoader.prototype._setLoading = function _setLoading(widgetId) {
  211. this.loadingWidgets[widgetId] = {
  212. cbOnLoad: [],
  213. cbOnError: []
  214. };
  215. };
  216. WidgetLoader.prototype._loadWidget = function _loadWidget(widgetId, widgetSpec, domNode, cbOnLoad, cbOnError, trackingId, transactionId) {
  217. cbOnLoad = cbOnLoad || function () {};
  218. if (!this.loadingWidgets[widgetId]) {
  219. this._setLoading(widgetId);
  220. }
  221. var widgetModule = this.widgetRegistry[widgetSpec.type] && this.widgetRegistry[widgetSpec.type].widget;
  222. if (!widgetModule) {
  223. try {
  224. cbOnLoad({
  225. 'id': widgetId,
  226. 'widget': null
  227. });
  228. } catch (error) {
  229. cbOnError(error);
  230. }
  231. return;
  232. }
  233. var dfd = new Deferred();
  234. var onLoadWidget = _.partial(this._onLoadWidget.bind(this), widgetId, widgetSpec, widgetModule, domNode, cbOnLoad, cbOnError, trackingId, transactionId);
  235. require([widgetModule], function (Widget) {
  236. return onLoadWidget(Widget).then(dfd.resolve.bind(dfd), dfd.reject.bind(dfd));
  237. }, function (e) {
  238. // If the require fails, errback should be called
  239. // TODO: This doesn't get called by DOJO, but it will once we switch to require.js
  240. dfd.reject(e);
  241. delete this.loadingWidgets[widgetId];
  242. this._onError(cbOnError, ERRORCODE.LOAD, widgetId, e, trackingId);
  243. }.bind(this));
  244. return dfd.promise;
  245. };
  246. WidgetLoader.prototype._onLoadWidget = function _onLoadWidget(widgetId, widgetSpec, widgetModule, domNode, cbOnLoad, cbOnError, trackingId, transactionId, Widget) {
  247. var _this2 = this;
  248. var nParent = void 0;
  249. var nWidgetContainer = void 0;
  250. var nParentWidgetContainer = void 0;
  251. var result = void 0;
  252. try {
  253. if (!this.loadingWidgets[widgetId]) {
  254. // This widget isn't in a loading state, we shouldn't continue loading it.
  255. // This can happen if the WidgetLoader is destroyed and there are pending loads
  256. return Promise.reject(new Error('Widget \'' + widgetId + '\' is not in a loading state. Unable to load.'));
  257. }
  258. PerfUtils.createPerformanceMark({
  259. 'component': 'dashboard',
  260. 'name': 'loadWidget',
  261. 'state': 'start'
  262. });
  263. this.logger.debug('Widget load finish (' + trackingId + ')');
  264. result = this._invokeLifeCycleHandlers('post:widget.load', {
  265. modelId: widgetId,
  266. widgetSpec: widgetSpec
  267. }).then(function () {
  268. return _this2.contentFeatureLoader.whenContentReady(widgetId);
  269. }).then(function () {
  270. var _this3 = this;
  271. // @todo investigate
  272. nParentWidgetContainer = nParent = document.getElementById(widgetId) || domNode;
  273. if (nParent && nParent.getElementsByClassName('widgetContentWrapper').length) {
  274. nParent = nParent.getElementsByClassName('widgetContentWrapper')[0];
  275. }
  276. if (!nParent) {
  277. this.logger.error('Could not find layout node for widget with id: "' + widgetId + '"');
  278. return;
  279. } else {
  280. var createContentNode = true;
  281. var widgetRegistry = this.widgetRegistry[widgetSpec.type];
  282. if (widgetRegistry) {
  283. createContentNode = this.widgetRegistry[widgetSpec.type].createContentNode;
  284. }
  285. if (createContentNode) {
  286. nWidgetContainer = document.createElement('div');
  287. // Apply widget type specific css
  288. nWidgetContainer.className = widgetSpec.type + 'WidgetContent widgetContent';
  289. nWidgetContainer.id = widgetId + '_';
  290. $(nParent).append(nWidgetContainer);
  291. } else {
  292. nWidgetContainer = nParent;
  293. }
  294. //TODO use namespace for widget dom node?
  295. var options = {
  296. // TODO - to be removed after refactoring
  297. services: this.services,
  298. appSettings: this.appSettings,
  299. // TODO END - to be removed after refactoring
  300. dashboardApi: this.dashboardApi,
  301. id: widgetId,
  302. canvas: this.canvas,
  303. content: this.canvas && this.canvas.getContent(widgetId),
  304. initialConfigJSON: widgetSpec,
  305. el: nWidgetContainer,
  306. widgetContainer: nParentWidgetContainer,
  307. registry: widgetRegistry,
  308. eventRouter: this.eventRouter,
  309. errorView: new ErrorView(),
  310. propertiesUtil: PropertiesUtil,
  311. contentFeatureLoader: this.contentFeatureLoader
  312. };
  313. options = this._addScopedEventInfo(options, widgetSpec, transactionId);
  314. options = this._addConfigInfo(options);
  315. //The Endor LiveWidget expects the widget model in its constructor rather than waiting for onContainerReady.
  316. var widgetModelInstance = this.boardModel && this.boardModel.widgetInstances && this.boardModel.widgetInstances[widgetId];
  317. if (widgetModelInstance) {
  318. options.widgetModel = widgetModelInstance;
  319. } else {
  320. options.widgetSpec = widgetSpec;
  321. }
  322. return this._createWidget(options, cbOnLoad, cbOnError, Widget, trackingId, transactionId).then(function () {
  323. PerfUtils.createPerformanceMark({
  324. 'component': 'dashboard',
  325. 'name': 'loadWidget',
  326. 'state': 'end'
  327. });
  328. }, function (error) {
  329. //prevent widgets which failed to load to be added to an event group
  330. _this3.failedWidgets[widgetId].setEventRouter(null);
  331. throw error;
  332. });
  333. }
  334. }.bind(this)).catch(this.logger.error.bind(this.logger));
  335. } catch (e) {
  336. // Make sure the widget is no longer in loading state even if we get an error
  337. delete this.loadingWidgets[widgetId];
  338. if (nParent) {
  339. nParent.removeChild(nWidgetContainer);
  340. }
  341. this._onError(cbOnError, ERRORCODE.LOAD, widgetId, e, trackingId);
  342. result = Promise.reject(e);
  343. }
  344. return result;
  345. };
  346. WidgetLoader.prototype._createWidget = function _createWidget(options, cbOnLoad, cbOnError, Widget, trackingId, transactionId) {
  347. var _this4 = this;
  348. this.logger.debug('Widget create start (' + trackingId + ')');
  349. var widgetId = options.id;
  350. var widget = new Widget(options);
  351. var result = void 0;
  352. var renderWidget = function renderWidget() {
  353. _this4.loadedWidgets[widgetId] = widget;
  354. return _this4._renderWidget(widget, widgetId, cbOnLoad, cbOnError, trackingId, transactionId);
  355. };
  356. var initialize = function initialize() {
  357. var stateApi = options.content.getFeature('state.internal');
  358. stateApi.setStatus(stateApi.STATUS.INITIALIZED);
  359. };
  360. if (widget.initialize) {
  361. var initializeOptions = {};
  362. if (transactionId) {
  363. initializeOptions.transactionId = transactionId;
  364. }
  365. result = widget.initialize(initializeOptions).then(function () {
  366. initialize();
  367. return renderWidget();
  368. }, function (error) {
  369. //widget failed to load and is added to failedWidgets object, so that it does not get assigned an event group
  370. _this4.failedWidgets[widgetId] = widget;
  371. if (error && error.uiErrorMessage && widget.showError) {
  372. widget.showError(error.uiErrorMessage);
  373. }
  374. throw error;
  375. });
  376. } else {
  377. initialize();
  378. result = renderWidget();
  379. }
  380. return result;
  381. };
  382. WidgetLoader.prototype._renderWidget = function _renderWidget(widget, widgetId, cbOnLoad, cbOnError, trackingId, transactionId) {
  383. var _this5 = this;
  384. this.logger.debug('Widget render start (' + trackingId + ')');
  385. var result = void 0;
  386. // If we have a pending request for the widget, we resolve now.
  387. if (this.pendingWidgetResolveRequests[widgetId]) {
  388. this.pendingWidgetResolveRequests[widgetId](widget);
  389. delete this.pendingWidgetResolveRequests[widgetId];
  390. }
  391. //If any callbacks were processed during load, we need to call them now....
  392. var cbOnLoadCallbacks = this.loadingWidgets[widgetId].cbOnLoad;
  393. var cbOnErrorCallbacks = this.loadingWidgets[widgetId].cbOnError;
  394. delete this.loadingWidgets[widgetId];
  395. try {
  396. if (!widget.content || widget.content.getType() !== 'widget.filter') {
  397. this.registerWidgetToEventGroup(widgetId, transactionId);
  398. }
  399. if (widget.render) {
  400. if (widget.createVisualizationCompleteDeferred) {
  401. result = widget.createVisualizationCompleteDeferred.then(function () {
  402. return Promise.resolve() // protection against $ deferred. TODO: please remove after all widgets return a proper promise
  403. .then(widget.render.bind(widget)).catch(function (error) {
  404. // render may reject when parent is not visible. ignore, but log the error.
  405. _this5.logger.error('[WidgetLoader] widget render error', error);
  406. });
  407. });
  408. } else {
  409. result = Promise.resolve(widget.render()).catch(function (error) {
  410. // render may reject when parent is not visible. ignore but log the error.
  411. _this5.logger.error('[WidgetLoader] widget render error', error);
  412. _.each(cbOnErrorCallbacks, function (cb) {
  413. _this5._onError(cb, ERRORCODE.RENDER, widgetId, error, trackingId);
  414. });
  415. _this5._onError(cbOnError, ERRORCODE.RENDER, widgetId, error, trackingId);
  416. throw error;
  417. });
  418. }
  419. } else {
  420. result = Promise.resolve();
  421. }
  422. _.each(cbOnLoadCallbacks, function (cb) {
  423. cb({
  424. 'id': widgetId,
  425. 'widget': widget
  426. });
  427. });
  428. cbOnLoad({
  429. 'id': widgetId,
  430. 'widget': widget
  431. });
  432. this.logger.debug('Widget render finish (' + trackingId + ')');
  433. } catch (e) {
  434. _.each(cbOnErrorCallbacks, function (cb) {
  435. _this5._onError(cb, ERRORCODE.RENDER, widgetId, e, trackingId);
  436. });
  437. this._onError(cbOnError, ERRORCODE.RENDER, widgetId, e, trackingId);
  438. result = Promise.reject(new Error(''));
  439. }
  440. return result;
  441. };
  442. WidgetLoader.prototype._getChannelId = function _getChannelId(containerPageId, widgetId, transactionId) {
  443. var eventGroup;
  444. if (this.eventGroups) {
  445. eventGroup = this.eventGroups.findGroup(widgetId);
  446. if (!eventGroup) {
  447. eventGroup = this.eventGroups.getDefaultGroup(containerPageId, {
  448. payloadData: {
  449. undoRedoTransactionId: transactionId
  450. }
  451. });
  452. }
  453. }
  454. // use the pageId if eventGroups are not defined
  455. return eventGroup ? eventGroup.id : containerPageId;
  456. };
  457. /**
  458. * If channelRouterHelper is available, add containerPageId and update eventRouter
  459. *
  460. * @returns updated options
  461. */
  462. WidgetLoader.prototype._addScopedEventInfo = function _addScopedEventInfo(options, widgetSpec, transactionId) {
  463. if (this.channelRouterHelper) {
  464. // Custom widget may declare filter support from it's definition
  465. var filterSupport = this.widgetRegistry[widgetSpec.type].filter;
  466. // Loading board case, containerPageId is passed in widgetSpec.
  467. // If not, it is onDrop case. Use current container page id.
  468. options.containerPageId = LayoutHelper.getContainerPageId(this.topLayoutModel, widgetSpec.id);
  469. var supported = ['data', 'live'];
  470. if (supported.indexOf(widgetSpec.type) !== -1 || filterSupport) {
  471. var channelId = this._getChannelId(options.containerPageId, widgetSpec.id, transactionId);
  472. options.eventRouter = this.channelRouterHelper.getChannelRouter(channelId);
  473. }
  474. }
  475. return options;
  476. };
  477. /**
  478. * Updates the options for the widget with any app/view level constraints
  479. * @param {object} options
  480. */
  481. WidgetLoader.prototype._addConfigInfo = function _addConfigInfo() {
  482. var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  483. options.focusModeDisabled = !!this.dashboardApi.getConfiguration('focusModeDisabled');
  484. options.interactivitySettings = this.dashboardApi.getConfiguration('interactions');
  485. return options;
  486. };
  487. WidgetLoader.prototype.cleanup = function cleanup() {
  488. var map = this.loadedWidgets;
  489. for (var widgetId in map) {
  490. if (map.hasOwnProperty(widgetId)) {
  491. var widget = map[widgetId];
  492. if (widget && widget.destroy) {
  493. widget.destroy();
  494. this.loadedWidgets[widgetId] = null;
  495. this.widgetReadyPromises[widgetId] = null;
  496. }
  497. }
  498. }
  499. this.loadingWidgets = {};
  500. if (this.eventGroups) {
  501. this.eventGroups.off('change:id', this.onEventGroupIdChange, this);
  502. this.eventGroups.off('change:widgetIds', this.onWidgetIdsChange, this);
  503. }
  504. if (this.boardModel) {
  505. this.boardModel.off('change:eventGroups', this.onEventGroupsChange, this);
  506. }
  507. if (this.channelRouterHelper) {
  508. this.channelRouterHelper.destroy();
  509. delete this.topLayoutModel;
  510. }
  511. };
  512. WidgetLoader.prototype.isLoaded = function isLoaded(id) {
  513. return id ? this.loadedWidgets[id] !== null && typeof this.loadedWidgets[id] !== 'undefined' : false;
  514. };
  515. WidgetLoader.prototype.isLoading = function isLoading(id) {
  516. return this.loadingWidgets[id];
  517. };
  518. WidgetLoader.prototype.getWidget = function getWidget(id) {
  519. return id ? this.loadedWidgets[id] : undefined;
  520. };
  521. WidgetLoader.prototype.getWidgetAsync = function getWidgetAsync(id) {
  522. var _this6 = this;
  523. if (!this.widgetReadyPromises[id]) {
  524. this.widgetReadyPromises[id] = new Promise(function (resolve) {
  525. var widget = void 0;
  526. if (_this6.isLoaded(id)) {
  527. widget = _this6.getWidget(id);
  528. }
  529. if (!widget) {
  530. _this6.pendingWidgetResolveRequests[id] = resolve;
  531. } else {
  532. resolve(widget);
  533. }
  534. });
  535. }
  536. return this.widgetReadyPromises[id];
  537. };
  538. /**
  539. * Call the named API on all loaded widgets that implement it.
  540. * Optionally, return the result of those calls as an array.
  541. * @returns An array of results (of arbitrary form) that the caller can examine.
  542. * If the api does not return anything, this array will be empty.
  543. */
  544. WidgetLoader.prototype.runAPIOnAllWidgets = function runAPIOnAllWidgets(api, pageId) {
  545. var resultSet = [];
  546. _.each(_.keys(this.loadedWidgets), function (widgetKey) {
  547. var loadedWidget = this.loadedWidgets[widgetKey];
  548. if (loadedWidget && loadedWidget[api] && (!pageId || loadedWidget.getContainerPageId() === pageId)) {
  549. var result = loadedWidget[api]();
  550. if (result) {
  551. resultSet.push(result);
  552. }
  553. }
  554. }.bind(this));
  555. return resultSet;
  556. };
  557. WidgetLoader.prototype._onError = function _onError(fOnError, errCode, oWidgetInfo, e, trackingId) {
  558. var error = {
  559. 'errorCode': errCode,
  560. 'widget': oWidgetInfo,
  561. 'exception': e
  562. };
  563. if (fOnError) {
  564. setTimeout(function () {
  565. fOnError(error);
  566. }, 1);
  567. }
  568. this.logger.error('Error loading widget', trackingId, error);
  569. };
  570. WidgetLoader.prototype._invokeLifeCycleHandlers = function _invokeLifeCycleHandlers(name, payload) {
  571. return this.dashboardApi.getFeature('.LifeCycleManager').invokeLifeCycleHandlers(name, payload);
  572. };
  573. return WidgetLoader;
  574. }();
  575. return WidgetLoader;
  576. });
  577. //# sourceMappingURL=WidgetLoader.js.map