SynchronizeDataModel.js 49 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417
  1. 'use strict';
  2. var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
  3. /*
  4. *+------------------------------------------------------------------------+
  5. *| Licensed Materials - Property of IBM
  6. *| IBM Cognos Products: Dashboard
  7. *| (C) Copyright IBM Corp. 2017, 2020
  8. *|
  9. *| US Government Users Restricted Rights - Use, duplication or disclosure
  10. *| restricted by GSA ADP Schedule Contract with IBM Corp.
  11. *+------------------------------------------------------------------------+
  12. */
  13. define(['../../../lib/@waca/dashboard-common/dist/core/Collection', 'underscore', './SynchronizeDataEntry', './SynchronizeDataHelper', './SynchronizePageContextEntry', './SynchContextHelper', './SynchJoinTable', '../../../widgets/livewidget/query/QueryService', '../../../widgets/livewidget/query/CommonQueryHelper', '../../../filters/pagecontext/PageContextEntry', '../../../filters/FilterMetadataHelper', '../../utils/DatasourceUtil', '../../../lib/@waca/core-client/js/core-client/utils/UniqueId'], function (Collection, _, SynchronizeDataEntry, SynchronizeDataHelper, SynchronizePageContextEntry, SynchContextHelper, SynchJoinTable, QueryService, CommonQueryHelper, PageContextEntry, FilterMetadataHelper, DatasourceUtil, UniqueId) {
  14. /**
  15. * The Synchronize Model implements the boardModelExtension defined in synchronizeData.json
  16. * It is used to persist information about mapped colours etc.
  17. */
  18. var SynchronizeDataModel = Collection.extend({
  19. modelClass: SynchronizeDataEntry,
  20. init: function init(spec, options) {
  21. SynchronizeDataModel.inherited('init', this, arguments);
  22. this.logger = options.logger;
  23. this.boardModel = options.boardModel;
  24. this.dashboardApi = options.dashboardApi;
  25. this.logger = this.dashboardApi.getGlassCoreSvc('.Logger');
  26. this.modules = {};
  27. this.items = {};
  28. this.loadValuesPromises = {};
  29. this.synchronizeDataEntries = {};
  30. this.filterDockItems = {};
  31. this.jointedTablesBrushData = {};
  32. this.widgets = {};
  33. this.eventListener = this.boardModel.on('change:pageContext', this._onChangePageContext, this);
  34. this.eventListenerRemoveLayouts = this.boardModel.on('removeLayouts', this._onRemoveWidget, this);
  35. this.dashboardApi.on('brushing:undoRedo', this._onUndoRedo, this);
  36. this.dashboardApi.on('filterIndicator:removeEntry', this._onRemoveFilterIndicatorEntry, this);
  37. this.dashboardApi.on('widget:renderComplete', this._onRenderComplete, this);
  38. },
  39. destroy: function destroy() {
  40. SynchronizeDataModel.inherited('destroy', this, arguments);
  41. if (this.eventListener) {
  42. this.eventListener.off();
  43. this.eventListener = null;
  44. }
  45. if (this.eventListenerRemoveLayouts) {
  46. this.eventListenerRemoveLayouts.off();
  47. this.eventListenerRemoveLayouts = null;
  48. }
  49. this.widgets = {};
  50. this.dashboardApi.off('brushing:undoRedo', this._onUndoRedo, this);
  51. this.dashboardApi.off('filterIndicator:removeEntry', this._onRemoveFilterIndicatorEntry, this);
  52. this.dashboardApi.off('widget:renderComplete', this._onRenderComplete, this);
  53. },
  54. /**
  55. * Loads the page context when first open the dashboard that has visualization widgets
  56. *
  57. * @param {object} pageContextAPI - the page context API
  58. * @param {object} visAPI - the viz API
  59. *
  60. * @return {object} A promise object
  61. */
  62. loadBrushedContext: function loadBrushedContext(pageContextAPI, visualization, visAPI) {
  63. var _this = this;
  64. if (!pageContextAPI || !visAPI) {
  65. return Promise.resolve();
  66. }
  67. var dataItems = visualization.getSlots().getDataItemList();
  68. if (!dataItems || dataItems.length === 0) {
  69. return Promise.resolve();
  70. }
  71. var projectedItems = SynchContextHelper.createSynchProjectedItems(visAPI.getModule(), dataItems.map(function (dataItem) {
  72. return {
  73. getItemId: function getItemId() {
  74. return dataItem.getColumnId();
  75. },
  76. getLabel: function getLabel() {
  77. return dataItem.getLabel();
  78. }
  79. };
  80. }));
  81. var pageContextMatchParams = {
  82. origin: 'visualization',
  83. scope: visAPI.getScope(),
  84. eventGroupId: visAPI.getEventGroupId()
  85. };
  86. var pageContextItems = pageContextAPI.getPageContextItems(pageContextMatchParams);
  87. if (pageContextItems.length === 0) {
  88. return Promise.resolve();
  89. }
  90. var promises = [];
  91. pageContextItems.forEach(function (pageContextItem) {
  92. var spec = pageContextItem.getPageContextSpec();
  93. var options = {
  94. module: visAPI.getModule(),
  95. itemIds: _.pluck(spec.hierarchies, 'hierarchyUniqueName'),
  96. data: spec.tupleSet
  97. };
  98. promises.push(_this._synchJointedTables(projectedItems, options));
  99. });
  100. return Promise.all(promises);
  101. },
  102. /**
  103. * Retrieve and return the found page context
  104. * This function is ensuring projected data items in joint tables have correct brushed values in vis
  105. *
  106. * @param {string} dataSourceId - the data source id
  107. * @param {object} pageContextAPI - the page context API
  108. * @param {string} itemId - the data item to search for a corresponding joined table tuple value
  109. * @param {string} pageContextMatchParams - the page context scope to search for page context item
  110. *
  111. * @return {object} page context entry API object if one found else return undefined
  112. */
  113. getPageContextItem: function getPageContextItem(dataSourceId, pageContextAPI, itemId, pageContextMatchParams) {
  114. var pageContextItem = pageContextAPI.getPageContextItem(_.extend({
  115. itemId: itemId
  116. }, pageContextMatchParams));
  117. if (pageContextItem) {
  118. //Found it so return
  119. return pageContextItem;
  120. }
  121. var pageContextItems = pageContextAPI.getPageContextItems({
  122. eventGroupId: pageContextMatchParams.eventGroupId,
  123. origin: pageContextMatchParams.origin,
  124. scope: pageContextMatchParams.scope
  125. });
  126. var synchColumn = void 0;
  127. var synchModule = this.jointedTablesBrushData[dataSourceId];
  128. if (synchModule) {
  129. synchColumn = synchModule.getColumn(itemId);
  130. }
  131. if (!synchColumn || pageContextItems.length === 0) {
  132. return undefined;
  133. }
  134. for (var idx = 0; idx < pageContextItems.length; idx++) {
  135. var _pageContextItem = pageContextItems[idx];
  136. var result = this._findJoinedTableTupleValue(_pageContextItem, synchColumn);
  137. if (result) {
  138. //Clone the spec so not to modify the original page context entry model
  139. var newSpec = JSON.stringify(_pageContextItem.getPageContextSpec());
  140. newSpec = JSON.parse(newSpec);
  141. newSpec.hierarchies = [{ hierarchyUniqueName: itemId }];
  142. newSpec.tupleSet = result.value;
  143. return new PageContextEntry(newSpec).getAPI();
  144. }
  145. }
  146. },
  147. /**
  148. * Find and return a matched tuple value if tables are joined
  149. * @private
  150. *
  151. * @param {object} pageContextItem - the pageContextItem to find a jointed table tuple value
  152. * @param {object} synchColumn - the column to look up for the jointed table tuple value
  153. *
  154. * @return and object containing the result if one found else return undefined
  155. */
  156. _findJoinedTableTupleValue: function _findJoinedTableTupleValue(pageContextItem, synchColumn) {
  157. var spec = pageContextItem.getPageContextSpec();
  158. var itemIds = _.pluck(spec.hierarchies, 'hierarchyUniqueName');
  159. var data = this._isArrayOfArray(spec.tupleSet) ? spec.tupleSet : [spec.tupleSet];
  160. var matchedItemId = void 0;
  161. var matchedTuples = void 0;
  162. var _loop = function _loop(idx) {
  163. data.forEach(function (tupleSet) {
  164. tupleSet.forEach(function (tuple, currentIdx) {
  165. if (idx === currentIdx) {
  166. matchedItemId = itemIds[idx];
  167. var foundTuple = synchColumn.getJointTupleValue(itemIds[idx], tuple);
  168. if (foundTuple) {
  169. if (!matchedTuples) {
  170. matchedTuples = [];
  171. }
  172. //Array array of values
  173. matchedTuples.push([foundTuple]);
  174. }
  175. }
  176. });
  177. });
  178. if (matchedTuples) {
  179. return {
  180. v: {
  181. itemId: matchedItemId,
  182. value: matchedTuples
  183. }
  184. };
  185. }
  186. };
  187. for (var idx = 0; idx < itemIds.length; idx++) {
  188. var _ret = _loop(idx);
  189. if ((typeof _ret === 'undefined' ? 'undefined' : _typeof(_ret)) === "object") return _ret.v;
  190. }
  191. },
  192. /**
  193. * Call to handle a brushing selection to synchronize data across data sources
  194. *
  195. * @param {object} selection - the selected brushing context
  196. * @param {object} options - the selected brushing options
  197. * Example of brushing spec
  198. * {
  199. * "origin": "visualization",
  200. * "eventSourceId": "model0000015fef997299_00000000",
  201. * "sourceId": "model0000015fef992bd7_00000001",
  202. * "scope": "page1",
  203. * "eventGroupId": "page1:1",
  204. * "hierarchies": [
  205. * {}
  206. * ],
  207. * "hierarchyUniqueNames": [
  208. * null
  209. * ]
  210. *}
  211. * @return {object} A resolved promise after brushing is handled to synchronize the data across data sources
  212. */
  213. handleBrushingSelection: function handleBrushingSelection() {
  214. var _this2 = this;
  215. var selection = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  216. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  217. var synchOptions = options.synchOptions || {};
  218. return this._doBrushingSelection(selection, options).then(function (results) {
  219. if (results) {
  220. results.arguments = {
  221. command: selection.command,
  222. pending: selection.pending
  223. };
  224. var append = selection.slotsToClear && selection.slotsToClear.length === 0;
  225. selection.synchDataId = UniqueId.get('');
  226. (results.data || []).map(function (item) {
  227. item.synchDataId = selection.synchDataId;
  228. item.append = append;
  229. item.getNetSlotsToClear = SynchContextHelper.getNetSlotsToClearBrushing;
  230. return item;
  231. });
  232. _this2.dashboardApi.triggerDashboardEvent('synchronizeData:applyBrushing', results);
  233. //Is this a non-projected items clear action?
  234. var newSelectionIsEmpty = synchOptions.newSelectionIsEmpty(selection.tuple);
  235. if (newSelectionIsEmpty && selection.slotsToClear.length > 0) {
  236. selection.slotsToClear = SynchContextHelper.getNetSlotsToClearBrushing({
  237. pageContext: synchOptions.pageContext,
  238. selector: {
  239. origin: 'visualization',
  240. scope: selection.brushingSpec.scope,
  241. eventGroupId: selection.brushingSpec.eventGroupId,
  242. sourceId: selection.brushingSpec.sourceId
  243. },
  244. sourceModule: results.brushSource.getModule(),
  245. targetModule: results.brushSource.getModule(),
  246. dataSlots: selection.slotsToClear,
  247. itemIds: selection.itemIds,
  248. tableNames: results.brushSource.getTableRef(),
  249. newSectionFromSource: true,
  250. newSelectionIsEmpty: true
  251. });
  252. }
  253. }
  254. return results;
  255. }).finally(function () {
  256. _this2._resetData();
  257. });
  258. },
  259. _resetData: function _resetData() {
  260. this.modules = {};
  261. this.items = {};
  262. this.loadValuesPromises = {};
  263. this.synchronizeDataEntries = {};
  264. },
  265. _doBrushingSelection: function _doBrushingSelection() {
  266. var _this3 = this;
  267. var selection = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  268. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  269. //Synchronizaton of data does not support right mouse click (which is for pending = true)
  270. if (selection.pending) {
  271. return Promise.resolve();
  272. }
  273. var brushingSpec = selection.brushingSpec;
  274. this._resetBrushing(options);
  275. var context = {
  276. scope: {
  277. origin: 'visualization',
  278. scope: brushingSpec.scope,
  279. eventGroupId: brushingSpec.eventGroupId,
  280. eventSourceId: brushingSpec.eventSourceId
  281. },
  282. itemIds: selection.itemIds,
  283. tuple: selection.tuple
  284. };
  285. return this._getModulesWithinSameEventGroup(brushingSpec.sourceId, brushingSpec.eventGroupId, brushingSpec.eventSourceId).then(function (brushingEntries) {
  286. if (brushingEntries) {
  287. var results = _this3._synchBrushingSelection(context, options, brushingEntries);
  288. if (results) {
  289. results.scope = context.scope;
  290. }
  291. brushingSpec.tableRef = brushingEntries.brushSource.getTableRef();
  292. return results;
  293. }
  294. });
  295. },
  296. _getNetPageContextItems: function _getNetPageContextItems(scope, eventGroupId, origin, synchDataId) {
  297. var paramsForPageContext = {
  298. scope: scope,
  299. eventGroupId: eventGroupId,
  300. origin: origin || 'visualization'
  301. };
  302. if (synchDataId) {
  303. paramsForPageContext.synchDataId = synchDataId;
  304. }
  305. return this.pageContext ? this.pageContext.getNetPageContextItems(paramsForPageContext) : [];
  306. },
  307. /**
  308. * Call to synchronize data for all data sources within same event group id
  309. *
  310. * @param {string} sourceId - the current selected souce id
  311. * @param {string} eventGroupId - the current selected event group id
  312. * @param {string} eventSourceId - where the event originating from
  313. *
  314. * @param {object} return all data source modules base on the specified values of sourceId and eventGroupId
  315. */
  316. _getModulesWithinSameEventGroup: function _getModulesWithinSameEventGroup(sourceId, eventGroupId, eventSourceId) {
  317. var _this4 = this;
  318. var dataViewModelMap = {};
  319. var dataViewModels = function dataViewModels(widget) {
  320. //return the fist on for now
  321. var models;
  322. var data = widget ? widget.data : null;
  323. if (data) {
  324. models = data.dataViews;
  325. }
  326. return models;
  327. };
  328. var dashboardInternal = this.dashboardApi.getFeature('internal');
  329. var sources = [];
  330. var boardModel = dashboardInternal.getBoardModel();
  331. this.pageContext = this.pageContext || boardModel.get('pageContext').getAPI();
  332. var eventGroups = boardModel.get('eventGroups');
  333. var eventGroup = eventGroups.get(eventGroupId);
  334. if (eventGroup) {
  335. var widgetIds = eventGroup.get('widgetIds');
  336. widgetIds.forEach(function (widgetId) {
  337. var widget = boardModel.findWidgetById(widgetId);
  338. var models = dataViewModels(widget);
  339. if (models) {
  340. models.forEach(function (model) {
  341. if (model.modelRef) {
  342. if (sources.indexOf(model.modelRef) === -1) {
  343. sources.push(model.modelRef);
  344. }
  345. if (!dataViewModelMap[model.modelRef]) {
  346. dataViewModelMap[model.modelRef] = [];
  347. }
  348. if (model.dataItems) {
  349. dataViewModelMap[model.modelRef].push({ widgetId: widgetId, projectedItems: model.dataItems.map(function (dataItemModel) {
  350. return {
  351. getItemId: function getItemId() {
  352. return dataItemModel.itemId;
  353. },
  354. getLabel: function getLabel() {
  355. return dataItemModel.itemLabel;
  356. }
  357. };
  358. }) });
  359. }
  360. }
  361. });
  362. }
  363. });
  364. }
  365. var dataSources = this.dashboardApi.getFeature('dataSources.deprecated');
  366. var promises = [];
  367. _.each(sources, function (id) {
  368. if (this.modules[id]) {
  369. promises.push(Promise.resolve(this.modules[id]));
  370. } else {
  371. // datasources.depreacted is not a core feature. Module is only required for synchronized data, unfortunately
  372. // the current brushing is tightly coupled with synch'd data.
  373. // @todo synchronized data should be supported as a query modifier, instead of overcomplicating the page context
  374. promises.push(dataSources ? dataSources.getModule(id) : Promise.resolve(null));
  375. }
  376. }, this);
  377. return Promise.all(promises).then(function (results) {
  378. _.each(results, function (module) {
  379. // module is not required when synchronized data is not supported
  380. if (module && !(module.getSourceId() in this.modules)) {
  381. this.modules[module.getSourceId()] = module; //add to cache
  382. }
  383. }, this);
  384. }.bind(this)).then(function () {
  385. return _this4._createSynchBrushEntries(eventGroupId, eventSourceId, dataViewModelMap);
  386. });
  387. },
  388. _createBrushEntry: function _createBrushEntry(eventGroupId, dataViewModelMap, eventSourceId, callback) {
  389. var _this5 = this;
  390. var newBrushEntry = void 0;
  391. for (var modelRef in dataViewModelMap) {
  392. var dataViewModel = dataViewModelMap[modelRef];
  393. if (!dataViewModel.length) {
  394. continue;
  395. }
  396. _.find(dataViewModel, function (dataView) {
  397. if (dataView.projectedItems.length) {
  398. var module = _this5.modules[modelRef];
  399. if (!eventSourceId || dataView.widgetId === eventSourceId) {
  400. newBrushEntry = callback(modelRef, eventGroupId, module, dataView.projectedItems, eventSourceId);
  401. return true;
  402. }
  403. }
  404. return false;
  405. });
  406. if (newBrushEntry) {
  407. break;
  408. }
  409. }
  410. return newBrushEntry;
  411. },
  412. _createSynchBrushEntries: function _createSynchBrushEntries(eventGroupId, eventSourceId, dataViewModelMap) {
  413. var _this6 = this;
  414. var brushEntries = [];
  415. var instantianNewBrushEntry = function instantianNewBrushEntry(modelRef, eventGroupId, module, projectedItem, widgetId) {
  416. var fromObject = {
  417. getSourceId: function getSourceId() {
  418. return modelRef;
  419. },
  420. getEventGroupId: function getEventGroupId() {
  421. return eventGroupId;
  422. },
  423. getEventSourceId: function getEventSourceId() {
  424. return projectedItem.widgetId;
  425. }
  426. };
  427. return SynchContextHelper.createNewSynchContext({
  428. fromObject: fromObject,
  429. module: module,
  430. projectedItems: projectedItem,
  431. widgetId: widgetId
  432. });
  433. };
  434. /**
  435. * dataViewModelMap = {
  436. * modelRef: [{
  437. * widgetId: widgetId,
  438. * projectedItems: [...]
  439. * }
  440. *
  441. */
  442. var synchSourceContext = this._createBrushEntry(eventGroupId, dataViewModelMap, eventSourceId, instantianNewBrushEntry);
  443. if (!synchSourceContext) {
  444. return synchSourceContext;
  445. }
  446. for (var modelRef in dataViewModelMap) {
  447. var dataViewModel = dataViewModelMap[modelRef];
  448. if (!dataViewModel.length) {
  449. continue;
  450. }
  451. dataViewModel.forEach(function (dataView) {
  452. if (dataView.projectedItems.length && dataView.widgetId !== eventSourceId) {
  453. var module = _this6.modules[modelRef];
  454. var newBrushEntry = instantianNewBrushEntry(modelRef, eventGroupId, module, dataView.projectedItems, dataView.widgetId);
  455. if (newBrushEntry) {
  456. if (SynchContextHelper.haveJoinedTables(synchSourceContext, newBrushEntry)) {
  457. synchSourceContext.mergeProjectedItems(newBrushEntry);
  458. synchSourceContext.addWidgetId(dataView.widgetId);
  459. } else {
  460. var existedEntry = _.find(brushEntries, function (entry) {
  461. return SynchContextHelper.haveJoinedTables(entry, newBrushEntry);
  462. });
  463. if (existedEntry) {
  464. existedEntry.mergeProjectedItems(newBrushEntry);
  465. existedEntry.addWidgetId(dataView.widgetId);
  466. } else {
  467. brushEntries.push(newBrushEntry);
  468. }
  469. }
  470. }
  471. }
  472. });
  473. }
  474. return { brushSource: synchSourceContext, entries: brushEntries };
  475. },
  476. /**
  477. * Synchronize joined table tuple values match by column names in jointed tables
  478. *
  479. * @param {array} projectedItems - array of viz projected items to synch tuple values
  480. * @param {object} options - options to synch joined table values
  481. *
  482. * @return {object} A promise object
  483. */
  484. _synchJointedTables: function _synchJointedTables(projectedItems) {
  485. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  486. var module = options.module;
  487. var itemIds = options.itemIds;
  488. if (!projectedItems || projectedItems.length === 0 || !itemIds || itemIds.length == 0 || !options.data || options.data.length === 0) {
  489. return Promise.resolve();
  490. }
  491. var promises = [];
  492. var isArrayOfArray = this._isArrayOfArray(options.data);
  493. var data = isArrayOfArray ? options.data : [options.data];
  494. itemIds.forEach(function (itemId, idx) {
  495. var handlers = {
  496. loadValues: this._loadValues.bind(this)
  497. };
  498. var synchModule = this.jointedTablesBrushData[module.getSourceId()] = this.jointedTablesBrushData[module.getSourceId()] || new SynchJoinTable(module, { handlers: handlers });
  499. var synchColumn = synchModule.getColumn(itemId);
  500. if (synchColumn) {
  501. projectedItems.forEach(function (projectedItem) {
  502. if (synchColumn.getItemId() !== projectedItem.getItemId() && synchColumn.matchedColumnByName(projectedItem.getItemId())) {
  503. var synchToFindMatchedValue = synchModule.getColumn(projectedItem.getItemId());
  504. data.forEach(function (tupleSet) {
  505. tupleSet.forEach(function (tuple, currentIdx) {
  506. if (idx === currentIdx) {
  507. promises.push(synchToFindMatchedValue.matchJointedTableValue(synchColumn, tuple));
  508. }
  509. });
  510. });
  511. }
  512. });
  513. }
  514. }.bind(this));
  515. return Promise.all(promises);
  516. },
  517. _clearBrushing: function _clearBrushing(tuple) {
  518. return !tuple || !_.flatten(tuple).length;
  519. },
  520. _synchClearBrushing: function _synchClearBrushing() {
  521. var context = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  522. var _this7 = this;
  523. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  524. var brushingEntries = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
  525. if (!brushingEntries.entries || brushingEntries.entries.length === 0 || !this._clearBrushing(context.tuple)) {
  526. return null;
  527. }
  528. var results = [];
  529. var cloneOptions = SynchronizeDataHelper.clone(options);
  530. var payloadData = cloneOptions.payloadData = cloneOptions.payloadData || {};
  531. payloadData.brushedSourceModule = brushingEntries.brushSource.getModule();
  532. payloadData.ignorePageContextChanged = true;
  533. var entries = [brushingEntries.brushSource].concat(brushingEntries.entries);
  534. entries.forEach(function (entry) {
  535. results.push({
  536. sourceId: entry.getSourceId(),
  537. tableRef: entry.getTableRef(),
  538. itemIds: context.itemIds,
  539. sourceModule: brushingEntries.brushSource.getModule(),
  540. tuple: context.tuple,
  541. options: cloneOptions,
  542. ignoreSynchBrushData: _this7._getIgnoreSynchBrushDataWidgetIdList(entry.getWidgetIds())
  543. });
  544. });
  545. return results.length > 0 ? { data: results, brushSource: brushingEntries.brushSource } : null;
  546. },
  547. /**
  548. * Internal handling of brushing to synchronize the data
  549. *
  550. * @param {array} itemIds - array of selected data items (e.g. [Product line, Order method])
  551. * @param {array} tuple - the selected tuple values (e.g. [Camping Equipment, Fax])
  552. * @param {object} options - the selected brushing options
  553. * @param {object} brushingEntries - the brushing entries to aply brushing
  554. *
  555. * @return {object} an object contain the synchronize brushing data
  556. */
  557. _synchBrushingSelection: function _synchBrushingSelection(context) {
  558. var _this8 = this;
  559. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  560. var brushingEntries = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
  561. if (!brushingEntries.entries || brushingEntries.entries.length === 0) {
  562. return null;
  563. }
  564. if (this._clearBrushing(context.tuple)) {
  565. return this._synchClearBrushing(context, options, brushingEntries);
  566. }
  567. var brushigData = this._constructBrushingData(context, brushingEntries);
  568. var results = [];
  569. _.each(brushigData.entries, function (entry) {
  570. results.push(this._synchEntry(entry.pageContextEntry, { source: brushigData.source, target: entry.targetSynchDataEntry, brushing: true, scope: context.scope }));
  571. }, this);
  572. if (results.length > 0) {
  573. var synchBrushingData;
  574. var cloneOptions = SynchronizeDataHelper.clone(options);
  575. var payloadData = cloneOptions.payloadData = cloneOptions.payloadData || {};
  576. payloadData.brushedSourceModule = brushingEntries.brushSource.getModule();
  577. payloadData.ignorePageContextChanged = true;
  578. results.forEach(function (result, index) {
  579. if (result) {
  580. if (!synchBrushingData) {
  581. synchBrushingData = [];
  582. }
  583. var entry = brushigData.entries[index].pageContextEntry;
  584. var targetSynchDataEntry = brushigData.entries[index].targetSynchDataEntry;
  585. var isArrayOfArray = brushigData.entries[index].isArrayOfArray;
  586. var tupleSet = isArrayOfArray ? entry.tupleSet : entry.tupleSet.length > 0 ? entry.tupleSet[0] : [];
  587. var value = {
  588. sourceId: targetSynchDataEntry.getSourceId(),
  589. tableRef: targetSynchDataEntry.getTableRef(),
  590. itemIds: _.pluck(entry.hierarchies, 'hierarchyUniqueName'),
  591. sourceModule: brushingEntries.brushSource.getModule(),
  592. tuple: tupleSet,
  593. options: cloneOptions,
  594. ignoreSynchBrushData: _this8._getIgnoreSynchBrushDataWidgetIdList(targetSynchDataEntry.getWidgetIds())
  595. };
  596. synchBrushingData.push(value);
  597. }
  598. }, this);
  599. return { brushSource: brushingEntries.brushSource, data: synchBrushingData };
  600. }
  601. },
  602. /**
  603. * Return an array of widget ids that should ignore synchronize brushed data
  604. *
  605. * @param {array} widgetIds - array of widget ids to process
  606. *
  607. * @return {array} List of widget ids that must ignore synchronize brushed dataś
  608. */
  609. _getIgnoreSynchBrushDataWidgetIdList: function _getIgnoreSynchBrushDataWidgetIdList() {
  610. var _this9 = this;
  611. var widgetIds = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
  612. var edgeSelectionIsTrue = [];
  613. var edgeSelectionIsFalse = [];
  614. widgetIds.forEach(function (id) {
  615. var widget = _this9.widgets[id];
  616. if (widget) {
  617. if (widget.getEdgeSelection()) {
  618. edgeSelectionIsTrue.push(widget.getId());
  619. } else {
  620. edgeSelectionIsFalse.push(widget.getId());
  621. }
  622. }
  623. });
  624. //Non edge selection brushing type take precedence over edge selection type.
  625. //Currenty summary, table, grid have edgeSection type equals to true, the rest have this flag equal to false
  626. //During synchronize brushing, edge selection type is ignoring synch brush event to avoid creating edge brush event if there is at least one non-edge selection vis
  627. return edgeSelectionIsFalse.length ? edgeSelectionIsTrue : [];
  628. },
  629. /**
  630. * Construct brushing data to synchronize across data sources
  631. *
  632. * @param {array} itemIds - array of selected data items (e.g. [Product line, Order method])
  633. * @param {array} tuple - the selected tuple values (e.g. [Camping Equipment, Fax])
  634. *
  635. * @param {object} return an object containing the synchronize brushing data
  636. */
  637. _constructBrushingData: function _constructBrushingData() {
  638. var context = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  639. var brushingData = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  640. var dataEntry = this._getSynchDataEntry(brushingData.brushSource.getSourceId(), context.scope, brushingData.brushSource.getTableRef());
  641. brushingData.brushSource.setSynchDataEntry(dataEntry);
  642. var targets = this._getTargetBrushingEntries(brushingData.entries, context.scope);
  643. return {
  644. source: brushingData.brushSource.getAPI(),
  645. entries: this._constructPageContextEntries(context.itemIds, context.tuple, brushingData.brushSource, targets)
  646. };
  647. },
  648. /**
  649. * Construct page context entries for brushing
  650. *
  651. * @param {array} itemIds - array of selected data items (e.g. [Product line, Order method])
  652. * @param {array} tuple - the selected tuple values (e.g. [Camping Equipment, Fax])
  653. * @param {object} source - the source brushing
  654. * @param {object} targets - an object containing the set of target modules
  655. *
  656. * @param {array} return an array of page context entries
  657. */
  658. _constructPageContextEntries: function _constructPageContextEntries(itemIds, tuple, source, targets) {
  659. var _this10 = this;
  660. var entries = [];
  661. _.each(targets, function (target) {
  662. var isArrayOfArray = _this10._isArrayOfArray(tuple);
  663. var clonedTuple = SynchronizeDataHelper.clone(tuple);
  664. var pageContextEntry = {
  665. hierarchies: _this10._getHierarchies(itemIds),
  666. tupleSet: isArrayOfArray ? clonedTuple : [clonedTuple],
  667. sourceId: source.getSourceId()
  668. };
  669. entries.push({ pageContextEntry: pageContextEntry, targetSynchDataEntry: target, isArrayOfArray: isArrayOfArray });
  670. });
  671. return entries;
  672. },
  673. _getHierarchies: function _getHierarchies(itemIds) {
  674. if (Array.isArray(itemIds)) {
  675. return _.map(itemIds, function (itemId) {
  676. return { hierarchyUniqueName: itemId };
  677. });
  678. } else if (_.isString(itemIds)) {
  679. return [{ hierarchyUniqueName: itemIds }];
  680. } else {
  681. return [];
  682. }
  683. },
  684. _isArrayOfArray: function _isArrayOfArray(tuple) {
  685. return _.isArray(tuple) && tuple.length > 0 && _.isArray(tuple[0]);
  686. },
  687. synchTupleSet: function synchTupleSet(module, itemIds, tupleSet) {
  688. var synched = false;
  689. var cloneTupleSet = SynchronizeDataHelper.clone(tupleSet);
  690. _.each(cloneTupleSet, function (tuple) {
  691. _.each(tuple, function (value, index) {
  692. var itemId = itemIds[index].hierarchyUniqueName;
  693. var values = this._getValues(module.getSourceId(), itemId) || [];
  694. if (SynchronizeDataHelper.matchItemByValue(values, value)) {
  695. synched = true;
  696. }
  697. }, this);
  698. }, this);
  699. return synched ? cloneTupleSet : tupleSet;
  700. },
  701. /**
  702. * @override
  703. */
  704. toJSON: function toJSON() {
  705. //1. clone models
  706. var modelMap = {};
  707. this.each(function (model) {
  708. if (model.hasSynchronizeData()) {
  709. var spec = model.toJSON();
  710. var cloneModel = new SynchronizeDataEntry(spec);
  711. modelMap[SynchContextHelper.getKey(cloneModel.getSourceId(), cloneModel.getTableRef())] = cloneModel;
  712. }
  713. }.bind(this));
  714. //2. merge entries to remove duplicates
  715. _.each(modelMap, function (model) {
  716. SynchronizeDataHelper.mergeDuplicates(model.getSourceId(), model.getTableRef(), model.getSynchronizeItems(), modelMap);
  717. });
  718. //3. Remove ones not needed
  719. _.each(modelMap, function (model) {
  720. if (!model.hasSynchronizeData()) {
  721. delete modelMap[SynchContextHelper.getKey(model.getSourceId(), model.getTableRef())];
  722. }
  723. });
  724. return _.map(modelMap, function (model) {
  725. return model.toJSON();
  726. });
  727. },
  728. /**
  729. * Returns a boolean indicating whether the column name has been referenced in filter dock.
  730. * The column name is matched in all data sources and return true when there is a first matched column name in a data source
  731. *
  732. * @param {object} scopeToCheck - the scope contains the set of data sources to search for a match of column name
  733. * @param {object} column - the column whose name is being search to match
  734. *
  735. * @return {boolean} true if there is a match else return false
  736. */
  737. _hasFilterDockItemMatchByColumnName: function _hasFilterDockItemMatchByColumnName(scopeToCheck, column) {
  738. var label = column.getLabel();
  739. var hasFilterDockItem = false;
  740. for (var sourceIdToCheck in scopeToCheck) {
  741. var filterDockItem = _.find(scopeToCheck[sourceIdToCheck], function (item) {
  742. return item.getItemName() === label;
  743. });
  744. if (filterDockItem) {
  745. hasFilterDockItem = true;
  746. break;
  747. }
  748. }
  749. return hasFilterDockItem;
  750. },
  751. /**
  752. * Add/remove a column from filter dock
  753. *
  754. * @param {object} item - the filter dock item being added/deleted from filter dock
  755. * @param {string} scope - the scope (global/current tab) where the item is being added/deleted
  756. * @param {boolean} isAdding - true when the item is being added
  757. */
  758. onAddRemoveFilterItem: function onAddRemoveFilterItem(item, scope, isAdding) {
  759. if (item && scope) {
  760. var currentScope = this.filterDockItems[scope] = this.filterDockItems[scope] || {};
  761. var currentSource = currentScope[item.getSourceId()] = currentScope[item.getSourceId()] || {};
  762. if (isAdding) {
  763. currentSource[item.getItemId()] = currentSource[item.getItemId()] || item;
  764. } else {
  765. delete currentSource[item.getItemId()];
  766. }
  767. }
  768. },
  769. /**
  770. * Internal api to handle the synchronization of data for the pageContextEntry
  771. *
  772. * @param {object} pageContextEntry - the page context entry to synchronize data
  773. * @param {object} options.source - the SynchronizeDataEntry to synchronize data from
  774. * @param {object} options.target - the SynchronizeDataEntry to synchronize data to
  775. *
  776. * @return {boolean} Return the synchronized data for the pageContextEntry
  777. */
  778. _synchEntry: function _synchEntry(pageContextEntry) {
  779. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  780. //clone here since removing collection items on the fly, done to not affect the iterating over collection process
  781. var results = [];
  782. var cloneHierarchies = SynchronizeDataHelper.clone(pageContextEntry.hierarchies);
  783. _.each(cloneHierarchies, function (hierarchy, index) {
  784. var context = {
  785. id: hierarchy.hierarchyUniqueName,
  786. tupleSet: pageContextEntry.tupleSet,
  787. conditions: pageContextEntry.conditions,
  788. index: index,
  789. source: options.source,
  790. target: options.target,
  791. scope: options.scope,
  792. origin: options.origin,
  793. brushing: options.brushing
  794. };
  795. results.push(this._synchItemUseValues(pageContextEntry.hierarchies, context));
  796. }, this);
  797. if (results.length > 0) {
  798. var applySynchronized = false;
  799. var synchedHierarchies = [];
  800. var synchedTupleSet = [];
  801. results.forEach(function (isSynchronized, hierarchyIdx) {
  802. var result = SynchronizeDataHelper.isBoolean(isSynchronized) ? isSynchronized : false;
  803. if (result) {
  804. if (!applySynchronized) {
  805. applySynchronized = result;
  806. }
  807. synchedHierarchies.push(pageContextEntry.hierarchies[hierarchyIdx]);
  808. pageContextEntry.tupleSet.forEach(function (tuple, tupleSetIdx) {
  809. var resultTuple = void 0;
  810. if (tupleSetIdx < synchedTupleSet.length) {
  811. resultTuple = synchedTupleSet[tupleSetIdx];
  812. } else {
  813. resultTuple = [];
  814. synchedTupleSet.push(resultTuple);
  815. }
  816. tuple.forEach(function (value, tupleIdx) {
  817. if (hierarchyIdx === tupleIdx) {
  818. resultTuple.push(value);
  819. }
  820. });
  821. });
  822. }
  823. });
  824. pageContextEntry.hierarchies = synchedHierarchies;
  825. pageContextEntry.tupleSet = synchedTupleSet;
  826. if (applySynchronized) {
  827. pageContextEntry.sourceId = options.target.getSourceId();
  828. }
  829. return applySynchronized;
  830. }
  831. return false;
  832. },
  833. /**
  834. * Internal api to handle the synchronization of data for the hierarchies
  835. *
  836. * @param {object} hierarchies - the collection of hierarchies to synchronize data to
  837. * @param {string} options.id - the data item use value to synchronize data from
  838. * @param {object} options.tupleSet - the tupleSet to synchronize data to
  839. * @param {object} options.source - the source to synchronize data from
  840. * @param {object} options.target - the target to synchronize data to
  841. *
  842. * @return {boolean} the synchronized boolean flag indicating whether the hierarchies had been synchronized or not
  843. */
  844. _synchItemUseValues: function _synchItemUseValues(hierarchies, options) {
  845. options = options || {};
  846. var source = options.source;
  847. var target = options.target;
  848. var matchedColumn;
  849. var sourceModule = this.modules[source.getSourceId()];
  850. var targetModule = this.modules[target.getSourceId()];
  851. var synchSource = source.getSynchronizeItemId(options.id, target.getSourceId(), target.getTableRef());
  852. if (synchSource) {
  853. matchedColumn = targetModule.getMetadataColumn(synchSource.itemId);
  854. } else {
  855. var column = sourceModule.getMetadataColumn(options.id);
  856. if (column) {
  857. var context = {
  858. projectedItems: target.getProjectedItems()
  859. };
  860. matchedColumn = SynchronizeDataHelper.matchColumByName(sourceModule, targetModule, column, context);
  861. }
  862. }
  863. if (!matchedColumn) {
  864. return Promise.resolve(false);
  865. }
  866. this._synchConditions(options.conditions, matchedColumn);
  867. var isSynchronized = options.conditions ? true : this._synchByValues(targetModule, {
  868. sourceColumn: source.getModule().getMetadataColumn(options.id),
  869. targetColumn: matchedColumn,
  870. hierarchies: hierarchies,
  871. tupleSet: options.tupleSet,
  872. conditions: options.conditions,
  873. currentIndex: options.index,
  874. scope: options.scope,
  875. origin: options.origin,
  876. brushing: options.brushing
  877. });
  878. if (isSynchronized) {
  879. hierarchies[options.index].hierarchyUniqueName = matchedColumn.getId();
  880. if (!synchSource) {
  881. source.addSynchronizeItem(options.id, target.getSourceId(), matchedColumn.getId(), target.getTableRef());
  882. }
  883. target.addSynchronizeItem(matchedColumn.getId(), source.getSourceId(), options.id, source.getTableRef());
  884. this._addSynchronizeDataEntry(source);
  885. this._addSynchronizeDataEntry(target);
  886. }
  887. return isSynchronized;
  888. },
  889. /**
  890. * Internal api to handle the synchronization of data for the conditions
  891. *
  892. * @param {string} options.targetColumn - the target metadata column to synch data to
  893. * @param {array} options.conditions - the hierarchies to synch data to
  894. *
  895. * @return {object} A promise resolved to return the synchronized data for the conditions
  896. */
  897. _synchConditions: function _synchConditions(conditions, targetColumn) {
  898. if (conditions && targetColumn) {
  899. _.each(conditions, function (condition) {
  900. var attributeUniqueNames = condition.attributeUniqueNames;
  901. if (_.isArray(attributeUniqueNames) && attributeUniqueNames.length > 0) {
  902. attributeUniqueNames[0] = targetColumn.getId();
  903. }
  904. });
  905. }
  906. },
  907. _emptyTupleSet: function _emptyTupleSet(tupleSet) {
  908. return !(tupleSet && !!tupleSet.length && tupleSet[0] && !!tupleSet[0].length);
  909. },
  910. /**
  911. * Internal api to handle the synchronization of data for the hierarchies
  912. *
  913. * @param {object} targetModule - the target module to synch data to
  914. * @param {string} options.targetColumn - the target metadata column to synch data to
  915. * @param {array} options.hierarchies - the hierarchies to synch data to
  916. * @param {array} options.tupleSet - the tuple set to synch data to
  917. * @param {number} options.currentIndex - the current index to the hierarchies array
  918. *
  919. * @return {boolean} Return the synchronized data for the hierarchies and tupleSet
  920. */
  921. _synchByValues: function _synchByValues(targetModule) {
  922. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  923. var tupleSet = options.tupleSet;
  924. /*Note added January 17, 2019
  925. * ============================
  926. * Synchronize data between different tables had been wired off for other types of datasource such as package. Currently only flat file types for flat file types within modules are supported.
  927. * Synchronize data between Global/local filters are also wired off as well. Since for filters users can define any values to filter where these values may be not be in the data source, this is a non-issue here as well
  928. *
  929. * Thefore what is left here is synchronize between brushing which requires looking at the data in the data source.
  930. */
  931. if (options.brushing && this._emptyTupleSet(tupleSet)) {
  932. return Promise.resolve(true);
  933. }
  934. var currentIndex = options.currentIndex;
  935. var isSynchronized = false;
  936. var cloneTupleSet = SynchronizeDataHelper.clone(tupleSet);
  937. var matchTupleSet = function matchTupleSet() {
  938. cloneTupleSet.forEach(function (tuple, outerIndex) {
  939. tuple.forEach(function (value, index) {
  940. if (tupleSet[outerIndex][index] && index === currentIndex) {
  941. if (SynchContextHelper.replaceUseValue(options.targetColumn, tupleSet[outerIndex][index])) {
  942. isSynchronized = true;
  943. } else {
  944. //Removing the item since don't match
  945. options.hierarchies.splice(index, 1);
  946. tupleSet[outerIndex].splice(index, 1);
  947. }
  948. }
  949. });
  950. });
  951. };
  952. matchTupleSet();
  953. return isSynchronized;
  954. },
  955. _addItemValues: function _addItemValues(sourceId, id, values) {
  956. if (!(sourceId in this.items)) {
  957. this.items[sourceId] = {};
  958. }
  959. var item = this.items[sourceId];
  960. item[id] = values;
  961. },
  962. _getModule: function _getModule(sourceId) {
  963. return this.modules[sourceId];
  964. },
  965. _loadValues: function _loadValues(module, id) {
  966. var key = module.getSourceId() + id;
  967. var promise = this.loadValuesPromises[key];
  968. if (!promise) {
  969. promise = this.loadValuesPromises[key] = this._loadValuesForMetadataColumn(module, id);
  970. }
  971. return promise.then(function (values) {
  972. this._addItemValues(module.getSourceId(), id, values);
  973. return values;
  974. }.bind(this));
  975. },
  976. _getSynchDataEntry: function _getSynchDataEntry(sourceId, scope, tableRef) {
  977. var entry = this.synchronizeDataEntries[SynchContextHelper.getKey(sourceId, tableRef)];
  978. if (!entry || !entry.inSameScope() || !entry.inSameTable(tableRef)) {
  979. var spec = {
  980. sourceId: sourceId,
  981. scope: scope.scope,
  982. eventGroupId: scope.eventGroupId,
  983. tableRef: tableRef
  984. };
  985. entry = new SynchronizeDataEntry(spec);
  986. }
  987. return entry;
  988. },
  989. _getTargetBrushingEntries: function _getTargetBrushingEntries(context, scope) {
  990. var entries = {};
  991. _.each(context, function (contextEntry) {
  992. var tableRef = contextEntry.getTableRef();
  993. var sourceId = contextEntry.getSourceId();
  994. var dataEntry = this._getSynchDataEntry(sourceId, scope, tableRef);
  995. contextEntry.setSynchDataEntry(dataEntry);
  996. entries[SynchContextHelper.getKey(sourceId, tableRef)] = contextEntry.getAPI();
  997. }.bind(this));
  998. return entries;
  999. },
  1000. _addSynchronizeDataEntry: function _addSynchronizeDataEntry(fromContext) {
  1001. var key = SynchContextHelper.getKey(fromContext.getSourceId(), fromContext.getTableRef());
  1002. var entry = this.synchronizeDataEntries[key];
  1003. if (entry) {
  1004. entry.set({ synchronizeItems: fromContext.getSynchronizeItems() }, { silent: true });
  1005. } else {
  1006. var spec = fromContext.toJSON();
  1007. var newEntry = this.add(spec, { silent: true });
  1008. this.synchronizeDataEntries[key] = newEntry;
  1009. }
  1010. },
  1011. _loadValuesForMetadataColumn: function _loadValuesForMetadataColumn(module, id) {
  1012. var columnInfo = {
  1013. column: {
  1014. columnId: id
  1015. },
  1016. sort: 'asc'
  1017. };
  1018. var queryHelper = this._getQueryHelper();
  1019. return queryHelper.whenSingleItemQueryReady(module.getSourceId(), columnInfo).then(function (values) {
  1020. return queryHelper.extactListOfQueryResultData(values);
  1021. }.bind(this));
  1022. },
  1023. _getQueryHelper: function _getQueryHelper() {
  1024. if (this._queryHelper) {
  1025. return this._queryHelper;
  1026. }
  1027. var queryService = new QueryService({
  1028. dashboardApi: this.dashboardApi
  1029. });
  1030. this._queryHelper = new CommonQueryHelper({
  1031. queryService: queryService
  1032. }, this.logger);
  1033. return this._queryHelper;
  1034. },
  1035. _getValues: function _getValues(sourceId, itemId) {
  1036. return sourceId in this.items ? this.items[sourceId][itemId] : null;
  1037. },
  1038. _getConditionValue: function _getConditionValue(conditions) {
  1039. var value = {};
  1040. if (_.isArray(conditions) && !!conditions.length) {
  1041. value.from = conditions[0].from[0];
  1042. value.to = conditions[0].to[0];
  1043. value.invert = conditions[0].invert;
  1044. }
  1045. return value;
  1046. },
  1047. /**
  1048. * Calls to reset brushing context when first brushing occur
  1049. *
  1050. * @param {object} options - brushing options
  1051. */
  1052. _resetBrushing: function _resetBrushing(options) {
  1053. options = options || {};
  1054. var payloadData = options.payloadData || {};
  1055. payloadData.isBrushed = true;
  1056. this.undoRedo = [];
  1057. },
  1058. /**
  1059. * Handle 'change:pageContext' event
  1060. *
  1061. * @param {object} event - the event object of the 'change:pageContext' event
  1062. */
  1063. _onChangePageContext: function _onChangePageContext(event) {
  1064. if (this._canProcessPageContextChange(event)) {
  1065. var cloneData = SynchronizeDataHelper.clone(event.data);
  1066. cloneData.ignoreUndoPageContextChanged = this.undoRedo.length === 0 ? false : true;
  1067. cloneData.ignoreRedoPageContextChanged = false;
  1068. event.data = cloneData;
  1069. this.undoRedo.push(event);
  1070. for (var idx = 0; idx < this.undoRedo.length - 1; idx++) {
  1071. this.undoRedo[idx].data.ignoreRedoPageContextChanged = true;
  1072. }
  1073. }
  1074. },
  1075. /**
  1076. * Handle 'brushing:undoRedo' event
  1077. *
  1078. * @param {object} event - the event object of the 'brushing:undoRedo' event
  1079. */
  1080. _onUndoRedo: function _onUndoRedo(payload) {
  1081. payload = payload || {};
  1082. if (payload.undoRedoTransactionType === 'undo') {
  1083. payload.ignorePageContextChanged = payload.ignoreUndoPageContextChanged;
  1084. } else if (payload.undoRedoTransactionType === 'redo') {
  1085. payload.ignorePageContextChanged = payload.ignoreRedoPageContextChanged;
  1086. }
  1087. },
  1088. _onRemoveFilterIndicatorEntry: function _onRemoveFilterIndicatorEntry(payload) {
  1089. payload = payload || {};
  1090. var pageContextItem = payload.pageContext.getPageContextItem({ id: payload.filterToDelete });
  1091. if (pageContextItem) {
  1092. var synchDataId = pageContextItem.getSynchDataId();
  1093. //@todo what is isValueDataItem?
  1094. if (pageContextItem.getIsModeledFilter()) {
  1095. payload.pageContext.deletePageContextItems({ id: payload.filterToDelete });
  1096. } else if (!synchDataId || pageContextItem.getOrigin() !== 'visualization' && !pageContextItem.isValueDataItem()) {
  1097. if (pageContextItem.getOrigin() === 'visualization') {
  1098. payload.pageContext.deletePageContextItems({ id: payload.filterToDelete });
  1099. } else {
  1100. // defect288403: nameset filter with no selection, if remove from the filter indicator, the page context entry should be removed too
  1101. if (pageContextItem.isValueDataItem()) {
  1102. var isItemRemoved = payload.pageContext.deletePageContextItems({ id: payload.filterToDelete });
  1103. if (isItemRemoved) {
  1104. // clear the cache in filterDockItems to synch
  1105. var currentScope = this.filterDockItems[pageContextItem.getScope()] = this.filterDockItems[pageContextItem.getScope()] || {};
  1106. var currentSource = currentScope[pageContextItem.getSourceId()] = currentScope[pageContextItem.getSourceId()] || {};
  1107. delete currentSource[pageContextItem.getItemId()];
  1108. }
  1109. } else {
  1110. payload.pageContext.clearPageContextItemValues({ id: payload.filterToDelete });
  1111. }
  1112. }
  1113. } else if (synchDataId) {
  1114. var deleteSpec = JSON.parse(payload.filterToDelete);
  1115. var deleteOptions = {
  1116. sourceIdsToUpdate: {},
  1117. removedItems: [],
  1118. payloadData: {
  1119. undoRedoTransactionId: UniqueId.get('delete_viz_filter_entry_'),
  1120. transactionToken: payload.transactionToken
  1121. }
  1122. };
  1123. var netPageContext = this._getNetPageContextItems(deleteSpec.scope, deleteSpec.eventGroupId, 'visualization', synchDataId);
  1124. netPageContext.forEach(function (pageContext) {
  1125. payload.pageContext.deleteVisTypePageContext({ id: pageContext.getKey() }, deleteOptions);
  1126. });
  1127. if (!deleteOptions.handlers || !deleteOptions.handlers.filterContextUpdated) {
  1128. throw new Error('Invalid delete filter indicator options');
  1129. }
  1130. var updatateContext = {
  1131. removedList: deleteOptions.removedItems,
  1132. removedItems: deleteOptions.removedItems,
  1133. sourceIdsToUpdate: deleteOptions.sourceIdsToUpdate
  1134. };
  1135. deleteOptions.handlers.filterContextUpdated(updatateContext, { transactionId: deleteOptions.transactionId });
  1136. }
  1137. }
  1138. },
  1139. /**
  1140. * Returns a boolean indicating whether can process 'change:pageContext' event
  1141. *
  1142. * @param {object} event - the event object of the 'change:pageContext' event
  1143. *
  1144. * @return {boolean} true if can process else return false
  1145. */
  1146. _canProcessPageContextChange: function _canProcessPageContextChange(event) {
  1147. event = event || {};
  1148. var data = event.data || {};
  1149. return data.isBrushed && event.name !== 'add';
  1150. },
  1151. /**
  1152. * Callback to process render complete event for a vis widget
  1153. *
  1154. * @param {object} payload - the payloda to process the event
  1155. */
  1156. _onRenderComplete: function _onRenderComplete() {
  1157. var payload = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  1158. var widget = payload.widget;
  1159. var id = widget && widget.widgetAPI ? widget.widgetAPI.getId() : null;
  1160. if (id) {
  1161. this.widgets[id] = {
  1162. getEdgeSelection: function getEdgeSelection() {
  1163. return widget.getEdgeSelection();
  1164. },
  1165. getSourceId: function getSourceId() {
  1166. return widget.widgetAPI.getVisApi().getModule().getSourceId();
  1167. },
  1168. getId: function getId() {
  1169. return widget.widgetAPI.getId();
  1170. }
  1171. };
  1172. }
  1173. },
  1174. /**
  1175. * Callback to process the remove vis widget event
  1176. *
  1177. * @param {object} payload - the payloda to process the event
  1178. */
  1179. _onRemoveWidget: function _onRemoveWidget() {
  1180. var _this11 = this;
  1181. var payload = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  1182. var value = payload.value || {};
  1183. var parameter = value.parameter || [];
  1184. parameter.forEach(function (id) {
  1185. delete _this11.widgets[id];
  1186. });
  1187. }
  1188. });
  1189. return SynchronizeDataModel;
  1190. });
  1191. /** Example:
  1192. [
  1193. {
  1194. "sourceId": "QC_Data",
  1195. 'tableRef: "Sheet_1",
  1196. "synchronizeItems": [
  1197. {
  1198. "id": "Product line",
  1199. "items": [
  1200. {
  1201. "sourceId": "Hollywood Movies",
  1202. 'tableRef: "Sheet_2",
  1203. "id": "Product line"
  1204. },
  1205. {
  1206. "sourceId": "Sales and Marketing",
  1207. "id": "Product line"
  1208. }
  1209. ]
  1210. },
  1211. {
  1212. "id": "Year",
  1213. "items": [
  1214. {
  1215. "sourceId": "Hollywood Movies",
  1216. "id": "Year"
  1217. }
  1218. ]
  1219. }
  1220. ]
  1221. },
  1222. {
  1223. "sourceId": "Hollywood Movies",
  1224. "synchronizeItems": [
  1225. {
  1226. "itemId": "Country",
  1227. "items": [
  1228. {
  1229. "sourceId": "QC_Data",
  1230. "itemId": "Country"
  1231. },
  1232. {
  1233. "sourceId": "Sales and Marketing",
  1234. "itemId": "Country"
  1235. }
  1236. ]
  1237. },
  1238. {
  1239. "id": "Location",
  1240. "items": [
  1241. {
  1242. "sourceId": "Hollywood Movies",
  1243. "itemId": "Location"
  1244. }
  1245. ]
  1246. }
  1247. ]
  1248. }
  1249. ]
  1250. */
  1251. //# sourceMappingURL=SynchronizeDataModel.js.map