'use strict'; /** *+------------------------------------------------------------------------+ *| Licensed Materials - Property of IBM *| IBM Cognos Products: Dashboard *| (C) Copyright IBM Corp. 2017, 2022 *| *| US Government Users Restricted Rights - Use, duplication or disclosure *| restricted by GSA ADP Schedule Contract with IBM Corp. *+------------------------------------------------------------------------+ */ /* global Blob FileReader */ define(['jquery', 'underscore', './VIPREventTarget', '../VisView', '../VisEventHandler', 'dashboard-analytics/visualizations/vipr/VIPR', 'dashboard-analytics/visualizations/vipr/VIPRUtils', 'dashboard-analytics/visualizations/vipr/VIPRDataRequestHandler', 'dashboard-analytics/visualizations/vipr/VIPRCachedDataRequestHandler', 'dashboard-analytics/visualizations/vipr/properties/VIPRProperties', 'dashboard-analytics/visualizations/vipr/properties/ColorPropertiesCreator', 'dashboard-analytics/visualizations/vipr/VIPRConfig', '../../../lib/@waca/core-client/js/core-client/utils/BrowserUtils', '../../../widgets/livewidget/nls/StringResources', 'dashboard-analytics/visualizations/interactions/BinningActionsUtils', '../../../lib/@waca/core-client/js/core-client/utils/PerfUtils', '../../../widgets/livewidget/SdkShowError', 'react-dom', 'react', './components/CustomVisPreviewMessage', '../../../util/ContentUtil', 'dashboard-analytics/widgets/livewidget/util/VisUtil'], function ($, _, VIPREventTarget, VisView, VisEventHandler, VIPR, VIPRUtils, VIPRDataRequestHandler, VIPRCachedDataRequestHandler, VIPRProperties, ColorPropertiesCreator, VIPRConfig, BrowserUtils, stringResources, BinningActionsUtils, PerfUtils, SdkShowError, ReactDOM, React, CustomVisPreviewMessage, ContentUtil, VisUtil) { 'use strict'; var DEFAULT_THUMBNAIL_PROPERTIES = { 'optimizeSize': true, 'gridLines.visible': false, 'markers.visible': false, 'widget.legend.size': 0, 'labels.visible': false, 'itemAxis.line.visible': false, 'valueAxis.line.visible': false, 'valueAxis.ticks.visible': false, 'itemAxis.ticks.visible': false, 'itemAxis.gridLines.visible': false, 'valueAxis.gridLines.visible': false }; /** * List of ignored decorations when generating a thumbnails * @type {string[]} */ var IGNORED_THUMBNAIL_DECORATIONS = [ // TODO: right now livewidget does not know what vis-crosshairs is // we should probably contribute this list of decorations to be ignored from outside 'compareLine', // old vis-crosshairs - to be removed when the new crosshair feature flag is removed 'lines' // new vis-crosshairs ]; var VIPRView = VisView.extend({ initVizPromise: null, init: function init(options) { VIPRView.inherited('init', this, arguments); this.logger = options.logger; this.$el.attr('id', this.viewId); this.$el.addClass('dataview vipr-view'); this.dashboard = options.dashboardApi; this.colorsService = this.dashboard.getFeature('Colors'); this.visAPI = options.visModel; this.predictHandler = this._createPredictHandler(VIPRDataRequestHandler); this.highContrastEnabled = $(document.body).hasClass('highcontrast'); this.content = options.content; this.visualization = this.content.getFeature('Visualization'); this.internalVisDefinitions = this.dashboard.getFeature('VisDefinitions.internal'); //Initialize members choosing which title to show (name/titleHtml vs annotation) if (this.visModel.getShowTitle()) { this._hasUserTitle = true; this._hasAnnotationTitle = false; } this.visController = {}; this.viprDecorations = {}; this.dataItemDecorations = {}; //A list of widgets marked to be destroyed after the current viprWidget control render completes (part of a reRender) this._deferredDestroyViprWidgetsList = []; this.viprControlRenderInProgress = false; }, /** * Setup the view and events based on the model * @override */ createView: function createView() { VIPRView.inherited('createView', this, arguments); // Listen for theme changes this.visModel.on('change:theme', this.onChangeTheme, this); if (this._isSmartTitleEnabled()) { this.visModel.on('change:titleMode', this.onShowTitle, this); } else { this.visModel.on('change:showTitle', this.onShowTitle, this); } this.visModel.on('change:customData', this.onChangeCustomData, this); this._createVisTabs(); }, /** * Create a predict handler * * @param {Function} Class - class to use for the predict handler * @param {[type]} [handler] - original predict handler, if any; This is used for thumbnails for caching API requests * * @return {object} predict handler instance */ _createPredictHandler: function _createPredictHandler(Class, handler) { var options = { visAPI: this.visAPI, visView: this, logger: this.logger }; if (handler) { options.handler = handler; } return new Class(options); }, /** * * @param event - payload for a customData model change event * event.prevValue - previous selections of custom data * event.value - current selections of custom data */ onChangeCustomData: function onChangeCustomData(event) { var prevSelections = event.prevValue && event.prevValue.selected || []; var prevIds = prevSelections.map(function (decoration) { return decoration.id; }); var prevSelectedCustomData = this.visAPI.getCustomData(prevIds); //clear decorations for previous selections; prevSelectedCustomData.forEach(function (item) { item.setDecoration && item.setDecoration('selected', false); }); var curSelectedCustomData = this.visAPI.getDecoratedCustomData('selected'); // set decorations for current selections; curSelectedCustomData.forEach(function (item) { item.setDecoration && item.setDecoration('selected', true); }); return this.renderVIPRControl({ callRenderComplete: true }); }, /** * Create the visualization tabs if the definition require the tabs * The tabs definition consists with the following: * tabs : { * entries:[{ * id: , * resource: * }] * default: * } */ _createVisTabs: function _createVisTabs() { var _this = this; if (this._tabs) { // ensure we start from scratch this._tabs.reset(); // check the tabs definitions var definition = this.visModel.getDefinition(); if (definition.tabs) { // create the tabs this._tabs.createTabs(_.map(definition.tabs.entries, function (tab) { return { id: tab.id, label: stringResources.get(tab.resource), onChange: function (tabId) { this.visModel.ownerWidget.updateVisProperties({ id: 'actions', value: tabId }); }.bind(_this) }; }), this.visModel.getPropertyValue('actions') || definition.tabs.default); } // hide the tabs until necessary to show this._tabs.hide(); } }, /** * return a public API to a decorator for the nth vipr dataset (default dataset=0) * the decorator implements: * - decorate (decorate the dataset), * - decorateItem (decorate an item), * - decoratePoint(decorate a point) * - clearItemDecoration (clear a given item decoration) * All decoratorAPIs implement the same updateDecorations method to render the decorations. * Most visualizations have only 1 decoratorAPI (ie: 1 vipr dataset). Maps, for example have 3. * @param n - the nth decorator (dataset)....default 0 * @returns a decoratorAPI for the whole decorator, items or points */ getDecoratorAPI: function getDecoratorAPI(n) { var _this2 = this; var setDecorateItem = function setDecorateItem(itemIndex, decoration, value) { // Save the decoration so that they are applied when the decoration is refreshed // We are only saving the dataitem decorations // TODO: Do we need to save datapoints and dataset decorations ?? if (!_this2.dataItemDecorations[n]) { _this2.dataItemDecorations[n] = {}; } if (!_this2.dataItemDecorations[n][itemIndex]) { _this2.dataItemDecorations[n][itemIndex] = {}; } var dataItemN = oDataSet.getDataItem(itemIndex); /** * decorationValue from viprData is usually the same as the value passed in EXCEPT FOR the 'lines' decoration. * The lines decoration returns an array of VIDA decoration instances that are the elements of the line that correspond to the value spec passed in . eg: { baseLine: {}, grabber: {}} */ var decorationValue = dataItemN && dataItemN.decorate(decoration, value); _this2.dataItemDecorations[n][itemIndex][decoration] = decorationValue; return; }; n = n || 0; if (!this.viprData || n > this.viprData.getDataSetCount() || !this._isViprWidgetValid()) { return null; } var oDataSet = this.viprData.getDataSetAt(n); if (oDataSet) { var datapoints = oDataSet.getDataPointIterator() && oDataSet.getDataPointIterator().datapoints; return { decorateItem: setDecorateItem, decoratePoint: function decoratePoint(row, decoration, value) { return datapoints && datapoints[row] && datapoints[row].decorate && datapoints[row].decorate(decoration, value); }, decorate: function decorate(decoration, value) { return oDataSet.decorate(decoration, value); }, clearItemDecoration: function clearItemDecoration(decoration) { if (_this2.dataItemDecorations[n]) { var dataItemIndex = void 0; for (dataItemIndex in _this2.dataItemDecorations[n]) { var dataItemN = oDataSet.getDataItem(dataItemIndex); if (dataItemN) { dataItemN.decorate(decoration, ''); } delete _this2.dataItemDecorations[n][dataItemIndex][decoration]; } } }, updateDecorations: function updateDecorations() { return _this2.renderVIPRControl(); } }; } return null; }, /** * return a public interface to all decorators in the visualization * (ie: one decoratorAPI per dataset as an array) * @returns an array of 0 or more getDecoratorAPIs */ getDecoratorAPIs: function getDecoratorAPIs() { var decoratorAPIs = []; if (this.viprData && this.viprData.getDataSetCount) { for (var i = 0; i < this.viprData.getDataSetCount(); ++i) { var decoratorAPI = this.getDecoratorAPI(i); if (decoratorAPI) { decoratorAPIs.push(decoratorAPI); } } } return decoratorAPIs; }, /** * @returns {boolean} true iff and only if this view supports annotations * The base view always returns false, the child view shoud override if different */ supportsAnnotations: function supportsAnnotations() { return !this.doesVisPropertyMatchExpected('doesNotSupportAnnotations', true); }, supportsForecasts: function supportsForecasts() { var forecastFeature = this.content && this.content.getFeature('Forecast'); return forecastFeature && forecastFeature.supportsForecasts(); }, /** * Handle visualization definition change */ onChangeDefinition: function onChangeDefinition() { this.initVizPromise = null; VIPRView.inherited('onChangeDefinition', this, arguments); //Clean Info Indicator and we update the indicator after Data Query is executed //for the new view. if (this.infoIndicator) { this.infoIndicator.reset(); } //clean listener flag for vida prop changes if (this.propChangeslistener) { this.propChangeslistener = undefined; } // recreate the tabs for the new vistype this._createVisTabs(); }, /** * Handle removal of the View and/or its embedded viprWidget control. * @param finalRemove - (default) remove is 'final', the embedded viprWidget control is destroyed immediately * remove can also be called as part of a reRender (final=false). In this case, the embedded viprWidget is marked * to be destroyed after the current control render. * (the embedded control render is async and can't be destroyed mid-render) */ remove: function remove() { var finalRemove = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; this.visModel.off('change:theme', this.onChangeTheme, this); if (this._isSmartTitleEnabled()) { this.visModel.off('change:titleMode', this.onShowTitle, this); } else { this.visModel.off('change:showTitle', this.onShowTitle, this); } this.visModel.off('change:customData', this.onChangeCustomData, this); this._cleanupVIPRWidget(finalRemove); // The super.destroy will clear all members of this class. // this is why we do it at the end VIPRView.inherited('remove', this, arguments); }, /** * Clean up the VIPR widget and event handler * @param finalRemove - (default) remove is 'final', false - part of reRender (see remove) */ _cleanupVIPRWidget: function _cleanupVIPRWidget() { var finalRemove = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; if (this.eventHandler) { this.eventHandler.remove(); } if (this.$customVisPreviewMessage) { ReactDOM.unmountComponentAtNode(this.$customVisPreviewMessage[0]); this.$customVisPreviewMessage.remove(); this.$customVisPreviewMessage = null; } this._destroyCustomVisShowError(); if (this.viprWidget) { if (finalRemove && !this.viprControlRenderInProgress) { //Destroy immmediately this.viprWidget.destroy(); this.viprWidget = null; } else if (this._deferredDestroyViprWidgetsList.indexOf(this.viprWidget) === -1) { //Set the viprWidget control to be destroyed at the end of control's async render. this._deferredDestroyViprWidgetsList.push(this.viprWidget); } } }, _destroyCustomVisShowError: function _destroyCustomVisShowError() { if (this.sdkShowError) { this.sdkShowError.destroy(); this.sdkShowError = null; } }, /** * This function is called after the viprWidget render to destroy any viprWidget controls that were marked to be destroyed. */ _deferredDestroyVIPRWidgets: function _deferredDestroyVIPRWidgets() { _.each(this._deferredDestroyViprWidgetsList, function (viprWidgetToDestroy) { viprWidgetToDestroy.destroy(); }); this._deferredDestroyViprWidgetsList = []; }, /** * Create a VIPRWidget instance and event handler */ _createVIPRWidget: function _createVIPRWidget() { var _this3 = this; if (!this.viprWidget) { // Create a VIPR container. // NOTE. VIPR will literally owns this container and will remove on viprWidget.destroy() var containerId = 'VIPR_' + this.viewId; this.$el.append($('
')); var cursors = VIPRUtils.getCursor(this.visAPI.getVisId()); // The last supported cursor wins. For example, only -webkit-grab works // on chrome. This means that if cursors = ['-webkit-grab', 'grab', '-moz-grab'], // the style.cursor of the element will be set to '-webkit-grab'. if (cursors) { _.forEach(cursors, function (cursor) { _this3.$el.get(0).style.cursor = cursor; }); } //If there is a passed in predictData during initilization, we will create a VIPRCachedDataRequestHandler that caches the predictData if (this.predictData) { this.predictHandler.setLastRequestData('Catalyst', this.predictData); this.predictHandler = this._createPredictHandler(VIPRCachedDataRequestHandler, this.predictHandler); } this.viprWidget = VIPR.createWidget(containerId, this.predictHandler); var userProfileService = this.dashboard.getGlassCoreSvc('.UserProfile'); if (userProfileService) { this.viprWidget.formatLocale = userProfileService.preferences.contentLocale; } this.eventHandler = new VisEventHandler({ target: new VIPREventTarget({ $el: this.$el, viprWidget: this.viprWidget, visAPI: this.visAPI, visualization: this.visualization, content: this.content }), transaction: this.transactionApi, ownerWidget: this.ownerWidget, visAPI: this.visAPI, logger: this.logger, dashboard: this.dashboard }); } }, /** * Handle when visualization is ready */ whenVisControlReady: function whenVisControlReady() { var _this4 = this; if (!this.initVizPromise) { this._createVIPRWidget(); this.initVizPromise = this.viprWidget.newViz(this.visAPI.getVisId(), 'client').then(function (vizBundle) { // force to create a new vipr data _this4.viprData = null; _this4.vizBundle = vizBundle; return _this4.visController; }).catch(function (error) { //Remove this checking in R5 for Custom Vis, this is no longer needed if (_this4.visAPI.getVisId() === 'visualizationPreview') { _this4.logger.error(error, _this4); return _this4.visController; } else { throw error; } }); } return this.initVizPromise; }, /** * Create an instance of VIPR data from query data * * @param {object} data - query data * * @return {object} VIPR data */ createViprData: function createViprData(data) { var viprData = data.getQueryResults ? VIPRUtils.createData(data.getQueryResults(), this.visualization, this.content) : VIPRUtils.createQueryResultData(data, this.visualization, this.content); viprData.hasMoreData = data.hasMoreData; viprData.queryThreshold = data.queryThreshold; return viprData; }, /** * When there is a connection error we should not deal with preparing data anymore. */ canRender: function canRender() { return this._hasMappedSlots() && this.isMappingComplete(); }, /** * Handle when data is ready */ whenDataReady: function whenDataReady() { var _this5 = this; return VIPRView.inherited('whenDataReady', this, arguments).then(function (_ref) { var data = _ref.data, sameQueryData = _ref.sameQueryData; var result = void 0; if (_this5.canRender() && !_this5.hasUnavailableMetadataColumns() && !_this5.hasMissingFilters()) { // preserve the query results _this5._dataObject = data; if (sameQueryData) { result = { data: data, sameQueryData: sameQueryData }; } else { _this5.predictHandler.resetCustomData(); _this5._viprSlotMapping = VIPRUtils.createSlotMapping(_this5.visualization); result = { data: data }; } } else { result = { data: {} }; } return result; }); }, /** * Set a model property if its valid. * @param {String} propertyName - name of the property of interest. * @param {String} visId - id of the visualization of interest. */ _setModelProperty: function _setModelProperty(propertyName, visId) { if (this._shouldPropertyBeApplied(propertyName)) { var val = this._getPropValueWhenSaving(visId, propertyName); var propInfo = { id: propertyName, value: val }; this.setProperty(propInfo); } }, /** * set properties needed before data is set. */ _setPreDataProperties: function _setPreDataProperties() { var _this6 = this; // Check for such properties in the vipr config var visId = this.visAPI.getVisId(); var config = VIPRConfig.getConfig(visId); if (config && config.propertiesToSetBeforeData && config.propertiesToSetBeforeData.length > 0) { config.propertiesToSetBeforeData.forEach(function (propertyName) { _this6._setModelProperty(propertyName, visId); }); } }, /** * handles when data is ready before setting * @param {Object} renderContext */ whenSetDataReady: function whenSetDataReady(renderContext) { var result = Promise.resolve(true); if (this.canRender() && !this.hasUnavailableMetadataColumns() && !this.hasMissingFilters()) { var autoRefresh = !!(renderContext && renderContext.extraInfo && renderContext.extraInfo.queryRefresh); // we only care if its the same query IFF the query is an auto refresh query // otherwise the query is considered valid to construct a new VIPRData var sameQueryData = autoRefresh ? !!renderContext.sameQueryData : false; var requiresNewVIPRData = this.viprData ? !sameQueryData : true; if (requiresNewVIPRData) { this._isAnnotated = false; // Some properties, such as custom polygons, need to be set before data is set. Do so now. this._setPreDataProperties(); // preserve the query results this._dataObject = renderContext.data; this._viprSlotMapping = VIPRUtils.createSlotMapping(this.visualization); this.viprData = this.createViprData(this._dataObject); if (this.viprData) { try { this.viprWidget.setData(this.viprData, this._viprSlotMapping); } catch (e) { result = Promise.reject(e); } } else { result = Promise.reject(new Error('Unable to create VIPR data in whenSetDataReady')); } } } else { //for cases that that we can render without complete mapping and there is no required slots like schematic, we need to check the slots, // if not data item is mapped, so we need to clear data var isMappingComplete = this.isMappingComplete() && this.visualization.getSlots().getMappingInfoList().length > 0; if (this.isRenderWithoutCompletingMapping() && !isMappingComplete) { this.viprWidget.setData(null); if (this.infoIndicator) { this.infoIndicator.reset(); } } } return result; }, _getAnnotationMessages: function _getAnnotationMessages(facetData) { return facetData.getMessages() || {}; }, whenAnnotatedResultsReady: function whenAnnotatedResultsReady() { var _this7 = this; return VIPRView.inherited('whenAnnotatedResultsReady', this, arguments).then(function (data) { if (data) { // preserve the result object which holds the annotation titles _this7._dataObject = data._aQueryResults && data._aQueryResults.length ? data : _this7._dataObject; if (!_.isEmpty(_this7.content.getFeature('Forecast').getForecastInfo().annotations)) { _this7.viprData = _this7.createViprData(_this7._dataObject); _this7.viprWidget.setData(_this7.viprData, _this7._viprSlotMapping); } // preserve the query results _this7._queryResults = data.getQueryResults(); _this7._isAnnotated = true; _this7.visAPI.setAnnotationMessages(_this7._getAnnotationMessages(data)); } return data; }).catch(function (e) { throw e; }); }, onShowTitle: function onShowTitle(event) { this._hasUserTitle = event.value !== 'noTitle'; if (this._hasUserTitle) { this._hasAnnotationTitle = false; } }, /** * VIPR view should use VIPR's built-in animations and not do the default animations. */ // @Override animate: function animate() {}, _readyForRender: function _readyForRender() { return (this.canRender() || this.isRenderWithoutCompletingMapping()) && !this.hasUnavailableMetadataColumns() && !this.hasMissingFilters(); }, _updateViprProp: function _updateViprProp(viprPropId, viprPropValue) { // we need to explicitly update property value so that dirty flag would be set and users will be aware. var options = { merge: true, remove: false, silent: false }; if (viprPropId !== undefined && viprPropValue !== undefined) { this.visModel.ownerWidget.updateVisProperties({ 'id': viprPropId, 'value': viprPropValue && viprPropValue.name ? viprPropValue.name : viprPropValue }, options); } }, _onViprPropValueChange: function _onViprPropValueChange(viprProp) { var viprPropId = viprProp.property.getName(); var viprPropValue = viprProp.property.getValue(); /* * It seems like dashboard is storing the object structures when persisting length properties. * This will (e.g. for Length) return an object { value:15, unit:"px" }. However, if vida starts adding * internal variables on the object, these will unnecessarily end up in the serialization. * If we want to store property values in a spec, need to serialize them by calling 'Prop.toString()'. * This will yield e.g. "15px" for the above object. * They can then simply be reconstituted into objects by calling Length.parse("15px"). * This applies to all complex properties, e.g. Color, Palettes, Length, Font etc. * NOTE: Have a discussion with Shawn, this is a temporary fix to have the minimum impact on the product, * need a generic fix in future */ if (viprProp.property.type === 'length') { viprPropValue = viprPropValue ? viprPropValue.toString() : viprPropValue; } this._updateViprProp(viprPropId, viprPropValue); }, _allowShowTabs: function _allowShowTabs() { return this.ownerWidget.allowShowTabs && this.ownerWidget.allowShowTabs(); }, _renderTabs: function _renderTabs() { // ensure the a tabs are visible with the selected tab if (this._tabs.getTabsCount() > 0 && this._allowShowTabs()) { this._tabs.show(); this._tabs.selectTab(this.visModel.getPropertyValue('actions')); } else { this._tabs.hide(); } }, /** * Render the VIPR visualization */ render: function render(renderInfo) { var _this8 = this; VIPRView.inherited('render', this, [renderInfo, /*callRenderComplete*/false]); this._destroyCustomVisShowError(); if (!this.viprWidget) { return Promise.resolve(); } if (this._renderCustomVisContext()) { renderInfo.widgetSize.height = this.$el.closest('.liveWidgetContent').height(); } this.resizeToWidget(renderInfo); if (!this._readyForRender()) { return VisUtil.validateVisDefinition(this.content, this.dashboardApi, { visId: this.ownerWidget.model.visId }).then(function (isValid) { if (isValid) { _this8.renderIconView(); } else { var $originalContentBeforeError = _this8.$el.children(); $originalContentBeforeError.detach(); _this8.sdkShowError = new SdkShowError(_this8.$el[0], stringResources.get('extVisNoSDKConnection'), stringResources.get('extVisNoSDKPreviewMsg'), 'disconnect'); _this8.sdkShowError.showError(); } }); } this._renderTabs(); this.removeIconView(); //Listen for property value change if any, add a flag to prevent registering the same listener multiple times if (!this.propChangeslistener) { this._registerListenerForPropChanges(); } return this._applySavedModelProperties(renderInfo).then(function () { if (!_this8.visModel.getRenderSequence().isActive()) { return; } var augmentations = _this8.visAPI.ownerWidget.model.augmentation; //TODO: add getAugmentations to visAPI if (augmentations && augmentations.length > 0) { for (var i = 0; i < augmentations.length; i++) { var augmentation = augmentations[i]; _this8._appendDVI(augmentation); } } return _this8.renderSelected(renderInfo); }); }, /** * Render items that are specific to custom visualizations (ex. Custom Vis Preview Message) */ _renderCustomVisContext: function _renderCustomVisContext() { if (this.ownerWidget.model.visId === 'visualizationPreview') { var iconsFeature = this.dashboard.getFeature('Icons'); var $parent = $(this.$el.closest('.widgetContentWrapper')); if ($parent.find('.customVisPreviewMessage').length < 1) { this.$customVisPreviewMessage = $('
'); this.$customVisPreviewMessage.addClass('customVisPreviewMessage'); $parent.prepend(this.$customVisPreviewMessage); ReactDOM.render(React.createElement(CustomVisPreviewMessage, { iconsFeature: iconsFeature }), this.$customVisPreviewMessage[0]); } return true; } return false; }, reveal: function reveal() { var _this9 = this; if (BrowserUtils.isIE11() || !this._readyForRender() || !this._getEntryDurationProperty() || !this.viprData) { this.visAPI.renderCompleteBeforeAnimation(); return Promise.resolve(); } // reveal is a render which triggers the widget's entry level animation. // This is done by clearing and resetting the data as there is no API for it atm. var emptyData = this.viprWidget.processData(this.viprData, this._viprSlotMapping).empty(); this.viprWidget.setData(emptyData, this._viprSlotMapping); this._applyEntryDurationProperty('zero'); return this.renderVIPRControl().then(function () { _this9._applyEntryDurationProperty('model'); _this9.viprWidget.setData(_this9.viprData, _this9._viprSlotMapping); _this9.renderVIPRControl(); return { // renderControlApi is a thenable object (has a then method), // but it's not a pure promise since it has other methods added by VIDA. // if we return it directly those api's get lost. renderControlApi: _this9.renderControlApi }; }).catch(function (e) { _this9.viprWidget.setData(_this9.viprData, _this9._viprSlotMapping); throw e; }); }, getCurrentViewSelector: function getCurrentViewSelector() { return this.eventHandler; }, /** * @private * TODO: SmartsExecution requests are using a legacy QueryResultsObject. * Annotations should be moved to a feature and updated with proper api's for decorations. * In the meantime, the response value indexes and decorations can be accessed via the "pt" array and "deco" member. * @param {Object} value - datapoint value * @return {String} unique string representing the datapoint */ _getDatapointKeyFromSmartAnnotationsResult: function _getDatapointKeyFromSmartAnnotationsResult(value) { // generate a unique key that consists of the category indices // without the ordinal values // INPUT: [2, 1, {v: 886.970000016}] // OUTPUT: '2,1' return value ? _.filter(value.pt, function (pt) { return typeof pt === 'number'; }).toString() : ''; }, _decorateDatapoints: function _decorateDatapoints(dataset, datasetIndex) { var it = dataset.getDataPointIterator(); var selection = this.getSelector().getSelection(ContentUtil.getColumnIdList(dataset.slots || this.visualization.getSlots().getMappedSlotList(), 'attribute')); var queryResult = this._isAnnotated ? this._queryResults[datasetIndex] : null; var facetData = queryResult ? queryResult.getFacetData() : null; var annotatedDatapoints = facetData ? facetData.getDatapoints() : null; var nextValue = it.nextValue(); var index = 0; while (nextValue !== null) { if (nextValue.decorate && nextValue.hasDecoration) { if (annotatedDatapoints && annotatedDatapoints[index] && // When dashboard submits a stats query for smart annotations, // certain rows may be removed when attempting to calculate using columns with no values. // As a result the stats query (and SA execution query) may have less rows compared to the main data query. // This code ensures they match. nextValue.getDataPointKey() === this._getDatapointKeyFromSmartAnnotationsResult(annotatedDatapoints[index])) { var decorations = annotatedDatapoints[index++].deco; if (decorations !== undefined) { _.each(decorations, function (value, key) { nextValue.decorate(key, value); }); } else { nextValue.clearDecorations(); } } else { nextValue.clearDecorations(); } // Decorate only if needed. Avoid re-decorating with the same decorations. if (selection && selection.isSelected(it.getTupleItems())) { nextValue.decorate('selected', true); } else if (nextValue.hasDecoration('selected')) { nextValue.decorate('selected', false); } } nextValue = it.nextValue(); } return !!selection; }, _decorateDataItems: function _decorateDataItems(dataset, datasetIndex) { var _this10 = this; var decorated = false; var queryResult = this._isAnnotated ? this._queryResults[datasetIndex] : null; var annotatedData = queryResult ? queryResult.getFacetData() : null; var _loop = function _loop(iDataItem) { var dataItem = dataset.getDataItem(iDataItem); // decorate annotations if (annotatedData) { var annotatedDataItem = annotatedData.getResultDataItem(iDataItem); if (annotatedDataItem) { var decoration = annotatedDataItem.getDecoration(); if (_.isEmpty(decoration)) { dataItem.clearDecorations(); } else { _.each(decoration, function (value, key) { dataItem.decorate(key, value); }); } } } else { dataItem.clearDecorations(); } // Maintain any decoration that were added using the widget decorator API if (_this10.dataItemDecorations[datasetIndex] && _this10.dataItemDecorations[datasetIndex][iDataItem]) { var dataItemDecorations = _this10.dataItemDecorations[datasetIndex][iDataItem]; var decoName = void 0; for (decoName in dataItemDecorations) { if (dataItemDecorations[decoName] !== null && dataItemDecorations[decoName] !== undefined) { dataItem.decorate(decoName, dataItemDecorations[decoName]); } } } // decorate selection if (dataItem.getType() === 'cat') { (function () { // collect the ItemIds (single or nested) var itemIds = []; var aItemClassSet = dataItem.getItemClassSet(); for (var iItemClass = 0; iItemClass < aItemClassSet.getItemClassCount(); iItemClass++) { itemIds.push(aItemClassSet.getItemClass(iItemClass).getUniqueName()); } var selection = _this10.getSelector().getSelection(itemIds); var selectedVIPRItem = {}; var allVIPRItem = {}; for (var iTuple = 0; iTuple < dataItem.getTupleCount(); iTuple++) { var tuple = dataItem.getTuple(iTuple); tuple.items.forEach(function (VIPRItem) { return allVIPRItem[VIPRItem.getUniqueName()] = VIPRItem; }); var tupleItems = []; for (var iTupleItem = 0; iTupleItem < tuple.getItemCount(); iTupleItem++) { tupleItems.push(tuple.getItem(iTupleItem).item); } // unlike the datapoint decorations, each legend items need to be explictly selected/unselected if (selection) { var isTupleSelected = !!selection.isSelected(tupleItems); tuple.decorate('selected', isTupleSelected); isTupleSelected && tuple.items.forEach(function (VIPRItem) { selectedVIPRItem[VIPRItem.getUniqueName()] = VIPRItem; }); decorated = true; } else { tuple.decorate('selected', false); tuple.items.forEach(function (VIPRItem) { VIPRItem.decorate('selected', false); }); } } // handle VIPRItem selection Object.values(selectedVIPRItem).forEach(function (viprItem) { return viprItem.decorate('selected', true); }); Object.keys(allVIPRItem).filter(function (key) { return !selectedVIPRItem[key]; }).forEach(function (itemKey) { allVIPRItem[itemKey].decorate('selected', false); }); })(); } }; for (var iDataItem = 0; iDataItem < dataset.getDataItemCount(); iDataItem++) { _loop(iDataItem); } return decorated; }, decorateData: function decorateData(viprData) { var decorations = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : { dataPoints: true, dataItems: true, selection: true, annotations: true }; var dataset = void 0; for (var i = 0; i < viprData.getDataSetCount(); i++) { dataset = viprData.getDataSetAt(i); if (dataset) { var dataItemsDecorated = false; if (decorations.dataItems) { dataItemsDecorated = this._decorateDataItems(dataset, i); } var datapointsDecorated = false; if (decorations.dataPoints) { datapointsDecorated = this._decorateDatapoints(dataset, i); } if (decorations.selection) { // The hasSelection decoration tells the visualization if there is any selection in play, and changes // how items are rendered by default. dataset.decorate('hasSelection', datapointsDecorated || dataItemsDecorated); } if (decorations.annotations) { // Check for annotation type var meaningfulDiff = _.where(this.visAPI.getEnabledAnnotations(), { type: 'MEANINGFUL_DIFFERENCES' }); dataset.decorate('hasAnnotations', !!meaningfulDiff.length); } } } }, renderSelected: function renderSelected(renderInfo) { var _this11 = this; if (!this.visModel.getRenderSequence().isActive()) { return Promise.resolve(); } if (this.viprData) { this.decorateData(this.viprData); } // restore the visualization state if (this.eventHandler) { this.eventHandler.restoreState(); } var perfMarkName = 'WidgetRender_' + this.visModel.getWidgetId(); PerfUtils.createPerformanceMark({ 'component': 'dashboard', 'name': perfMarkName, 'state': 'start' }); return this.renderVIPRControl({ renderInfo: renderInfo, callRenderComplete: true }).then(function () { PerfUtils.createPerformanceMark({ 'component': 'dashboard', 'name': perfMarkName, 'state': 'end' }); if (!_this11.visModel.getRenderSequence().isActive()) { return; } }); }, renderVIPRControl: function renderVIPRControl() { var _this12 = this; var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : { callRenderComplete: false }; var renderInfo = options.renderInfo, callRenderComplete = options.callRenderComplete; // complete the previous render if it's still running. if (this.renderControlApi && this.renderControlApi.complete) { this.renderControlApi.complete(); } this.viprControlRenderInProgress = true; this.renderControlApi = this.viprWidget.render('client'); this.renderControlApi.onprogress = function (_message) { if (_message.status === 'animating') { _this12.visAPI.renderCompleteBeforeAnimation(); } }; return this.renderControlApi.then(function () { callRenderComplete && _this12.visAPI.renderComplete(renderInfo); _this12._deferredDestroyVIPRWidgets(); _this12.viprControlRenderInProgress = false; }).catch(function (e) { throw e; }); }, /** * check if the value of a property matched expected * @param {String} propertyName * @param {object} expectedValue * @return {boolean} true if it matches */ doesVisPropertyMatchExpected: function doesVisPropertyMatchExpected(propertyName, expectedValue) { if (propertyName && this.viprWidget) { return VIPRUtils.doesConfigPropertyMatchExpected(this.visualization.getDefinition().getId(), propertyName, expectedValue); } return false; }, /** * check if auto binning is set for this view. * @return {boolean} true if anto binning can be appied for this view */ canApplyAutoBin: function canApplyAutoBin() { var doesConfigSupportAutobinning = VIPRUtils.canApplyAutoBin(this.visualization.getDefinition().getId()); var definition = this.visualization.getDefinition(); var slots = this.visualization.getSlots().getMappedSlotList(); var slotOneRole = 'explanatory'; var colorSlotRole = 'group'; var doesWidgetHaveBinnableDataItem = false; if (definition.getProperty('canApplyBinning')) { _.find(slots, function (slot) { var role = slot.getDefinition().getRole(); if (slot.getDefinition().getType() !== 'ordinal' && (role === slotOneRole || role === colorSlotRole)) { var fact = slot.getDataItemList().find(function (dataItem) { return dataItem.getMetadataColumn().getType() === 'fact'; }); doesWidgetHaveBinnableDataItem = !!fact; return doesWidgetHaveBinnableDataItem; } }); } return doesConfigSupportAutobinning && doesWidgetHaveBinnableDataItem; }, /** * @returns the default binning config for this widget. */ getBinningConfig: function getBinningConfig() { return VIPRUtils.getBinning(this.visualization.getDefinition().getId()); }, /** * @returns the default palette from the colorsService */ _getDefaultContinuousPaletteName: function _getDefaultContinuousPaletteName() { return this.colorsService.getDefaultPaletteName('HeatPalette'); }, /** * @returns the default palette from the colorsSerivce */ _getDefaultCategoricalPaletteName: function _getDefaultCategoricalPaletteName() { return this.colorsService.getDefaultPaletteName('ColorPalette'); }, /** * @param palette - palette property item describing a specific vipr palette * @param possibleDataSlots - all possible data slots for the current vis type. * @returns a continuous heat palette description needed to set a palette resolver in VIPR * eg. * { * type: "cont" * colors: description of colors (either continuous or categorical) * defaultIndex: index into the palette for an element when the color by slot is empty. * } */ _getContinuousColorPaletteDesc: function _getContinuousColorPaletteDesc(viprPalette, possibleDataSlots) { var _this13 = this; // Get either the overriden palette or the default palette var colorPropsCreatorInstance = ColorPropertiesCreator.getInstance(); var dataLayerId = colorPropsCreatorInstance.getDataLayerIdFromPaletteAndSlots(viprPalette, possibleDataSlots); var propertyName = colorPropsCreatorInstance.getContinuousColorPalettePropertyNameWithLayer(dataLayerId); var paletteName = this.visModel.getPropertyValue(propertyName) || this._getDefaultContinuousPaletteName(); return this.colorsService.getPalette({ paletteId: paletteName, type: 'HeatPalette' }).then(function (palette) { var paletteDef = { type: '' }; if (palette) { paletteDef.type = 'cont'; paletteDef.dataLayerId = dataLayerId; var colors = []; /* VIPR had an original issue with the typical way we express our color * stops. We could wait for their fix (its on its way) or work around it. * This is the work around. We want to change our stops to return in this * format: * ["color %", "color %", ...] */ // Order the colors depending on the heat scale color order property. var heatScalePropId = colorPropsCreatorInstance.getHeatScalePalettePropertyNameWithLayer(dataLayerId); var colorOrder = _this13.visModel.getPropertyValue(heatScalePropId); var paletteFills = palette.fills; var paletteLength = paletteFills.length; for (var index = 0; index < paletteLength; index++) { // If the colorOrder is dark to light then reverse the colors. var color = colorOrder !== 'DarkerForLowerValue' ? paletteFills[index].fill : paletteFills[paletteLength - index - 1].fill; var at = paletteFills[index].at; var colorStr = color + ' ' + String(at * 100) + '%'; colors.push(colorStr); } paletteDef.colors = colors; } return paletteDef; }); }, /** * @param {Object} palette - viprWidget palette * @returns secondary color index is needed, undefined otherwise. */ _getSecondaryColorIndexForCatColorPalettes: function _getSecondaryColorIndexForCatColorPalettes(palette) { var defaultSecondaryColor = void 0; var secondaryColorProp = void 0; var singlePaletteProps = VIPRUtils.getSinglePaletteProperties(this.visAPI.getVisId()); if (singlePaletteProps) { Object.values(singlePaletteProps).forEach(function (propDesc) { if (propDesc.defaultColorIndex === 1) { secondaryColorProp = propDesc; } }); } if (secondaryColorProp) { var secondaryValue = this.visModel.getPropertyValue(secondaryColorProp.id); defaultSecondaryColor = secondaryValue !== null ? secondaryValue : 1; // Ensure defaultIndex is not above the number of colors in the current palette defaultSecondaryColor = defaultSecondaryColor < palette.fills.length ? defaultSecondaryColor : 1; } return defaultSecondaryColor; }, /** * @param {Object} palette - viprWidget palette * @returns {Object} primary and secondary color index */ _getColorIndicesForCatColorPalettes: function _getColorIndicesForCatColorPalettes(palette, propertyName) { var defaultIndex = this.visModel.getPropertyValue('defaultPaletteIndex') || this.visModel.getPropertyValue(propertyName + '_defaultIndex') || 0; // Ensure defaultIndex is not above the number of colors in the current palette defaultIndex = defaultIndex < palette.fills.length ? defaultIndex : 0; var defaultSecondaryColor = this._getSecondaryColorIndexForCatColorPalettes(palette); return { defaultIndex: defaultIndex, secondaryIndex: defaultSecondaryColor }; }, /** * @param viprPalette - viprWidget.palettes item describing a specific vipr palette * @param possibleDataSlots - all possible data slots for the current vis type. * @returns a categorical palette description needed to set a palette resolver in VIPR * eg. * { * type: "cat" * colors: description of colors (either continuous or categorical) * defaultIndex: index into the palette for an element when the color by slot is empty. * } */ _getCategoricalColorPaletteDesc: function _getCategoricalColorPaletteDesc(viprPalette, possibleDataSlots) { var _this14 = this; // Get either the overriden palette or the default palette var colorPropsCreatorInstance = ColorPropertiesCreator.getInstance(); var dataLayerId = colorPropsCreatorInstance.getDataLayerIdFromPaletteAndSlots(viprPalette, possibleDataSlots); var propertyName = colorPropsCreatorInstance._getCategoricalColorPalettePropertyNameWithLayer(dataLayerId); var paletteName = this.visModel.getPropertyValue(propertyName) || this._getDefaultCategoricalPaletteName(); return this.colorsService.getPalette({ paletteId: paletteName, type: 'ColorPalette' }).then(function (palette) { var paletteDef = { type: '', colors: '' }; var colorIndices = _this14._getColorIndicesForCatColorPalettes(palette, propertyName); if (palette) { paletteDef.type = 'cat'; paletteDef.colors = palette.fills; paletteDef.defaultIndex = colorIndices.defaultIndex; paletteDef.secondaryColorIndex = colorIndices.secondaryIndex; } return paletteDef; }); }, /** * @param viprPalette - viprWidget.palettes item describing a specific vipr palette * @param possibleDataSlots - all possible data slots for the current vis type. * @returns a palette description needed to set a palette resolver in VIPR * eg. * { * type: "" // cat or cont * colors: description of colors (either continuous or categorical) * defaultIndex: index into the palette for an element when the color by slot is empty. This is only supplied for categorical palettes. * } */ _getPaletteDescForPalette: function _getPaletteDescForPalette(viprPalette, possibleDataSlots) { var getColorPaletteDesc = void 0; switch (viprPalette.paletteType) { case 'cont': case 'customcont': getColorPaletteDesc = this._getContinuousColorPaletteDesc.bind(this); break; case 'cat': case 'single': case 'customcat': getColorPaletteDesc = this._getCategoricalColorPaletteDesc.bind(this); break; default: getColorPaletteDesc = null; } return getColorPaletteDesc ? getColorPaletteDesc(viprPalette, possibleDataSlots).then(function (desc) { desc.type = viprPalette.paletteType; return desc; }) : Promise.resolve({}); }, _getEntryDurationProperty: function _getEntryDurationProperty() { var name = null; var configuration = VIPRConfig.getConfig(this.visAPI.getVisId()); if (configuration && configuration.entryDurationProperty) { name = configuration.entryDurationProperty; } else if (this.viprWidget.properties.get('effect.duration')) { var val = VIPRUtils.getOverridenDefaultForProperty(this.visAPI.getVisId(), 'effect.duration').defaultValue; if (val !== null && val >= 0) { // backward compatible value, if we actually have a value to set (I.e defaultValue is configured) name = 'effect.duration'; } } return name; }, _isDurationPropertyValuePropValid: function _isDurationPropertyValuePropValid(value) { return !(value === undefined || value < 0); }, _applyEntryDurationProperty: function _applyEntryDurationProperty(durationOverwrite) { var propertyName = this._getEntryDurationProperty(); if (!propertyName) { return; } /* * 3 possible values for the duration: * 1) zero - set the value to 0 so there is no animation * 2) model - ask the model for the value to use. If the user has set a value * then it will be used. * 3) if value is still undefined, check our local config for overrides. * * zero is a special case where we set all the animations values to zero. * for now this only includes effect.duration. * */ var value = void 0; var effectValue = VIPRUtils.getOverridenDefaultForProperty(this.visAPI.getVisId(), 'effect.duration').defaultValue || 0; if (durationOverwrite === 'zero') { value = 0; effectValue = 0; } else if (durationOverwrite === 'model') { value = this.visModel.getPropertyValue(propertyName); } //default (and if model has no value) - check for config override. if (!this._isDurationPropertyValuePropValid(value)) { value = VIPRUtils.getOverridenDefaultForProperty(this.visAPI.getVisId(), propertyName).defaultValue; } // If we still don't have a value, forget it, we'll use the default. if (this._isDurationPropertyValuePropValid(value)) { this.setProperty({ id: 'effect.duration', value: effectValue }); this.setProperty({ id: propertyName, value: value }); } }, /** * Single palettes are handled differently than categorical or continuous * For one, there is always two of them. Secondly, both the single palette * resolvers must use the same categorical palette (or at least same colors) */ _applySavedSingleColorPalette: function _applySavedSingleColorPalette() { var _this15 = this; // Create the categorical palette resolver needed for the single palette. return this._getCategoricalColorPaletteDesc().then(function (catPaletteDesc) { return VIPRProperties.getInstance().setSinglePaletteResolvers(_this15.viprWidget, catPaletteDesc); }); }, /** * VIPR has some properties in its bundles that are handled in a different way. * We have to protect against setting them using the properties API as there * could be unexpected side affects. * @param property - specific vipr defined property in question * @returns true iff the property should be applied * @// TODO: replace this list with dontApply: true in the VIPR config */ _shouldPropertyBeApplied: function _shouldPropertyBeApplied(propertyName) { var blackList = ['legend.display', 'legend.position', 'effect.duration', 'effect.entry.line.duration', 'effect.entry.bar.duration']; blackList = blackList.concat(this._getDontApplyProperties()); return !(blackList.indexOf(propertyName) !== -1); }, _getDontApplyProperties: function _getDontApplyProperties() { var dontApplyProperties = []; //Since the VIDA 2.10 change, VIDA is now treating color palettes like properties. //They are now included as part of the VIPRWidget properties. //Therefore, we dont want to override the palettes with the default value //Add them to the list of dontApplyProperties var colorPalettes = VIPRUtils.getPalettes(this.viprWidget.properties, this.visualization.getDefinition().getId()); colorPalettes.forEach(function (palette) { if (palette.name !== 'colors.series') { //colors.series is marked in VIPRConfig to not set the default value dontApplyProperties.push(palette.name); } }); return dontApplyProperties; }, /** * Set the palette resolvers for the categorical and continuous palettes. */ _applySavedNonSingleColorPalette: function _applySavedNonSingleColorPalette() { var _this16 = this; var promises = []; // Only apply the palettes associated with the current visualization. var possibleDataSlots = this.visualization.getSlots().getSlotList(); VIPRUtils.getPalettes(this.viprWidget.properties, this.visualization.getDefinition().getId()).forEach(function (palette) { promises.push(_this16._getPaletteDescForPalette(palette, possibleDataSlots).then(function (paletteDef) { // Make sure there is avalid defintion for the palette type. if (paletteDef) { VIPRProperties.getInstance().setPaletteResolvers(palette, paletteDef, possibleDataSlots, _this16.visAPI.getFredIsRed()); } return paletteDef; })); }); return Promise.all(promises); }, /** * Apply all palette properties relevant to the current visualization */ _applySavedColorPalette: function _applySavedColorPalette() { var isThereSinglePalettes = VIPRUtils.getSinglePalettes(this.viprWidget.properties, this.visualization.getDefinition().getId()).length > 0; if (isThereSinglePalettes) { return this._applySavedSingleColorPalette(); } else { return this._applySavedNonSingleColorPalette(); } }, /** * VIPR will optimize the size of a visualization when told (i.e. remove axis labels * when it thinks more space should be given to the visual elements). We want * to enable this be default or if we are showing small multiples. It should * only be disabled for upgraded content. */ _setOptimizedSizeProperty: function _setOptimizedSizeProperty(visId) { var propertyId = 'optimizeSize'; var defaultValue = this._getPropValueWhenSaving(visId, propertyId); if (!defaultValue) { // Do we have small multiple mapped slots? var aMultipliers = _.filter(this.visualization.getSlots().getMappedSlotList(), function (dataSlot) { return dataSlot.getDefinition().getProperty('multiplier') === true; }); // If we have small multiple mapped slots then lets optimize. if (aMultipliers && aMultipliers.length > 0 || this.visAPI.isOptimizeForSize()) { defaultValue = true; } } var propInfo = { id: propertyId, value: defaultValue === undefined || defaultValue === null ? true : defaultValue // true by default }; this.setProperty(propInfo); }, /** * @return the default background color in hex */ _getDefaultBackgroundColorInHex: function _getDefaultBackgroundColorInHex() { var defaultSpecColor = this.colorsService.getPropertyForUIElement('widget', 'backgroundColor').value; // The value may be equal to a simple hex or variable name or it may be an object which we have to dig into a little more. if (_.isObject(defaultSpecColor)) { defaultSpecColor = defaultSpecColor.color; } // I love themes and the theme definition object . The color may come back // as a nice hex value or as a variable that needs to be mapped. If its a variable it will begin with // a '$' and we have to map it to get the hex value. if (defaultSpecColor.substring(0, 1) === '$') { defaultSpecColor = this.colorsService.getValueForVariable('Color', defaultSpecColor.substr(1)); } // Ensure we have a valid hex value or transparent if (defaultSpecColor.substring(0, 1) === '#' && !isNaN(parseInt(defaultSpecColor.substr(1), 16)) || defaultSpecColor === 'transparent') { return defaultSpecColor; } return null; }, /** * Set the background color of the viprwidget */ _setBackgroundColor: function _setBackgroundColor() { var _this17 = this; if (this.highContrastEnabled) { //Clear background and foreground element colors in high-contrast mode and allow idvis to determine them. this.setProperty({ id: 'backgroundColor', value: undefined }); var fgElementProps = this.colorsService.getForegroundPropertiesForUIElement(); var propMap = {}; this.visModel.getDefinition().themeMapping.forEach(function (prop) { propMap[prop.id] = prop.mapping; }); fgElementProps.forEach(function (prop) { if (propMap[prop.id]) { propMap[prop.id].forEach(function (mappedPropName) { _this17.setProperty({ id: mappedPropName, value: undefined }); }); } else { _this17.setProperty({ id: prop.id, value: undefined }); } }); } else { // Get the current fill color stored in our model var fillColor = this.visModel.ownerWidget.model.fillColor; var hexFillColor = void 0; if (fillColor) { // If there is a defined fill color its in the form of 'colorx', we need // it in the hex form. Get the hex value and set the property. hexFillColor = this.colorsService.getHexColorFromDashboardColorSet(fillColor); } else { // Get the default value and set the property. hexFillColor = this._getDefaultBackgroundColorInHex(); } var propInfo = { id: 'backgroundColor', value: hexFillColor }; this.setProperty(propInfo); } }, _getColorToSave: function _getColorToSave(savedValue, isCustomVis) { var colorToSave = void 0; if (_.isString(savedValue)) { if (this.colorsService.isCustomColor(savedValue)) { this.colorsService.addCustomColor(this.colorsService.getHexColorFromClassName(savedValue)); } colorToSave = this.colorsService.getHexColorFromDashboardColorSet(savedValue); } else { if (isCustomVis) { savedValue = this.colorsService.makeSureColorIsValidInModel(savedValue); } colorToSave = savedValue; } return colorToSave; }, /** * Apply the color set property of the specified property id * @param {String} visId - visualization type id * @param {String} propId - id of the color set property to set */ _applyColorSetProperty: function _applyColorSetProperty(visId, propId, isCustomVis) { var savedValue = this._getPropValueWhenSaving(visId, propId); // Only apply the color if we have something saved if (savedValue) { var colorToSave = this._getColorToSave(savedValue, isCustomVis); // Put the information in a structure Vida understands var colorInfo = { id: propId, value: colorToSave }; this.setProperty(colorInfo); } }, /** * @param {String} visId - id of the current visualization. * @param {Array} properties - array of property names for the specified vis */ _applyColorSetProperties: function _applyColorSetProperties(visId, properties, isCustomVis) { var _this18 = this; properties.forEach(function (propertyName) { var property = _this18.visModel.getPropertyById(propertyName); if (property && property.colorClass) { _this18._applyColorSetProperty(visId, propertyName, isCustomVis); } }); }, _getLengthPropWithPxUnit: function _getLengthPropWithPxUnit(value) { if (typeof value !== 'undefined' && value !== null) { return value && value.endsWith && value.endsWith('px') ? value : value + 'px'; } return null; }, _applyTargetMarkerThicknessProperties: function _applyTargetMarkerThicknessProperties(visId) { var targetThickness = this._getPropValueWhenSaving(visId, 'target.marker.thickness'); if (VIPRProperties.getInstance().isTargetThicknessInputValid(targetThickness)) { var valueToSet = this._getLengthPropWithPxUnit(targetThickness); var targetThicknessInfo = { id: 'target.marker.thickness', value: valueToSet }; this.setProperty(targetThicknessInfo); } }, /** * Apply any saved layer transparencies. * Only applies to visualizations that support layers. */ _applySavedLayerTransparencies: function _applySavedLayerTransparencies() { var configuration = VIPRConfig.getConfig(this.visAPI.getVisId()); if (configuration && configuration.supportsLayers) { configuration.layerDescriptions.forEach(function (layerDesc) { var val = this.visModel.getPropertyValue(layerDesc.colorRelatedProps.transparencyProp); if (val !== undefined && val !== null) { // Can be false var propInfo = { id: layerDesc.colorRelatedProps.transparencyProp, value: val * 100 // Property is saved from 0.0 to 1.0, we want 0-100 }; this.setProperty(propInfo); } }.bind(this)); } }, /** * @returns true iff the value is not undefined. */ _isPropValueValid: function _isPropValueValid(value, propDesc) { var isPropValidByDescCheck = propDesc.checkForValidValue ? propDesc.checkForValidValue(value) : true; return isPropValidByDescCheck && value !== undefined; }, /** * @returns true if the prop is inactive, readonly and has a valid default value */ _isValidReadOnlyProperty: function _isValidReadOnlyProperty(propDesc) { return propDesc && propDesc.isActive === false && propDesc.isReadOnly === true && this._isPropValueValid(propDesc.defaultValue, propDesc); }, /** * So, Vida will provide us with defaults for value and default value among * other things. We need these for properties such as themeable props. One * thing we don't need is all the vida property methods that come alogn with * the property. So, we want to overide our model prop with wanted object * properties only. Note, calling this will actually override the property * in the model! * @param {String} propertyName - id of the property of interest. */ overrideWithVidaProperty: function overrideWithVidaProperty(propertyName) { var modelProp = this.visModel.getPropertyById(propertyName); var vidaProp = this.viprWidget.properties.get(propertyName); if (vidaProp && modelProp) { // Should never happen but Vida is missing some deprecated props right now. var propsWeWantFromVidaProp = { active: vidaProp.active, caption: vidaProp.caption, defaultValue: vidaProp.defaultValue, description: vidaProp.description, name: vidaProp.name, type: vidaProp.type, possibleValues: vidaProp.possibleValues }; if (modelProp.value !== undefined) { _.extend(propsWeWantFromVidaProp, { value: vidaProp.value }); } _.defaults(modelProp, propsWeWantFromVidaProp); } }, /** * When saving the values for a property it can be a little tricky. For example, * if you set the stacked parameter to false in an area chart and then change the * vis type to a stacked bar chart we still want it to stack. So, we should * check the property to see if its active. If it isn't then we have to do a * little priority checking. * @param {String} visId - bundle id * @param {String} propertyName - name of the property of interest * @returns value of the property to use when saving. */ _getPropValueWhenSaving: function _getPropValueWhenSaving(visId, propertyName) { var result = null; var propDesc = VIPRUtils.getOverridenDefaultForProperty(visId, propertyName); /* * Do we have a readonly property (i.e. one that has a default and can't be * overriden by a user or programmatically)? If so, use the defaultValue */ if (this._isValidReadOnlyProperty(propDesc)) { result = propDesc.defaultValue; } else { // Get any vida defaults we may need. this.overrideWithVidaProperty(propertyName); // Now we should look at the model. If there is a valid (non undefined) value there // use it, otherwise check for the defaultValue in the config. var modelValue = this.visModel.getPropertyValue(propertyName); if (this._isPropValueValid(modelValue, propDesc)) { result = modelValue; } else { result = propDesc.defaultValue; } } return result; }, _registerListenerForPropChanges: function _registerListenerForPropChanges() { var _this19 = this; var listenForPropChanges = this.visAPI.getListenForPropChangesFromDefinition(); if (listenForPropChanges) { _.each(listenForPropChanges, function (prop) { _this19.viprWidget.properties.get(prop).on('value', _this19._onViprPropValueChange.bind(_this19)); }); //should update the flag when there are actual listeners have been registered this.propChangeslistener = true; } }, /** * When a visualization is about to be rendered we want to ensure that the * visualization properties are all set to the user overriden value, if any. */ _applySavedModelProperties: function _applySavedModelProperties(renderInfo) { var _this20 = this; return this._applySavedColorPalette().then(function () { var visId = _this20.visAPI.getVisId(); var configuration = VIPRConfig.getConfig(visId); var configInclude = configuration && configuration.config && configuration.config.include ? configuration.config.include : []; if (configuration && configuration.isCustomVis && configuration.bundleInclude) { configInclude = configuration.bundleInclude; } configInclude.forEach(function (propertyName) { _this20._setModelProperty(propertyName, visId); }); _this20._applySavedLayerTransparencies(); if (renderInfo && renderInfo.extraInfo && renderInfo.extraInfo.preserveDrawingBuffer === true) { _this20.setProperty({ id: 'webGL.preserveDrawingBuffer', value: true }); } else { _this20.setProperty({ id: 'webGL.preserveDrawingBuffer', value: false }); } // Now that the default value for optimize size is set, override it to what we really want. _this20._setOptimizedSizeProperty(visId); _this20._setBackgroundColor(); _this20._applyEntryDurationProperty(renderInfo && renderInfo.extraInfo && renderInfo.extraInfo.entryDuration); _this20._applyTargetMarkerThicknessProperties(visId); // Apply all color set property values as denoted in VIPRConfig configuration && _this20._applyColorSetProperties(visId, configuration.config.include, configuration.isCustomVis); }); }, /** * @returns array of run time properties in a format usable by the properties panel. */ getProperties: function getProperties() { var _this21 = this; return this.whenVisControlReady().then(function () { return VIPRProperties.getInstance().getProperties(_this21.viprWidget.properties, _this21.visualization.getSlots().getSlotList(), _this21.visualization.getDefinition().getId()); }).then(function (properties) { //todo ???widget.getFeature? is it dead code?? livewidget_cleanup var propertiesFilter = _this21.visModel.ownerWidget.getFeature('visPropertiesFilter'); return propertiesFilter ? propertiesFilter.filter(properties) : properties; }); }, getPropertyList: function getPropertyList() { return VIPRProperties.getInstance().getPropertyList(this.viprWidget.properties, this.visualization.getSlots().getSlotList(), this.content); }, getPropertyLayoutList: function getPropertyLayoutList() { return VIPRProperties.getInstance().getPropertyLayoutList(this.viprWidget.properties, this.visualization.getSlots().getSlotList(), this.content, this.dashboard); }, /** * When a theme changes we expect that all the models and their theme definitions have been * updated before we are notified. Then all we have to do is re-render. * * NOTE: Only Rave and VIPR Views have to do this as all other (current views) use css. */ onChangeTheme: function onChangeTheme() { this.visModel.getRenderSequence().reRender(); }, /** * Set the specified property to the specified value * @param - info -> property of interest * { * id - property id, * value - new value for the prop * } */ setProperty: function setProperty(info) { VIPRProperties.getInstance().setProperty(this.viprWidget, info); }, /** * Generate thumbnail * * @param {Object} [options] - generate options * @param {Object} [options.size] - aspect ratio to generate * @param {number} [options.size.width=150] - aspect ratio width * @param {number} [options.size.height=100] - aspect ratio height * @param {string} [options.type=svg] - type of thumbnail to generate * * @return {Promise} */ generateThumbnail: function generateThumbnail() { var _this22 = this; var _ref2 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, _ref2$size = _ref2.size, size = _ref2$size === undefined ? {} : _ref2$size, _ref2$type = _ref2.type, type = _ref2$type === undefined ? 'svg' : _ref2$type; var _getThumbnailConfig = this.getThumbnailConfig(), _getThumbnailConfig$e = _getThumbnailConfig.enabled, enabled = _getThumbnailConfig$e === undefined ? true : _getThumbnailConfig$e; var isMappingIncomplete = false; var isDisabled = !enabled; if (!this.isMappingComplete()) { isMappingIncomplete = true; } var promise = void 0; if (isDisabled || isMappingIncomplete) { promise = Promise.resolve(); } else { var _getThumbnailConfig2 = this.getThumbnailConfig(), configType = _getThumbnailConfig2.type, _getThumbnailConfig2$ = _getThumbnailConfig2.properties, configProperties = _getThumbnailConfig2$ === undefined ? {} : _getThumbnailConfig2$; var width = size.width, height = size.height; if (configType) { type = configType; } var handler = this._createPredictHandler(VIPRCachedDataRequestHandler, this.predictHandler); promise = this.viprWidget.createHidden(width, height, type, true, IGNORED_THUMBNAIL_DECORATIONS, handler).then(function (_hiddenViprWidgetInstance) { var properties = Object.assign({}, DEFAULT_THUMBNAIL_PROPERTIES, configProperties); Object.keys(properties).forEach(function (key) { var value = properties[key]; _hiddenViprWidgetInstance.setProperty(key, value); }); return _hiddenViprWidgetInstance.render(type).then(function (_renderOperation) { var result = void 0; if (_renderOperation && _renderOperation.completed) { result = _this22.processThumbnailData(_renderOperation.data); } _hiddenViprWidgetInstance.destroy(); return result; }); }); } return promise.then(function (thumbnail) { return { thumbnail: thumbnail, isMappingIncomplete: isMappingIncomplete, isDisabled: isDisabled }; }); }, processThumbnailData: function processThumbnailData(data) { var _this23 = this; var result = void 0; if (data instanceof Blob) { result = new Promise(function (resolve, reject) { try { var reader = new FileReader(); var altThumbnailImage = stringResources.get('loadedThumbnailImage', { visType: _this23.visModel.getDefinition().label }); reader.addEventListener('loadend', function () { resolve('' + altThumbnailImage + ''); }); reader.readAsDataURL(data); } catch (error) { reject(error); } }); } else { result = Promise.resolve(data); } return result; }, /** * Thumbnail config * * @return {Object} thumbnail config */ getThumbnailConfig: function getThumbnailConfig() { var _ref3 = VIPRConfig.getConfig(this.visAPI.getVisId()) || {}, _ref3$thumbnail = _ref3.thumbnail, thumbnail = _ref3$thumbnail === undefined ? {} : _ref3$thumbnail; return thumbnail; }, _isSmartTitleEnabled: function _isSmartTitleEnabled() { var featureChecker = this.dashboard && this.dashboard.getGlassCoreSvc('.FeatureChecker'); if (featureChecker && featureChecker.checkValue) { return !featureChecker.checkValue('dashboard', 'SmartTitle', 'disabled'); } return false; }, /** * check if vipr widget is valid (its id does not exists in the destroyed widgets array) * * @return {Boolean} True, if valid, False otherwise */ _isViprWidgetValid: function _isViprWidgetValid() { var _this24 = this; return !this._deferredDestroyViprWidgetsList.find(function (widget) { return widget.id === _this24.viprWidget.id; }); } }); return VIPRView; }); //# sourceMappingURL=VIPRView.js.map