DataSourcePanel.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. 'use strict';
  2. /**
  3. * Licensed Materials - Property of IBM
  4. *
  5. * IBM Cognos Products: Dashboard
  6. *
  7. * Copyright IBM Corp. 2015, 2020
  8. *
  9. * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
  10. */
  11. define(['jquery', 'underscore', '../../lib/@waca/core-client/js/core-client/ui/core/View', 'text!./templates/DataSourcePanel.html', '../nls/StringResources', '../../DynamicFileLoader', '../../lib/@waca/core-client/js/core-client/utils/BidiUtil', '../../lib/@waca/core-client/js/core-client/utils/BrowserUtils'], function ($, _, BaseView, template, resources, DynamicFileLoader, BidiUtil, BrowserUtils) {
  12. 'use strict';
  13. var DataSourcePanel = BaseView.extend({
  14. templateString: template,
  15. events: {
  16. 'primaryaction .backButton ': 'goBack'
  17. },
  18. init: function init() {
  19. var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  20. DataSourcePanel.inherited('init', this, arguments);
  21. $.extend(this, options);
  22. this.slideout = options.slideout;
  23. this.dashboardView = options.glassContext.appController.getCurrentContentView();
  24. this.initialize();
  25. // Indicate that this view must be reloaded when the dashboard view is reloaded
  26. // because we depend on the datasource service
  27. this.dashboardView.addReloadableObject(this.viewId, this);
  28. this._cachedControllers = [];
  29. },
  30. initialize: function initialize() {
  31. this.dashboardApi = this.dashboardView.getDashboardApi();
  32. this.currentView = null;
  33. this.viewEntries = [];
  34. this.iconsFeature = this.dashboardApi.getFeature('Icons');
  35. this._setupEvents();
  36. this._setupPostRelinkHandler();
  37. },
  38. _setupPostRelinkHandler: function _setupPostRelinkHandler() {
  39. this.relinkHandler = this.dashboardApi.getFeature('.LifeCycleManager').registerLifeCycleHandler('post:relink', this._onRelink.bind(this));
  40. },
  41. _onRelink: function _onRelink() {
  42. this._cachedControllers = [];
  43. },
  44. reload: function reload() {
  45. this.initialize();
  46. if (this.isRendered) {
  47. this.render();
  48. }
  49. },
  50. _setupEvents: function _setupEvents() {
  51. this.registeredEvents = [];
  52. var dataSourcesSvc = this.dashboardApi.getFeature('dataSources.deprecated');
  53. this.dataSourceCollection = dataSourcesSvc.getSourcesCollection();
  54. this.registeredEvents.push(this.dashboardApi.on('widget:selected', this.handleWidgetSelection.bind(this)));
  55. this.registeredEvents.push(this.dashboardApi.on('dataSourceGrid:dataSourceSelected', this.showMetadataTree.bind(this)));
  56. this.registeredEvents.push(this.dashboardApi.on('dataSourcePanel:dataSourceAdded', this.handleDataSourceAddedByUser.bind(this)));
  57. this.registeredEvents.push(this.dashboardApi.on('dataSourceGrid:clearSourceSelected', this.clearGridSourceSelection.bind(this)));
  58. },
  59. render: function render() {
  60. this.isRendered = true;
  61. // We will hide the slideout default pin button
  62. this.slideout.$el.addClass('sidepane');
  63. this.$el.empty();
  64. this.$el.addClass('datasetPanel');
  65. var dataSourceIcon = this.iconsFeature.getIcon('common-data_source');
  66. var previousIcon = this.iconsFeature.getIcon('common-previous');
  67. var sHtml = this.dotTemplate({
  68. dataSourceIcon: dataSourceIcon.id,
  69. previousIcon: previousIcon.id,
  70. goBackLabel: resources.get('backLabel')
  71. });
  72. this.$el.append(sHtml);
  73. this._$dataSourceHeaderTitle = this.$('.datasetHeader .title');
  74. this._$typeIcon = this.$('.datasetHeader .typeIcon');
  75. this._$backButton = this.$('.datasetHeader .backButton');
  76. this.showView(this._getViewOptions(), undefined, true);
  77. },
  78. setTransientState: function setTransientState(name, value) {
  79. this.dashboardView.setTransientState(name, value);
  80. },
  81. getTransientState: function getTransientState(name) {
  82. return this.dashboardView.getTransientState(name);
  83. },
  84. getDataSourceListOptions: function getDataSourceListOptions() {
  85. return {
  86. id: 'dataSourceList',
  87. title: resources.get('sourcePaneLabel'),
  88. module: 'dashboard-analytics/dataSources/views/DataSourceList'
  89. };
  90. },
  91. remove: function remove() {
  92. this.dashboardView.removeReloadableObject(this.viewId, this);
  93. if (this.registeredEvents.length) {
  94. _.each(this.registeredEvents, function (collectionEvent) {
  95. if (collectionEvent) {
  96. collectionEvent.remove();
  97. }
  98. });
  99. this.registeredEvents.length = 0;
  100. }
  101. for (var i = 0; i < this.viewEntries.length; i += 1) {
  102. var _cachedView = this.viewEntries[i]._cachedView;
  103. if (_cachedView) {
  104. _cachedView.remove();
  105. this.viewEntries[i]._cachedView = null;
  106. }
  107. }
  108. this.viewEntries.length = 0;
  109. this._cachedControllers.length = 0;
  110. this.slideout = null;
  111. this.dashboardView = null;
  112. this.dashboardApi = null;
  113. this.iconsFeature = null;
  114. this.dataSourceCollection = null;
  115. this.relinkHandler && this.relinkHandler.remove();
  116. this.relinkHandler = null;
  117. DataSourcePanel.inherited('remove', this, arguments);
  118. },
  119. showView: function showView(options, preventRender) {
  120. var setFocus = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
  121. if (options.forceRefreshMetadata) {
  122. // Return when the current view is not metadata panel
  123. if (this.currentView && !this.currentView.back) {
  124. return;
  125. } else {
  126. this._removeViewEntry({
  127. id: options.id
  128. });
  129. }
  130. }
  131. //Save the state of the pane
  132. this.setTransientState(this._getStateName(), $.extend({}, options));
  133. if (!options.id) {
  134. options.id = options.title;
  135. }
  136. var viewEntry = this._findViewEntry(options);
  137. if (viewEntry) {
  138. this._enableCachedView(viewEntry, preventRender, setFocus);
  139. return;
  140. }
  141. // Add common attributes to the view options
  142. options.panelController = this;
  143. options.dashboardApi = this.dashboardApi;
  144. var registerView = function (view) {
  145. options._cachedView = view;
  146. this.viewEntries.push(options);
  147. this._enableCachedView(options, undefined, setFocus);
  148. }.bind(this);
  149. if (this._viewInstance) {
  150. registerView(this._viewInstance);
  151. } else {
  152. DynamicFileLoader.load([options.module]).then(function (modules) {
  153. var view = new modules[0](options);
  154. registerView(view);
  155. }.bind(this));
  156. }
  157. },
  158. /**
  159. * Goes back and shows the previous view
  160. * @param preventRender When true the view will not be rendered
  161. */
  162. goBack: function goBack(preventRender) {
  163. if (this.currentView && this.currentView.back) {
  164. this.showView(this.currentView.back, preventRender);
  165. this.currSourceId = null;
  166. }
  167. },
  168. clearIcons: function clearIcons() {
  169. this.$('.datasetHeader .icons').empty();
  170. },
  171. addIcon: function addIcon($icon, handler) {
  172. var $button = $('<div class="ds_btn" tabindex="0" role="button"></div>');
  173. $button.attr('aria-labelledby', $icon.attr('id'));
  174. $button.append($icon);
  175. $button.on('primaryaction', handler);
  176. this.$('.datasetHeader .icons').append($button);
  177. },
  178. renderDataSourcePaneButtons: function renderDataSourcePaneButtons(collectionId) {
  179. return this.dashboardApi.findGlassCollection(collectionId).then(function (buttonsCollection) {
  180. if (buttonsCollection) {
  181. buttonsCollection.sort(function (a, b) {
  182. return (a.weight || 0) - (b.weight || 0);
  183. });
  184. buttonsCollection.forEach(function (buttonDefinition) {
  185. this._renderDataSourceButton(buttonDefinition);
  186. }.bind(this));
  187. }
  188. }.bind(this));
  189. },
  190. _renderDataSourceButton: function _renderDataSourceButton(buttonDefinition) {
  191. var id = this.viewId + buttonDefinition.name;
  192. var icon = {};
  193. var buttonIcon = buttonDefinition.icon;
  194. if (buttonIcon.indexOf('add-new') !== -1) {
  195. icon = this.iconsFeature.getIcon('addNew');
  196. } else if (buttonIcon.indexOf('menuoverflow') !== -1) {
  197. icon = this.iconsFeature.getIcon('overflowMenuHorizontal32');
  198. } else if (buttonIcon.indexOf('warning') !== -1 || buttonIcon.indexOf('error') !== -1) {
  199. icon = this.iconsFeature.getIcon('common-warning');
  200. }
  201. var btnSVG = this._getSVGIcon(icon.id);
  202. var $addBtn = $('<div class="addIcon" id="' + id + '">' + btnSVG + '</div>');
  203. //Add Source Button
  204. var label = buttonDefinition.label;
  205. $addBtn.attr('title', label);
  206. $addBtn.attr('aria-label', label);
  207. this.addIcon($addBtn, this.executeAction.bind(this, buttonDefinition));
  208. },
  209. renderSelectSourceButton: function renderSelectSourceButton(buttonDefinition) {
  210. var id = this.viewId + buttonDefinition.name;
  211. var label = buttonDefinition.label;
  212. var $button = $('<button type="button" role="button" id="s' + id + '" class="selectSourceButton">' + label + '</button>');
  213. $button.attr('title', label);
  214. $button.attr('aria-label', label);
  215. $button.on('primaryaction', this.executeAction.bind(this, buttonDefinition));
  216. return $button;
  217. },
  218. executeAction: function executeAction(buttonDefinition, evt) {
  219. var actionController = buttonDefinition.actionController;
  220. if (!actionController) {
  221. return;
  222. }
  223. if (buttonDefinition.name && this._cachedControllers[buttonDefinition.name]) {
  224. this._cachedControllers[buttonDefinition.name].execute(evt, buttonDefinition);
  225. } else {
  226. require([actionController], function (ActionController) {
  227. var controller = new ActionController({
  228. dashboardApi: this.dashboardApi,
  229. dataSourcePanel: this
  230. });
  231. if (buttonDefinition.name) {
  232. this._cachedControllers[buttonDefinition.name] = controller;
  233. }
  234. controller.execute(evt, buttonDefinition);
  235. }.bind(this));
  236. }
  237. },
  238. setFocus: function setFocus() {
  239. //This is being handled by the sub-view.
  240. },
  241. /*
  242. * Private Methods
  243. */
  244. _getViewOptions: function _getViewOptions() {
  245. // check if there is an active data source set in explore view, if yes we want to show metadata tree of this data source
  246. var activeSourceId = this.dashboardApi.getActiveDataSourceId();
  247. var options = this.getTransientState(this._getStateName());
  248. if (activeSourceId) {
  249. if (!options || !options.dataSource || options.dataSource.getId() !== activeSourceId) {
  250. if (!options) {
  251. // options is null when we render the datasource panel for the first time, mark 'dataSourceListRendered' state to true so that when DataSourceList.render() is called for goBack,
  252. // it doesn't try to load the metadata
  253. this.setTransientState('dataSourceListRendered', true);
  254. }
  255. options = this._getActiveDataSourceOptions(activeSourceId);
  256. }
  257. }
  258. if (!options) {
  259. options = this.getDataSourceListOptions();
  260. }
  261. return options;
  262. },
  263. _getActiveDataSourceOptions: function _getActiveDataSourceOptions(activeSourceId) {
  264. var dataSource = this.dataSourceCollection.getSource(activeSourceId);
  265. var name = dataSource.getName();
  266. var options = {
  267. id: dataSource.getId(),
  268. module: 'dashboard-analytics/dataSources/views/Metadata',
  269. dataSource: dataSource,
  270. back: this.getDataSourceListOptions(),
  271. title: name,
  272. dataSourceName: name
  273. };
  274. return options;
  275. },
  276. _getStateName: function _getStateName() {
  277. return 'dataSourcePaneState';
  278. },
  279. _enableCachedView: function _enableCachedView(viewEntry, preventRender) {
  280. var setFocus = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
  281. //Toggle elements which are only visible in one view.
  282. this._$backButton.toggle(!!viewEntry.back);
  283. this._$typeIcon.toggle(!!viewEntry.back);
  284. this.setTypeIcon(viewEntry.dataSource);
  285. var isIE = BrowserUtils.isIE();
  286. if (this.currentView && this.currentView._cachedView) {
  287. if (isIE) {
  288. this.currentView._cachedView.hide();
  289. } else {
  290. this.currentView._cachedView.detach();
  291. }
  292. }
  293. this.clearIcons();
  294. if (viewEntry.title) {
  295. this._$dataSourceHeaderTitle.text(viewEntry.title);
  296. this._$dataSourceHeaderTitle.attr('title', viewEntry.title);
  297. this._$dataSourceHeaderTitle.attr('aria-label', viewEntry.title);
  298. this._$dataSourceHeaderTitle.attr('dir', BidiUtil.resolveBaseTextDir(viewEntry.title));
  299. }
  300. if (!viewEntry._cachedView) return;
  301. if (isIE && viewEntry._cachedView && viewEntry._cachedView.$el.parent().length !== 0) {
  302. viewEntry._cachedView.$el.show();
  303. } else {
  304. this.$('.datasetContent').append(viewEntry._cachedView.$el);
  305. }
  306. //currentView needs to be set before the cached view is rendered. This is so that user
  307. //can still return to data source list pane in case of error since the view will always be partially rendered with
  308. //'go back' button.
  309. this.currentView = viewEntry;
  310. this.currentView && this.currentView._cachedView && this.currentView._cachedView.show({ forceRefreshMetadata: viewEntry.forceRefreshMetadata, preventRender: preventRender, setFocus: setFocus });
  311. },
  312. setTypeIcon: function setTypeIcon(dataSource) {
  313. var dataSourceIcon = this.iconsFeature.getIcon('common-data_source');
  314. var typeIcon = this._getSVGIcon(dataSourceIcon.id);
  315. if (dataSource) {
  316. //These rules should match those in templates/DataSourceItem.html
  317. var state = dataSource.getState(),
  318. type = dataSource.getType();
  319. if (state === 'loading') {
  320. typeIcon = '<svg class="loader--small"><circle class="loader__path" cx="12.35px" cy="12.35px" r="6px"></circle></svg>';
  321. } else if (type === 'module') {
  322. var modelIcon = this.iconsFeature.getIcon('common-titan-model');
  323. typeIcon = this._getSVGIcon(modelIcon.id);
  324. } else if (type === 'uploadedFile') {
  325. var uploadIcon = this.iconsFeature.getIcon('common-upload');
  326. typeIcon = this._getSVGIcon(uploadIcon.id);
  327. } else {
  328. var dataSetIcon = this.iconsFeature.getIcon('common-dataset');
  329. typeIcon = this._getSVGIcon(dataSetIcon.id);
  330. }
  331. }
  332. this._$typeIcon.html(typeIcon);
  333. },
  334. _getSVGIcon: function _getSVGIcon(SVGIconId) {
  335. return SVGIconId && '<svg class="svgIcon"><use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#' + SVGIconId + '"></use></svg>';
  336. },
  337. _findViewEntry: function _findViewEntry(options) {
  338. return _.find(this.viewEntries, function (viewEntry) {
  339. return options.id === viewEntry.id;
  340. });
  341. },
  342. _removeViewEntry: function _removeViewEntry(options) {
  343. var entry = _.findWhere(this.viewEntries, {
  344. id: options.id
  345. });
  346. if (entry && entry._cachedView) {
  347. entry._cachedView.remove();
  348. }
  349. this.viewEntries = _.without(this.viewEntries, entry);
  350. },
  351. /**
  352. * Override Gemini base behavior
  353. */
  354. _hideDataSourcePanel: function _hideDataSourcePanel() {
  355. // Empty method.
  356. },
  357. loadMetadata: function loadMetadata(dataSource) {
  358. var forceRefreshMetadata = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
  359. var setFocus = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
  360. dataSource.getLocalizedName().then(function (name) {
  361. this.currSourceId = dataSource.getId();
  362. this.showView({
  363. id: dataSource.getId(),
  364. dataSourceName: name,
  365. module: 'dashboard-analytics/dataSources/views/Metadata',
  366. dataSource: dataSource,
  367. back: this.getDataSourceListOptions(),
  368. forceRefreshMetadata: forceRefreshMetadata,
  369. title: name
  370. }, undefined, setFocus);
  371. }.bind(this));
  372. },
  373. handleDataSourceAddedByUser: function handleDataSourceAddedByUser(event) {
  374. if (event.sender && this.dataSourceCollection) {
  375. this.dashboardApi.triggerDashboardEvent('dataSourcePanel:dataSourceSelected', {
  376. sender: event.sender
  377. });
  378. this.showMetadataTree(event, true);
  379. }
  380. },
  381. showMetadataTree: function showMetadataTree(event) {
  382. var setFocus = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
  383. if (typeof setFocus !== 'boolean') {
  384. setFocus = false;
  385. }
  386. if (event.sender && this.dataSourceCollection) {
  387. this.loadMetadata(this.dataSourceCollection.getSource(event.sender), false, setFocus);
  388. }
  389. },
  390. /**
  391. * Clear the UI selection from the metadata tree
  392. */
  393. clearGridSourceSelection: function clearGridSourceSelection() {
  394. if (this.dataSourceCollection) {
  395. var $items = this.$el.find('.bi-common-treeItem');
  396. $items.removeClass('is-selected');
  397. }
  398. },
  399. handleWidgetSelection: function handleWidgetSelection(event) {
  400. if (event.sender && this.dataSourceCollection) {
  401. var sourceId = this.dataSourceCollection.usesSource(event.sender);
  402. if (sourceId && this.currSourceId !== sourceId) {
  403. var source = this.dataSourceCollection.getSource(sourceId);
  404. if (source.getState() !== 'error') {
  405. //Don't attempt to show a tree for a source in an error state.
  406. this.currSourceId = sourceId;
  407. this.loadMetadata(source);
  408. }
  409. }
  410. }
  411. }
  412. });
  413. return DataSourcePanel;
  414. });
  415. //# sourceMappingURL=DataSourcePanel.js.map