'use strict'; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** * Licensed Materials - Property of IBM * IBM Cognos Products: Dashboard * (C) Copyright IBM Corp. 2018, 2021 * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp. */ define(['underscore', '../../lib/@waca/dashboard-common/dist/core/APIFactory', './SlotAPISpec', '../SlotAPI', '../../visualizations/interactions/BinningActionsUtils', '../../apiHelpers/SlotAPIHelper', '../../widgets/livewidget/nls/StringResources', '../../util/TransactionUtil'], function (_, APIFactory, SlotAPISpec, SlotAPI, BinningActionsUtils, SlotAPIHelper, StringResources, TransactionUtil) { var SlotImpl = function (_SlotAPISpec) { _inherits(SlotImpl, _SlotAPISpec); function SlotImpl(slotDefinitionAPI, filterSupport, slotsImpl, transaction, locale, visualizationImpl, dataModel, slotMappingModel) { _classCallCheck(this, SlotImpl); // @todo remove localFilters, slotDfn (for now used in creating MappedDataItem) plus SlotAPI var _this = _possibleConstructorReturn(this, _SlotAPISpec.call(this)); _this.slotDefinitionAPI = slotDefinitionAPI; // @todo need to revise by using events _this.filterSupport = filterSupport; _this.metadataManager = filterSupport.getMetadata(); _this.transaction = transaction; _this.locale = locale; _this.slotsImpl = slotsImpl; _this.visualizationImpl = visualizationImpl; _this.dataModel = dataModel; _this.slotMappingModel = slotMappingModel; _this.api = APIFactory.createAPI(_this, [SlotAPI]); return _this; } SlotImpl.prototype.setDefinition = function setDefinition(slotDefinitionAPI) { this.slotDefinitionAPI = slotDefinitionAPI; }; SlotImpl.prototype.destroy = function destroy() { this.slotDefinitionAPI = null; this.filterSupport = null; this.metadataManager = null; this.transaction = null; this.locale = null; this.slotsImpl = null; this.visualizationImpl = null; this.dataModel = null; this.slotMappingModel = null; this.api = null; }; SlotImpl.prototype.getAPI = function getAPI() { return this.api; }; SlotImpl.prototype.getId = function getId() { return this.getDefinition().getId(); }; SlotImpl.prototype.getLocalFilters = function getLocalFilters() { return this.filterSupport.getLocalFilters(); }; SlotImpl.prototype.getDataSource = function getDataSource() { return this.slotsImpl.getDataSource(); }; SlotImpl.prototype.resolveDataViewId = function resolveDataViewId() { var viewId = void 0; // step 1 : check the first item (if any) that belongs to this slot var dataItemList = this.getDataItemList(); if (dataItemList.length > 0) { viewId = this.dataModel.getDataViewIdForDataItem(dataItemList[0].getId()); } // Step 2: If we have no mapped dataitem, search the slots that belong to the same dataset if (!viewId) { var slot = void 0; var slotDefinition = this.getDefinition(); var slotDefinitionList = this.visualizationImpl.getDefinition().getSlotList(); for (var i = 0; i < slotDefinitionList.length; i++) { if (slotDefinition.getId() !== slotDefinitionList[i].getId() && slotDefinition.getDatasetIdList()[0] === slotDefinitionList[i].getDatasetIdList()[0]) { // the slots are part of the same dataset // see if it has mapped dataitems.. then use the same view slot = this.slotsImpl.getSlot(slotDefinitionList[i].getId()); dataItemList = slot.getDataItemList(); if (dataItemList.length > 0) { viewId = this.dataModel.getDataViewIdForDataItem(dataItemList[0].getId()); break; } } } } if (!viewId) { // we could not find a view.. we create a new one var dataView = this.dataModel.createDataView(); var dataSource = this.visualizationImpl.getDataSource(); // default the modelRef if we have other views if (dataSource) { this.dataModel.setModelRefInAllViews(dataSource.getId()); } viewId = dataView.id; } return viewId; }; /** * Return the slotImpl associated with the multi-measure series. We use the implementation so that * any operations are considered inernal and no events will be generated. */ SlotImpl.prototype.getMultiMeasuresSeriesSlot = function getMultiMeasuresSeriesSlot() { var multiMeasureSlot = void 0; var dataset = this.getDefinition().getDatasetIdList()[0]; var customMultiMeasureSlotId = this.slotMappingModel.getMultiMeasureSlotId(dataset); if (customMultiMeasureSlotId) { // slot may not exist, e.g changing visualization from one with slot series to one with color. multiMeasureSlot = this.slotsImpl.getSlotImpl(customMultiMeasureSlotId); } if (!multiMeasureSlot) { var defaultMultiMeasureSlot = SlotAPIHelper.getMultiMeasureSeriesSlot(this.visualizationImpl.getAPI(), dataset).getId(); multiMeasureSlot = this.slotsImpl.getSlotImpl(defaultMultiMeasureSlot); } return multiMeasureSlot; }; SlotImpl.prototype.getMultiMeasureSeriesPosition = function getMultiMeasureSeriesPosition() { var dataset = this.getDefinition().getDatasetIdList()[0]; return this.slotMappingModel.getMultiMeasurePosition(dataset); }; /** * Check how many measure we have in the slot and decide if we need to add, remove or update the measure series slot */ SlotImpl.prototype._processMultiMeasure = function _processMultiMeasure() { if (!this.getDefinition().getProperty('multiMeasure')) { return; } var targetSlot = this.getMultiMeasuresSeriesSlot(); if (targetSlot) { var currentDataItemList = this.getDataItemList(); var multiMeasuresLabel = StringResources.get('MeasuresGroupCaption', { count: currentDataItemList.length }); var multiMeasureDataItemId = SlotAPIHelper.MULTI_MEASURES_SERIES + '_' + targetSlot.getDefinition().getDatasetIdList()[0]; var multiMeasureRequired = currentDataItemList.length > 1; var multiMeasureDataItem = this.slotsImpl.getDataItemImpl(multiMeasureDataItemId); var createMultiMeasureDataItem = function () { // Use the implementation to keep the actions internal (i.e. no events) this.slotsImpl.createDataItems([{ id: multiMeasureDataItemId, columnId: SlotAPIHelper.MULTI_MEASURES_SERIES, itemLabel: multiMeasuresLabel }]); targetSlot.addDataItemsMapping([multiMeasureDataItemId], this.getMultiMeasureSeriesPosition()); }.bind(this); var deleteMultiMeasureDataItem = function () { // Use the implementation to keep the actions internal (i.e. no events) this.slotsImpl.deleteDataItems([multiMeasureDataItemId]); targetSlot.removeDataItemsMapping([multiMeasureDataItemId]); }.bind(this); var updateMultiMeasureDataItem = function () { // Use the implementation to keep the actions internal (i.e. no events) this.slotsImpl.getDataItemImpl(multiMeasureDataItem.getId()).setLabel(multiMeasuresLabel); }.bind(this); if (multiMeasureRequired && multiMeasureDataItem) { updateMultiMeasureDataItem(); } else if (multiMeasureRequired && !multiMeasureDataItem) { createMultiMeasureDataItem(); } else if (!multiMeasureRequired && multiMeasureDataItem) { deleteMultiMeasureDataItem(); } } }; SlotImpl.prototype.getDataItemIndex = function getDataItemIndex(id) { var slotModel = this.slotMappingModel.getSlot(this.getId()); return slotModel.dataItems.indexOf(id); }; // Return the actual instance and not just the API interface SlotImpl.prototype.getDataItemImpl = function getDataItemImpl(dataItemId) { var impl = void 0; // Search the mapping model var slotModel = this.slotMappingModel.getMappedSlot(dataItemId); if (slotModel && slotModel.name === this.getId()) { // the dataItem is mapped to this slot impl = this.slotsImpl.getDataItemImpl(dataItemId); } return impl; }; SlotImpl.prototype.getDataItem = function getDataItem(dataItemId) { var impl = this.getDataItemImpl(dataItemId); return impl ? impl.getAPI() : null; }; SlotImpl.prototype.addDataItems = function addDataItems(dataItemSpecList, position, transactionToken) { var subTransaction = this.transaction.startTransaction(transactionToken); // Use the API to trigger the event var dataItemList = this.slotsImpl.getAPI().createDataItems(dataItemSpecList, subTransaction); this.getAPI().addDataItemsMapping(dataItemList.map(function (dataItem) { return dataItem.getId(); }), position, subTransaction); this.transaction.endTransaction(subTransaction); return dataItemList; }; SlotImpl.prototype.removeDataItems = function removeDataItems(dataItemIdList, transactionToken) { var _this2 = this; var subTransaction = this.transaction.startTransaction(transactionToken); this._removeSelectionsOnUnusedDataItems(dataItemIdList, subTransaction); this._removedUnmappedOrdinalSlotsWithFilters(dataItemIdList.map(function (id) { var dataItem = _this2.slotsImpl.getDataItemImpl(id); if (dataItem) { return { columnId: dataItem.getColumnId(), aggregationType: dataItem.getAggregation() }; } }), subTransaction); this.getAPI().removeDataItemsMapping(dataItemIdList, subTransaction); // Use the API to trigger the event this.slotsImpl.getAPI().deleteDataItems(dataItemIdList, subTransaction); this.transaction.endTransaction(subTransaction); }; SlotImpl.prototype._removeSelectionsOnUnusedDataItems = function _removeSelectionsOnUnusedDataItems(dataItemIdList, transactionToken) { var _this3 = this; var columnIdList = dataItemIdList.map(function (id) { return _this3.getDataItemImpl(id).getColumnId(); }); this.filterSupport.clearSelectionsByDataItemIds(columnIdList, TransactionUtil.transactionTokenToOptions(transactionToken)); }; SlotImpl.prototype._removedUnmappedOrdinalSlotsWithFilters = function _removedUnmappedOrdinalSlotsWithFilters(unusedFilterRef, transactionToken) { var localFilters = this.getLocalFilters(); var filterEntryToRemove = _.find(localFilters.models, function (filterEntry) { if (filterEntry.isMissing) { return _.findWhere(unusedFilterRef, _.pick(filterEntry, 'columnId')); } else if (filterEntry.aggregationType) { /* Remove obsolete ordinal filter when there is no match for both column id and aggregation type. 1. when the data item is not projected anymore - eg. projected data in the slot but no aggregation type matching - eg. projected data items with ids 'col1' (sum), 'col1' (count), col2' but the filter was set on 'col1' (avg) */ return _.findWhere(unusedFilterRef, _.pick(filterEntry, 'columnId', 'aggregationType')); } else { return _.findWhere(unusedFilterRef, _.pick(filterEntry, 'columnId')); } }); if (filterEntryToRemove) { localFilters.removeFilterEntry(filterEntryToRemove); localFilters.allFilterModificationComplete(TransactionUtil.transactionTokenToOptions(transactionToken)); } }; SlotImpl.prototype.removeDataItemsMapping = function removeDataItemsMapping(dataItemIds) { var _this4 = this; var result = []; _.each(dataItemIds, function (dataItemId) { var dataItem = _this4.getDataItemImpl(dataItemId); if (dataItem) { _this4.slotMappingModel.unmapDataItemsFromSlots([dataItemId]); dataItem.setSlot(null); result.push(dataItem); } else { result.push(null); } }); this._processMultiMeasure(); return result; }; SlotImpl.prototype.addDataItemsMapping = function addDataItemsMapping(dataItemIdList) { var _this5 = this; var position = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : -1; var transactionToken = arguments[2]; dataItemIdList.forEach(function (id, index) { var dataItem = _this5.slotsImpl.getDataItemImpl(id); // When dataItem is empty, we remove it from dataItemIdList, here is an example: // e.g. we now have this slots mapping // Length // - measure1 // - measure2 // Color // - Measures Group(2) // // now we move measure2 into `Color` and put it before `Measures Group(2)`, `setDataItems()` will be called with a // `dataItemIdList` including measure2's dataItemId and a multimeasure dataItemId, inside `setDataItems`, measure2 will be removed from original // slot(ie, Length) first, removing it will remove the related multimeasure data item(see _processMultiMeasure), then // `addDataItemsMappingthen` is called with the `dataItemIdList`, in this case, `dataItemIdList` contains the id of the deleted // multimeasure data item, so getDataItemImpl(id) will return null for the multimeasure data item. if (!dataItem) { dataItemIdList.splice(index, 1); return; } if (dataItem.getSlot()) { throw new Error('Cannot map an already mapped data item, it must to be unmapped first. The dataitem with id "' + id + '" is mapped to slot "' + dataItem.getSlot().getId() + '"'); } }); var actualPosition = position === -1 ? this.getDataItemList().length : position; this.dataModel.moveDataItemsToView(dataItemIdList, this.resolveDataViewId()); this.slotMappingModel.mapDataItemsToSlot(dataItemIdList, this.getId(), position); dataItemIdList.forEach(function (id, index) { // If one of the dataitems is a measure series, then mark this slot as the preferred measure series slot. if (id.indexOf(SlotAPIHelper.MULTI_MEASURES_SERIES) > -1) { var dataset = _this5.getDefinition().getDatasetIdList()[0]; _this5.slotMappingModel.setMultiMeasureSlotId(dataset, _this5.getId()); _this5.slotMappingModel.setMultiMeasurePosition(dataset, actualPosition + index); } _this5.slotsImpl.getDataItemImpl(id).setSlot(_this5.getAPI(), transactionToken); }); this._processMultiMeasure(); }; SlotImpl.prototype.getDataItemList = function getDataItemList() { var ignoreDefaultDataItemMapping = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; return this._getDataItemListWithOptions(ignoreDefaultDataItemMapping); }; SlotImpl.prototype._getDataItemListWithOptions = function _getDataItemListWithOptions(ignoreDefaultDataItemMapping) { var _this6 = this; // TODO - livewidget cleanup - cache and free the list for performance var idList = this.slotMappingModel.getMappedDataItemIdList(this.getId()); var list = idList.map(function (id) { var dataItem = _this6.slotsImpl.getDataItem(id); if (!dataItem) { throw new Error('The referenced data item \'' + id + '\' is not found in the widget.'); } return dataItem; }); if (list.length === 0 && !ignoreDefaultDataItemMapping) { var defaultFromSlot = this.getDefinition().getDefaultFromSlot(); if (defaultFromSlot && defaultFromSlot.id) { var slot = this.slotsImpl.getSlot(defaultFromSlot.id); if (slot) { list = slot.getDataItemList(); } } } return list; }; SlotImpl.prototype.supportsColumns = function supportsColumns(metadataColumnList) { return !this.itemsNotSupported(metadataColumnList); }; /** * @param metadata either an array of metadatacolumns or a single dataitemapi which both have access to their taxonomy(the class and family of the data column) * @returns a boolean whether the slot supports the taxonomy or datatype of the metadata item(s). * This method will check to make sure that the columns being dropped into the slots have the correct taxonomy class and family as per these rules: * 1) If the slot has no taxonomy defined, then it accepts all data columns * 2) If the slot is a lot/long, then it can accept columns that are also lat/long or if they do not have a taxonomy defined, then the slot will also accept numbers * 3) If the slot has a taxonomy defined which is not lat/long then it accepts everything except for lat/long columns */ //jshint maxdepth:4 SlotImpl.prototype.itemsNotSupported = function itemsNotSupported(metadata) { var slot = this.getDefinition(); if (slot.getSubType()) { if (_.isArray(metadata)) { if (this._itemsNotSupportedFromTree(metadata)) { return true; } } else { if (this._itemsNotSupportedFromSlots(metadata)) { return true; } } } return false; }; SlotImpl.prototype._isLatLong = function _isLatLong() { var slot = this.getDefinition(); return slot.getSubType() === 'latitude' || slot.getSubType() === 'longitude'; }; SlotImpl.prototype._itemsNotSupportedFromTree = function _itemsNotSupportedFromTree(metadata) { var i; for (i = 0; i < metadata.length; i++) { var metadataItem = metadata[i]; if (this._itemsNotSupportedHelper(metadataItem)) { return true; } } }; //this block handles cases where you are swapping slots SlotImpl.prototype._itemsNotSupportedFromSlots = function _itemsNotSupportedFromSlots(metadata) { if (this._itemsNotSupportedHelper(metadata)) { return true; } }; SlotImpl.prototype._itemsNotSupportedHelper = function _itemsNotSupportedHelper(metadataItem) { var slot = this.getDefinition(); var taxonomy = metadataItem.getTaxonomyList() ? metadataItem.getTaxonomyList()[0] : null; if (this._isLatLong()) { if (taxonomy) { if (!(taxonomy.getFamily() === 'cLatitude' && slot.getSubType() === 'latitude' || taxonomy.getFamily() === 'cLongitude' && slot.getSubType() === 'longitude')) { return true; } } else if (!this._isSupportedDataType(metadataItem.getDataType())) { return true; } } else { if (taxonomy && (taxonomy.getFamily() === 'cLatitude' || taxonomy.getFamily() === 'cLongitude')) { return true; } } }; SlotImpl.prototype._isSupportedDataType = function _isSupportedDataType(datatype) { if (this._isLatLong()) { return ['integer', 'decimal', 'double', 'float'].indexOf(datatype) > -1; } return true; }; SlotImpl.prototype.getDefinition = function getDefinition() { return this.slotDefinitionAPI; }; SlotImpl.prototype.isStacked = function isStacked() { var mappedDataItems = this.slotMappingModel.getMappedDataItemIdList(this.getId()); return !!(mappedDataItems.length > 1 && this.getDefinition().isStackItems()); }; SlotImpl.prototype.hasUnavailableMetadataColumns = function hasUnavailableMetadataColumns() { var unavailableColumn = _.find(this.getDataItemList(), function (dataItem) { return dataItem.isColumnUnavailable(); }); return !!unavailableColumn; }; SlotImpl.prototype._convertTransactionTokenToLegacyOptions = function _convertTransactionTokenToLegacyOptions(transactionToken) { var oldOptions = {}; if (transactionToken) { oldOptions.payloadData = { transactionToken: transactionToken }; if (transactionToken.transactionId) { oldOptions.payloadData.undoRedoTransactionId = transactionToken.transactionId; } } return oldOptions; }; return SlotImpl; }(SlotAPISpec); return SlotImpl; }); //# sourceMappingURL=Slot.js.map