VisActionHelper.js 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910
  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: Dashboard
  6. * (C) Copyright IBM Corp. 2013, 2021
  7. * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
  8. *
  9. * VisActionHelper
  10. * The VisActionHelper is responsible for loading and executing interactive actions that affect the vismodel.
  11. *
  12. */
  13. define(['jquery', 'underscore', '../../widgets/livewidget/nls/StringResources', '../../DynamicFileLoader', './VisActionSlotSupport', '../../dataSources/utils/DatasourceUtil', '../../apiHelpers/SlotAPIHelper'], function ($, _, resources, DynamicFileLoader, VisActionSlotSupport, DatasourceUtil, SlotAPIHelper) {
  14. 'use strict';
  15. var SlotAction = function () {
  16. function SlotAction(spec) {
  17. _classCallCheck(this, SlotAction);
  18. this.spec = spec;
  19. }
  20. SlotAction.prototype.getActionId = function getActionId() {
  21. return this.spec.ACTION_ID;
  22. };
  23. SlotAction.prototype.getAvailableActions = function getAvailableActions() {
  24. return [this.spec];
  25. };
  26. return SlotAction;
  27. }();
  28. var VisActionHelper = function () {
  29. function VisActionHelper(attributes) {
  30. _classCallCheck(this, VisActionHelper);
  31. this._visModel = attributes.visModel;
  32. this.selectionController = attributes.selectionController;
  33. this.pageContext = attributes.pageContext;
  34. this.ownerWidget = attributes.ownerWidget;
  35. this.content = attributes.content;
  36. this.dashboardApi = attributes.ownerWidget.getDashboardApi();
  37. //Any dynamically loaded actions (eg: FilterLocalExcludeAction) should be saved here for cleanup on remove.
  38. this._registeredActions = {};
  39. this.logger = attributes.ownerWidget.logger;
  40. this._visActionSlotSupport = new VisActionSlotSupport(attributes);
  41. }
  42. VisActionHelper.prototype.remove = function remove() {
  43. var _this = this;
  44. if (this.toolbar) {
  45. this.toolbar.remove();
  46. this.toolbar = null;
  47. }
  48. this._visModel = null;
  49. this.selectionController = null;
  50. this.pageContext = null;
  51. this.ownerWidget = null;
  52. this.visualization = null;
  53. this.dashboardApi = null;
  54. this.logger = null;
  55. // cleanup registered actions (eg FilterLocalKeep, FilterLocalExclude, FilterAllKeep).
  56. _.each(_.keys(this._registeredActions), function (action) {
  57. delete _this._registeredActions[action];
  58. });
  59. };
  60. VisActionHelper.prototype._supportsSortAction = function _supportsSortAction() {
  61. return this._visModel && this._visModel.supportsSortAction();
  62. };
  63. VisActionHelper.prototype._supportsContextualTopBottomAction = function _supportsContextualTopBottomAction() {
  64. return this.getVisualization().getDefinition().getProperty('supportsContextualTopBottomAction');
  65. };
  66. VisActionHelper.prototype._supportsNonContextualTopBottomAction = function _supportsNonContextualTopBottomAction() {
  67. return this.getVisualization().getDefinition().getProperty('supportsNonContextualTopBottomAction');
  68. };
  69. VisActionHelper.prototype._supportsFormatAction = function _supportsFormatAction() {
  70. return this._visModel && this._visModel.supportsFormatAction();
  71. };
  72. VisActionHelper.prototype._supportsBinAction = function _supportsBinAction() {
  73. return this._visModel && this._visModel.supportsBinAction();
  74. };
  75. /**
  76. * Show the actions/labels tooltip for the current selection
  77. *
  78. * @param selections - Array of selections
  79. * @param position - Container node or bounds information where the tooltip should appear
  80. * @param {boolean} includeApplyFilter - whether to include the 'filter' (apply filter) action
  81. * @param {boolean} noFilter - true - hide all options on hover or used by grid/crosstab to turn off the filter option
  82. * @param {boolean} drillOnly
  83. * @param {boolean} noDrillthrough - whether to include the 'Drill through' (JumpToAction) action
  84. * @param {options}
  85. */
  86. VisActionHelper.prototype.showDataPointActions = function showDataPointActions(selections, position, includeApplyFilter, noFilters, drillOnly, noDrillthrough, options) {
  87. var _this2 = this;
  88. options = options || {};
  89. var actionOptions = {
  90. includeApplyFilter: includeApplyFilter,
  91. noFilters: noFilters,
  92. drillOnly: drillOnly,
  93. noDrillthrough: noDrillthrough,
  94. selections: selections,
  95. isHovering: options.isHover,
  96. area: options.area,
  97. isAreaSelected: options.isAreaSelected
  98. };
  99. var availableActions = [];
  100. var fRegisterAction = function fRegisterAction(action) {
  101. availableActions = availableActions.concat(action.getAvailableActions());
  102. };
  103. return this._getExtraDatapointActions(options).then(function (actions) {
  104. _.each(actions, fRegisterAction);
  105. var labels = _this2.selectionController.getToolbarLabelsForSelections(selections);
  106. var sTitle = _this2.selectionController.getToolbarTitleForSelections(selections);
  107. if (!options.isHover) {
  108. var _availableActions;
  109. (_availableActions = availableActions).push.apply(_availableActions, _this2._getDataPointActionList(selections, actionOptions, options));
  110. }
  111. if (labels.length > 0 || sTitle.length > 0) {
  112. var eventOptions = {
  113. 'targetNode': position ? position.targetNode : null,
  114. 'targetBounds': position ? position.bounds : null,
  115. 'actions': availableActions,
  116. 'labels': labels,
  117. 'title': sTitle,
  118. 'options': options,
  119. 'tooltipContext': { /* This is not used in for now. It is here for the custom tooltip extension */
  120. content: _this2.content,
  121. context: _this2._convertOldSelectionToNewSpec(selections)
  122. }
  123. };
  124. eventOptions.toolbarOptions = {
  125. iconOnly: true,
  126. isTextEditorOnly: false
  127. };
  128. if (availableActions.length === 1) {
  129. eventOptions.toolbarOptions['isTextEditorOnly'] = true;
  130. }
  131. _this2.dashboardApi.triggerDashboardEvent('widget:availableActions', eventOptions);
  132. }
  133. });
  134. };
  135. /**
  136. * As agreed, the final selections payload is the union of brushing and pending filters.
  137. * @example of the payload:
  138. * {
  139. * 'area': 'visualization',
  140. * 'dataPoints': [{
  141. * 'categories': [{
  142. * 'dataItemId': 'item0_id',
  143. * 'columnId': 'item0',
  144. * 'value': 'value1',
  145. * 'label': 'value1_label'
  146. * }, {
  147. * 'dataItemId': 'item1_id',
  148. * 'columnId': 'item1',
  149. * 'value': 'value2',
  150. * 'label': 'value2_label'
  151. * }],
  152. * 'facts': [{
  153. * 'dataItemId': 'fact2_id',
  154. * 'columnId': 'fact2',
  155. * 'value': 1000
  156. * }]
  157. * }, {
  158. * 'categories': [{
  159. * 'dataItemId': 'item0_id',
  160. * 'columnId': 'item0',
  161. * 'value': 'value3',
  162. * 'label': 'value3_label'
  163. * }, {
  164. * 'dataItemId': 'item1_id',
  165. * 'columnId': 'item1',
  166. * 'value': 'value2',
  167. * 'label': 'value2_label'
  168. * }]
  169. * }
  170. */
  171. VisActionHelper.prototype._getDataPointActionList = function _getDataPointActionList(selections, actionOptions, options) {
  172. var newSelections = this._getBrushingAndPendingSelections(selections, this.content, options);
  173. return this.content.getFeature('DataPointActions').getDataPointActionList(newSelections, actionOptions);
  174. };
  175. VisActionHelper.prototype._convertOldSelectionToNewSpec = function _convertOldSelectionToNewSpec(selections) {
  176. var _this3 = this;
  177. var dataPoints = [];
  178. var categorySelections = selections && selections.getCategorySelections() || [];
  179. var selectionEntry = {};
  180. var categories = [];
  181. categorySelections.forEach(function (category) {
  182. categories.push(_this3._toPayLoadValue(category.value, category.slotDataItem.getColumnId(), category.slotDataItem.getId()));
  183. });
  184. var ordinalSelections = selections && selections.getOrdinalSelections() || [];
  185. var facts = _.map(ordinalSelections, function (factItem) {
  186. return {
  187. dataItemId: factItem.slotDataItem.getId(),
  188. columnId: factItem.slotDataItem.getColumnId(),
  189. value: factItem.value instanceof Object ? factItem.value.value : factItem.value
  190. };
  191. });
  192. if (categories.length) {
  193. selectionEntry.categories = categories;
  194. }
  195. if (facts.length) {
  196. selectionEntry.facts = facts;
  197. }
  198. dataPoints.push(selectionEntry);
  199. return {
  200. dataPoints: dataPoints
  201. };
  202. };
  203. VisActionHelper.prototype._getBrushingAndPendingSelections = function _getBrushingAndPendingSelections(selections, content, options) {
  204. var _this4 = this;
  205. var dataPoints = [];
  206. var categorySelections = selections && selections._aCategorySelections || [];
  207. var selectionEntry = {};
  208. var categories = [];
  209. categorySelections.forEach(function (category) {
  210. if (!category.isEdgeSelection) {
  211. categories.push(_this4._toPayLoadValue(category.value, category.slotDataItem.getColumnId(), category.slotDataItem.getId()));
  212. }
  213. });
  214. var ordinalSelections = selections && selections._aOrdinalSelections || [];
  215. var facts = _.map(ordinalSelections, function (factItem) {
  216. return {
  217. dataItemId: factItem.slotDataItem.getId(),
  218. columnId: factItem.slotDataItem.getColumnId(),
  219. value: factItem.value /* TODO - fix the value here. It should just be the value, but it is a json {value:1234, v:1234} */
  220. };
  221. });
  222. if (categories.length) {
  223. selectionEntry.categories = categories;
  224. }
  225. if (facts.length) {
  226. selectionEntry.facts = facts;
  227. }
  228. dataPoints.push(selectionEntry);
  229. var selectedDataItemList = selections._aCategorySelections ? selections._aCategorySelections.map(function (selection) {
  230. return selection.slotDataItem;
  231. }) : [];
  232. var pageContextSelections = content.getFeature('DataPointSelections').getSelections(selectedDataItemList);
  233. var pendingSelections = content.getFeature('DataPointSelections.pending').getSelections(selectedDataItemList);
  234. // Check if current selection is in pageContext selections
  235. var selectionInPageContext = false;
  236. var currentCategoryKey = _.pluck(categories, 'columnId').join();
  237. pageContextSelections.forEach(function (selection) {
  238. var categoryKey = _.pluck(selection.categories, 'columnId').join();
  239. // if current selection is in pageContext selections, use combined selections
  240. // OR if edge selection, and current selection is in pageContext
  241. if (currentCategoryKey === categoryKey || currentCategoryKey === '' && _this4._compareCategorySelections(categorySelections, selection.categories)) {
  242. pageContextSelections = pageContextSelections.concat(pendingSelections);
  243. selectionInPageContext = true;
  244. }
  245. });
  246. if (!selectionInPageContext) {
  247. pageContextSelections = pendingSelections;
  248. }
  249. dataPoints = dataPoints.concat(pageContextSelections);
  250. dataPoints = this._uniqueValues(dataPoints);
  251. return {
  252. area: options && options.area,
  253. dataPoints: dataPoints
  254. };
  255. };
  256. VisActionHelper.prototype._compareCategorySelections = function _compareCategorySelections(categorySelections, categories) {
  257. var values = _.pluck(categorySelections, 'value');
  258. var categoryKeys = _.pluck(categories, 'value');
  259. var contains = true;
  260. values.forEach(function (value) {
  261. if (!(categoryKeys.indexOf(value.u) !== -1)) {
  262. contains = false;
  263. }
  264. });
  265. return values && categoryKeys && contains;
  266. };
  267. VisActionHelper.prototype._toPayLoadValue = function _toPayLoadValue(value, columnId, dataItemId) {
  268. var resultPayLoad = {
  269. dataItemId: dataItemId,
  270. columnId: columnId,
  271. value: value.u,
  272. label: value.d
  273. };
  274. if (value.pu || value.p) {
  275. resultPayLoad.parentId = value.pu || value.p.u;
  276. }
  277. return resultPayLoad;
  278. };
  279. VisActionHelper.prototype._uniqueValues = function _uniqueValues() {
  280. var dataPointSelections = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
  281. var uniqueValues = [];
  282. var valuesMap = {};
  283. dataPointSelections.forEach(function (dataPoint) {
  284. var categoryKey = _.pluck(dataPoint.categories, 'value').join();
  285. if (!valuesMap[categoryKey]) {
  286. valuesMap[categoryKey] = 1;
  287. uniqueValues.push(dataPoint);
  288. }
  289. });
  290. return uniqueValues;
  291. };
  292. VisActionHelper.prototype._getExtraDatapointActions = function _getExtraDatapointActions(options) {
  293. var result = void 0;
  294. if (!(options && options.slots && options.mapIndex !== undefined)) {
  295. result = Promise.resolve([]);
  296. } else {
  297. options.actions && options.actions.actionOptions && (options.actions.actionOptions.JumpToAction = {
  298. area: options.area
  299. });
  300. result = this.getActionsForSlots(options.slots, options.mapIndex, null, options.actions);
  301. }
  302. return result;
  303. };
  304. VisActionHelper.prototype.hideToolbarActions = function hideToolbarActions() {
  305. this.dashboardApi.triggerDashboardEvent('widget:hideToolbar');
  306. };
  307. /**
  308. * @param FilterAction - the class of the filter action.
  309. * @param slot - the data slot associated with this filter action.
  310. * @param index - the index of the data item in the slot.
  311. * @returns a new filter action associated with the passed in data slot.
  312. */
  313. VisActionHelper.prototype.getNewFilterActionForSlot = function getNewFilterActionForSlot(FilterAction, slot, index) {
  314. // This method assumes that the required FilterAction object is loaded.
  315. //TODO: This action should probably be called FilterSlotAction (and separated from FilterAction)
  316. // since it is specific to filtering items assigned to visible slots.
  317. return new FilterAction(this.getVisualization(), slot, index, this.ownerWidget.getDashboardApi(), this.logger);
  318. };
  319. /**
  320. * @param FilterAction - the class of the filter action
  321. * @param visColumn - the column to create this context filter for.
  322. * @returns a FilterAction associated with the passed in context visColumn.
  323. * Note: Use getNewFilterActionForSlot when possible because a slot
  324. * can override the usage of a column (eg: an attribute column assigned to an ordinal slot becomes a count).
  325. */
  326. VisActionHelper.prototype.getNewFilterActionForContextColumn = function getNewFilterActionForContextColumn(FilterDropAction, metadataColumn) {
  327. // This method assumes that the required FilterAction object is loaded.
  328. return new FilterDropAction(this.getVisualization(), metadataColumn, this.pageContext, this.ownerWidget.getDashboardApi(), this.logger);
  329. };
  330. VisActionHelper.prototype.getActionsForLocalFilter = function getActionsForLocalFilter(metadataColumn, filterId) {
  331. var _this5 = this;
  332. var availableActions = []; //The set of all availableActions for this context
  333. return DynamicFileLoader.load(['dashboard-analytics/visualizations/interactions/FilterDropAction', 'dashboard-analytics/visualizations/interactions/DeleteFilterAction']).then(function (modules) {
  334. var FilterDropAction = modules[0];
  335. var DeleteFilterAction = modules[1];
  336. var existingFilterEntry = filterId && _this5._visModel.localFilters.getFilterEntry({
  337. id: filterId
  338. });
  339. if (existingFilterEntry && _this5._visModel.isFilterEditable(existingFilterEntry)) {
  340. availableActions.push(_this5.getNewFilterActionForContextColumn(FilterDropAction, metadataColumn));
  341. }
  342. availableActions.push(new DeleteFilterAction(_this5._visModel, metadataColumn, filterId));
  343. return availableActions;
  344. });
  345. };
  346. VisActionHelper.prototype._getSingleSlot = function _getSingleSlot(slots, mapIndex) {
  347. //if slots length is not 1, return null
  348. if (slots.length !== 1) {
  349. return null;
  350. }
  351. //if mapIndex exists and its an array of length > 1, return null
  352. if (mapIndex && mapIndex instanceof Array && mapIndex.length > 1) {
  353. return null;
  354. }
  355. //otherwise, return slots[0]
  356. return slots[0];
  357. };
  358. /**
  359. * Collect action information for slots that can be used to
  360. * determine which actions are available for the slots.
  361. * @param slots {Array} - an array of up to two slots
  362. * @param mapIndex integer or array of integers corresponding to indices of data items in a slot
  363. * @param selectionObj selected DOM representing the selected dataItem
  364. * @param options specifies the additional options necessary to get the actions
  365. * @return action information for slot
  366. *
  367. * @note the case where mapIndex can be an array:
  368. * (1) create a croostab vis
  369. * (2) Rows: Revenue, Product Line
  370. * Values: Order method type, Quantity
  371. * (3) multi-select two column, and right click on it.
  372. */
  373. VisActionHelper.prototype._getActionInfoForSlots = function _getActionInfoForSlots(slots, mapIndex, selectionObj, options) {
  374. var singleSlot = this._getSingleSlot(slots, mapIndex);
  375. var focusMode = options && options.focusMode != undefined ? options.focusMode : true;
  376. var itemsArea = options && options.itemsArea ? options.itemsArea : undefined;
  377. //DEFECT 253869
  378. //Verify if mapIndex is defined to differentiate when we're selecting specific data item that is a multi measure or multiple data items (get actions for y axis)
  379. var isMultiMeasure = singleSlot && mapIndex == undefined ? SlotAPIHelper.isMultiMeasuresSeriesSlot(singleSlot) || SlotAPIHelper.isMultiMeasuresValueSlot(singleSlot) : false;
  380. var slotState = {
  381. selectionObj: selectionObj,
  382. slots: slots,
  383. singleSlot: singleSlot,
  384. dataItems: [],
  385. mapIndex: mapIndex,
  386. isAuthoringMode: this._visModel.ownerWidget.isAuthoringMode,
  387. isCalculationEnabled: false,
  388. isMultiMeasure: isMultiMeasure,
  389. isEditCalculation: false,
  390. isMappedToUnavailableSourceColumn: false,
  391. isFocusMode: focusMode,
  392. supportsSortAction: this._supportsSortAction(),
  393. supportsContextualTopBottomAction: this._supportsContextualTopBottomAction(),
  394. supportsNonContextualTopBottomAction: this._supportsNonContextualTopBottomAction(),
  395. supportsFormatAction: this._supportsFormatAction(),
  396. supportsBinAction: this._supportsBinAction(),
  397. area: itemsArea
  398. };
  399. if (mapIndex === undefined || mapIndex === null) {
  400. mapIndex = 0;
  401. }
  402. if (mapIndex && Array.isArray(mapIndex)) {
  403. _.each(mapIndex, function (index) {
  404. slotState.dataItems.push(slots[0].getDataItemList()[index]);
  405. });
  406. } else {
  407. slotState.dataItems.push(slots[0].getDataItemList()[mapIndex]);
  408. }
  409. slotState.isMappedToUnavailableSourceColumn = _.every(slotState.dataItems, function (dataItem) {
  410. return dataItem.isColumnUnavailable();
  411. });
  412. if (!slotState.isMappedToUnavailableSourceColumn) {
  413. var slot = slots[0];
  414. var dataItemList = slot.getDataItemList();
  415. // [todo] livewidget_cleanup: need to revisit this and test crosstab multiselection again...
  416. if (mapIndex && Array.isArray(mapIndex)) {
  417. slotState.isCalculationEnabled = dataItemList[mapIndex[0]].getType() === 'fact';
  418. } else {
  419. slotState.isCalculationEnabled = dataItemList[mapIndex].getType() === 'fact';
  420. }
  421. var metadataColumn = void 0;
  422. if (singleSlot) {
  423. // if singleSlot is defined, mapIndex wont be an array. See _getSingleSlot()
  424. metadataColumn = dataItemList[mapIndex].getMetadataColumn();
  425. slotState.isEditCalculation = metadataColumn ? metadataColumn.isEditableCalculation() : false;
  426. }
  427. // For the grid, we need to check if the data items selected are type 'fact' to determine if calculation should be enabled
  428. if (!slotState.isCalculationEnabled && slot.getDefinition().getType() === 'any' && mapIndex && mapIndex.length < 3) {
  429. slotState.isCalculationEnabled = _.every(mapIndex, function (index) {
  430. return dataItemList[index].getType() === 'fact';
  431. });
  432. }
  433. }
  434. return {
  435. state: slotState,
  436. actions: this._enableSupportedSlotActions(slotState)
  437. };
  438. };
  439. /**
  440. * Enable / disable slot actions
  441. * @param {Object} slotState - state object of the slot information
  442. * @return array of enabled / disabled actions
  443. */
  444. VisActionHelper.prototype._enableSupportedSlotActions = function _enableSupportedSlotActions(slotState) {
  445. var _this6 = this;
  446. var actions = {};
  447. var actionExt = slotState.singleSlot ? slotState.singleSlot.getDefinition().getProperty('actions') : null;
  448. // 1. Default state of each action
  449. // Actions defined in the slot takes priority.
  450. // If no actions are defined (null or undefined) in the slot,
  451. // all actions are considered as available by default.
  452. _.each(_.keys(VisActionSlotSupport.SLOT_ACTIONS), function (action) {
  453. actions[action] = actionExt ? actionExt.indexOf(action) >= 0 : true;
  454. });
  455. // 2. Enable / Disable actions for slots
  456. _.each(_.keys(VisActionSlotSupport.SLOT_ACTIONS), function (action) {
  457. // Check if the enabled action is available for slot
  458. if (actions[action]) {
  459. var slotSupport = _this6._visActionSlotSupport.getInterface(action);
  460. // action without a validator (supports???Action) is considered enabled
  461. actions[action] = slotSupport.isSupported ? slotSupport.isSupported(slotState) : true;
  462. }
  463. });
  464. return actions;
  465. };
  466. /**
  467. * Get the list module paths to be loaded
  468. * @param actionInfo action info object
  469. * @see {@link _getActionInfoForSlots}
  470. */
  471. VisActionHelper.prototype._getActionModules = function _getActionModules(actionInfo) {
  472. var availableActionModules = [];
  473. var singleSlot = actionInfo.state.singleSlot;
  474. if (!actionInfo.state.isMappedToUnavailableSourceColumn) {
  475. _.each(actionInfo.actions, function (active, action) {
  476. var actionIndex = VisActionSlotSupport.SLOT_ACTIONS[action];
  477. availableActionModules[actionIndex] = active ? action : '';
  478. });
  479. }
  480. // defect 246203 & 262087 - if the number of slots is one in the visualization, don't show filter and calculation actions
  481. // since the filter action is determined by the ordinal slot
  482. var numberOfSlots = this.getVisualization().getSlots().getSlotList().length;
  483. //if there are any disallowed actions as defined for a single slot in the vis definition/viprConfig, then remove them here
  484. if (numberOfSlots == 1) {
  485. var disallowedActionsForSingleSlot = singleSlot.getDefinition().getProperty('disallowedActions');
  486. // If we are putting a category into an ordinal slot type then we need to
  487. // restrict actions since the filters are confused. The user will still be able
  488. // to apply a filter by dragging a category filter to local filter slots.
  489. // Note that this affects multiple visualizations and not just Summary widget.
  490. var dataItem = actionInfo.state.dataItems.length === 1 && actionInfo.state.dataItems[0];
  491. if (DatasourceUtil.isOrdinalAttribute(singleSlot, dataItem)) {
  492. if (!disallowedActionsForSingleSlot) {
  493. disallowedActionsForSingleSlot = [];
  494. }
  495. disallowedActionsForSingleSlot = disallowedActionsForSingleSlot.concat(['FilterAction']);
  496. }
  497. if (disallowedActionsForSingleSlot) {
  498. _.each(availableActionModules, function (actionModule, key, availableActionModulesArray) {
  499. if (disallowedActionsForSingleSlot.indexOf(actionModule) > -1) {
  500. availableActionModulesArray[key] = '';
  501. }
  502. });
  503. }
  504. }
  505. // prepend modules with full path
  506. return availableActionModules.map(function (sModule) {
  507. return sModule ? 'dashboard-analytics/visualizations/interactions/' + sModule : '';
  508. });
  509. };
  510. /**
  511. * Load the action modules and instantiate the action objects
  512. * @param slots {Array} - an array of up to two slots
  513. * @param mapIndex integer or array of integers corresponding to indices of data items in a slot
  514. * @param selectionObj selected DOM representing the selected dataItem
  515. * @param actionModules array of action module path
  516. * @param actionInfo action info object
  517. * @see {@link _getActionInfoForSlots}
  518. */
  519. VisActionHelper.prototype._loadActionModules = function _loadActionModules(slots, actionModules, actionInfo, options) {
  520. var _this7 = this;
  521. var slotState = actionInfo.state;
  522. var actions = options || {};
  523. return DynamicFileLoader.load(actionModules).then(function (modules) {
  524. var availableActions = []; //The set of all availableActions for this context
  525. _.each(modules, function (Module, index) {
  526. if (Module) {
  527. var action = void 0;
  528. var slotSupport = _this7._visActionSlotSupport.getInterface(index);
  529. if (slotSupport.get) {
  530. action = slotSupport.get(Module, slotState, _this7.dashboardApi);
  531. }
  532. if (action) {
  533. availableActions.push(action);
  534. }
  535. }
  536. });
  537. var slotId = slotState.singleSlot ? slotState.singleSlot.getId() : slotState.slots[0].getId();
  538. var actionExt = slotState.singleSlot ? slotState.singleSlot.getDefinition().getProperty('actions') : null;
  539. if (actions.actionsToKeep && actions.actionsToKeep.length > 0 && actionExt && actionExt.length > 0) {
  540. // there might not be a real usecase for this scenario, but it is here in case the caller provides
  541. // its own list of actionsToKeep and we need to intersect it with the list that comes from the viz definition
  542. actions.actionsToKeep = _.intersection(actions.actionsToKeep, actionExt);
  543. } else if (actionExt && actionExt.length > 0) {
  544. actions.actionsToKeep = actionExt;
  545. }
  546. var specs = _this7.content.getFeature('SlotActions').getSlotActionList(slotId, slotState.mapIndex, actions);
  547. _.each(specs, function (spec) {
  548. spec.text = spec.label;
  549. availableActions.push(new SlotAction(spec));
  550. });
  551. return availableActions;
  552. });
  553. };
  554. /**
  555. * Gets the actions for an array of up to slots based on the number and types of the slots
  556. * @param slots {Array} - an array of up to two slots
  557. * @param mapIndex integer or array of integers corresponding to indices of data items in a slot
  558. * @param selectionObj selected DOM representing the selected dataItem
  559. * @param options specifies the additional options necessary to get the actions
  560. * @returns jquery promise
  561. */
  562. VisActionHelper.prototype.getActionsForSlots = function getActionsForSlots(slots, mapIndex, selectionObj, options) {
  563. // 1. get he summarized information of actions for the slots
  564. var actionInfo = this._getActionInfoForSlots(slots, mapIndex, selectionObj, options);
  565. // 2. get the list of modules to load for each actions
  566. var availableActionModules = this._getActionModules(actionInfo);
  567. // If there is an `actionsToKeep` array, don't bother loading these availableActions, they will be filtered out in the end.
  568. // `actionsToKeep` is currently only used for sortAction (rank column). When all actions are refactored to be loaded
  569. // via _loadActionModules, we will not need this following line here.
  570. var actions = options && options.actions || options;
  571. if (actions && actions.actionsToKeep && actions.actionsToKeep.length) {
  572. availableActionModules = [];
  573. }
  574. // 3. load the modules in to an array of action instances
  575. return this._loadActionModules(slots, availableActionModules, actionInfo, actions);
  576. };
  577. /**
  578. * Load (and display) any available actions that should be rendered on selection
  579. * of a category column
  580. * @param slots An array of the category columns of up to two columns
  581. * @param position an optional object that contains the target node and/or the target bounds which can be set to improve context menu placement.
  582. * @param selectionObj Selected DOM representing the selected dataItem
  583. * @param mapIndex An optional integer or array of integers corresponding to indices of data items in a slot
  584. * @param itemsArea The clicked area of the target items. The area could be of values: visualization, legend, or none
  585. * @param actions Optional object containing actionsToKeep (array of action ids), and actionOptions (object).
  586. * When actionsToKeep exists, we will only return those actions. actionOptions contain extra options for each action.
  587. * @returns promise which is resolved when all actions are loaded.
  588. */
  589. VisActionHelper.prototype.showTitleActions = function showTitleActions(slots, position, selectionObj, mapIndex, itemsArea, actions) {
  590. var _this8 = this;
  591. if (!slots || slots.length === 0) {
  592. return Promise.resolve();
  593. }
  594. var options = {
  595. iconOnly: true,
  596. focusMode: false,
  597. itemsArea: itemsArea,
  598. actions: actions
  599. };
  600. // Don't show title when multiple selected
  601. if (slots.length === 1 && mapIndex && mapIndex.length === 1) {
  602. var index = Array.isArray(mapIndex) ? mapIndex[0] : mapIndex;
  603. options.toolbarName = slots[0].getDataItemList()[index].getLabel();
  604. }
  605. var availableActions = []; //The set of all availableActions for this context
  606. var fRegisterAction = function fRegisterAction(action) {
  607. availableActions = availableActions.concat(action.getAvailableActions());
  608. _this8._registeredActions[action.getActionId()] = action;
  609. };
  610. return this.getActionsForSlots(slots, mapIndex, selectionObj, options).then(function (actions) {
  611. _.each(actions, fRegisterAction);
  612. slots = slots.length === 1 ? slots[0] : null;
  613. //When there is only one slot in the slots, pass constructToolar the single slot in order to show the toolbar menu title
  614. _this8._constructToolbar(availableActions, position, slots, options);
  615. });
  616. };
  617. /* Constructs the toolbar needed
  618. * @param options : Additional options for the toolbar such as showing only text or icons
  619. */
  620. VisActionHelper.prototype._constructToolbar = function _constructToolbar(availableActions, oPosition, slot, options) {
  621. if (availableActions.length !== 0) {
  622. //Add additional actions for category column selections here..
  623. var position = oPosition || {};
  624. var mapping = slot && slot.mapping;
  625. var toolbarName = mapping ? this._visModel.getLabelByMapping(mapping) : options.toolbarName ? options.toolbarName : undefined;
  626. if (position.bounds) {
  627. var visBounds = this._visModel.ownerWidget.el.getBoundingClientRect();
  628. var visChart_center_x = visBounds.left + visBounds.width / 2;
  629. var visElement_center_x = position.bounds.left + position.bounds.width / 2;
  630. if (visElement_center_x > visChart_center_x) {
  631. // The element is on the right side of the chart, so try to show the Flyout on the right to avoid hiding the chart.
  632. position.placement = 'right auto';
  633. }
  634. }
  635. var eventOptions = {
  636. targetNode: position.targetNode || null,
  637. targetBounds: position.bounds || null,
  638. placement: position.placement || 'left auto',
  639. actions: availableActions,
  640. toolbarName: toolbarName
  641. };
  642. if (options) {
  643. eventOptions.toolbarOptions = options;
  644. }
  645. this.dashboardApi.triggerDashboardEvent('widget:availableActions', eventOptions);
  646. }
  647. };
  648. /**
  649. * Brings up the UI to edit an existing filter
  650. * @param filterId - the Id of the filter to edit.
  651. * @param node - the node to guide placement of the dialog.
  652. */
  653. VisActionHelper.prototype.editLocalFilter = function editLocalFilter(filterId, node) {
  654. var _this9 = this;
  655. var visualization = this.getVisualization();
  656. var filterEntry = _.find(visualization.getLocalFilters().getFilterList(), function (filter) {
  657. return filter.id === filterId;
  658. });
  659. var filterDesc = _.find(visualization.getLocalFilters().getFiltersDesc(), function (filter) {
  660. return filter.dataId === filterId;
  661. });
  662. if (!node || !filterDesc || !filterDesc.editEnabled) {
  663. return Promise.resolve();
  664. }
  665. //If this filterEntry has a matching slot dataItem, load the dataItem filter module and launch the editor with the appropriate slot and index defined.
  666. //otherwise, load the contextFilter module and launch that editor with the metadata column information.
  667. var matchingSlotDataItem = null;
  668. if (filterEntry) {
  669. matchingSlotDataItem = _.find(visualization.getSlots().getMappingInfoList(), function (mapping) {
  670. var match = mapping.dataItem.getColumnId() === filterEntry.columnId;
  671. if (match && filterEntry.aggregationType) {
  672. match = mapping.dataItem.getAggregation() === filterEntry.aggregationType;
  673. }
  674. return match;
  675. });
  676. }
  677. //If there's a matchingSlotDataItem, load the slot filter action, otherwise, load the context filter action.
  678. var actionToLoad = matchingSlotDataItem ? 'dashboard-analytics/visualizations/interactions/FilterAction' : 'dashboard-analytics/visualizations/interactions/FilterDropAction';
  679. return DynamicFileLoader.load([actionToLoad]).then(function (modules) {
  680. var FilterAction = modules[0];
  681. var fAction = matchingSlotDataItem ? _this9.getNewFilterActionForSlot(FilterAction, matchingSlotDataItem.slot, matchingSlotDataItem.indexInSlot) : _this9.getNewFilterActionForContextColumn(FilterAction, visualization.getDataSource().getMetadataColumn(filterEntry.columnId));
  682. return _this9.renderFilterDialog(fAction.getEditorModuleName(), fAction.getViewOptions(), node);
  683. });
  684. };
  685. VisActionHelper.prototype.renderFilterDialog = function renderFilterDialog(sDialogModuleName, viewOptions, node, launchView, backButtonCallback) {
  686. var _this10 = this;
  687. return DynamicFileLoader.load([sDialogModuleName]).then(function (modules) {
  688. var FilterDialog = modules[0];
  689. var view = new FilterDialog(viewOptions);
  690. var preloadDone = view.preload ? view.preload() : Promise.resolve();
  691. var title = viewOptions.title || resources.get('toolbarActionFilter');
  692. return preloadDone.then(_this10._createContext.bind(_this10, view, node, title, launchView, backButtonCallback)).then(view.renderCallBack.bind(view));
  693. });
  694. };
  695. VisActionHelper.prototype.resetActionState = function resetActionState(actionName, slot, index) {
  696. var slotAction = this._getSlotActionByNameOrGroup(slot, actionName, index);
  697. if (slotAction && slotAction.actions && slotAction.actions.reset) {
  698. slotAction.actions.reset(slot.getId(), index);
  699. }
  700. };
  701. VisActionHelper.prototype.showAction = function showAction(actionName, slot, node, index) {
  702. var _this11 = this;
  703. var slotAction = this._getSlotActionByNameOrGroup(slot, actionName, index);
  704. if (slotAction) {
  705. var viewModule = slotAction.view && slotAction.view.module;
  706. return DynamicFileLoader.load([viewModule]).then(function (modules) {
  707. var Dialog = modules[0];
  708. var view = new Dialog({
  709. state: slotAction.view.state,
  710. actions: slotAction.actions
  711. });
  712. return _this11._createContext(view, node, resources.get(slotAction.text));
  713. });
  714. }
  715. return Promise.resolve();
  716. };
  717. VisActionHelper.prototype._getSlotActionByNameOrGroup = function _getSlotActionByNameOrGroup(slot, actionName, index) {
  718. var specs = this.content.getFeature('SlotActions').getSlotActionList(slot.getId(), index);
  719. return specs.find(function (spec) {
  720. return spec.name === actionName || spec.group === actionName;
  721. });
  722. };
  723. VisActionHelper.prototype._createContext = function _createContext(view, node, toolbarLabel, launchView, backButtonCallback) {
  724. var actions = [{
  725. responsive: false,
  726. editable: false,
  727. changedAction: null,
  728. subView: view,
  729. type: 'SubView'
  730. }];
  731. return this._setToolbarForEditor(actions, node, toolbarLabel, launchView, backButtonCallback);
  732. };
  733. VisActionHelper.prototype._setToolbarForEditor = function _setToolbarForEditor(aActions, node, sLabel, launchView, backButtonCallback) {
  734. var _this12 = this;
  735. return DynamicFileLoader.load(['dashboard-analytics/lib/@waca/dashboard-common/dist/ui/AuthoringToolbar']).then(function (modules) {
  736. var Toolbar = modules[0];
  737. if (_this12.toolbar) {
  738. _this12.toolbar.remove();
  739. }
  740. _this12.toolbar = new Toolbar({
  741. container: $('body'),
  742. placement: launchView && launchView.getPlacement ? launchView.getPlacement() : 'right auto',
  743. calculateBoundingRect: true,
  744. launchView: launchView,
  745. backButtonCallback: backButtonCallback
  746. });
  747. if (sLabel) {
  748. _this12.toolbar.setName(sLabel);
  749. }
  750. _this12.toolbar.addItems(aActions);
  751. _this12.toolbar.setSelectionContext([node]);
  752. _this12.toolbar.show();
  753. return _this12.toolbar;
  754. });
  755. };
  756. VisActionHelper.prototype.getVisualization = function getVisualization() {
  757. //@todo After the old visAPI is removed, not need to have this function
  758. //Instead the constructor of this class can just set this.visualization = this.widgetAPI.getFeature('Visualization')
  759. //Right now is is not possible due to deprecated old apis dependencies on
  760. //visAPI.getUITestApi visAPI.and selectData
  761. if (!this.visualization) {
  762. this.visualization = this.content.getFeature('Visualization');
  763. }
  764. return this.visualization;
  765. };
  766. return VisActionHelper;
  767. }();
  768. return VisActionHelper;
  769. });
  770. //# sourceMappingURL=VisActionHelper.js.map