'use strict'; 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; }; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /** *+------------------------------------------------------------------------+ *| Licensed Materials - Property of IBM *| IBM Cognos Products: Dashboard *| (C) Copyright IBM Corp. 2017, 2020 *| *| US Government Users Restricted Rights - Use, duplication or disclosure *| restricted by GSA ADP Schedule Contract with IBM Corp. *+------------------------------------------------------------------------+ */ define(['jquery', 'underscore', '../../lib/@waca/dashboard-common/dist/utils/EventChainLocal', '../../apiHelpers/SlotAPIHelper', './VisSelectionInfo', './VisTooltipInfo', '../../util/DashboardFormatter', '../interactions/lassoSelect/LassoController', '../../features/content/visualizationGesture/api/VisualizationGestureAPI', '../../lib/@waca/dashboard-common/dist/core/APIFactory'], function ($, _, EventChainLocal, SlotAPIHelper, VisSelectionInfo, VisTooltipInfo, Formatter, LassoController, VisualizationGestureAPI, APIFactory) { 'use strict'; ///////////////////// // VisEventHandler // // options : { // target: { // getTargetElement(): (JQuery element of the target view which the events will be handled.), // getEventTargets(event): (Retrieve a list of event target objects. See EventTarget.), // getTargetsByCords(cords, summarize) (Retrieve an array of targets base on the specified co-ordintates), // decorateTarget(targets, name): (Decorate the given target.), // getDecoration(target, name): (Obtain the decoration value of the given target), // completeDecorations: (End of decorations.) // applyCustomDataSelection(target): Apply custom data selection to target. // getCustomDataSelections(items): Get the list of custom selections // canZoom(): Determines whether the target supports zoom. // zoom(event): Apply zoom to the target with the given event. // zoomStart(event): Begin the zooming session // zoomEnd(event): End the zooming session // pan(event): Apply pan movements to the target // panStart(event): Begin the panning session // panEnd(event): End the panning session // }, // visAPI: (Instance of VisAPI) // } // // EventTarget: { // key: (A unique key representing a target.) // type: (Target type. datapoint | item | itemclass) // source: (original source object of the target.) // values[]: (values of the target in mapped order.) // datasetId: (dataset id of origainl source object) // } // var VisEventHandler = function () { function VisEventHandler(options) { _classCallCheck(this, VisEventHandler); this._init(options); } VisEventHandler.prototype._init = function _init(options) { this._oTimerOut = null; this._lastSelected = null; this._lastHighlighted = null; this._lastImpliedSelection = []; this.dashboard = options.dashboard; this.logger = options.logger; this._target = options.target; this._targetElement = this._target.getTargetElement(); this._ownerWidget = options.ownerWidget; this.content = this._ownerWidget.content; this.state = this.content.getFeature('state'); this.visualization = this.content.getFeature('Visualization'); this._visAPI = options.visAPI; this._visController = this.content.getFeature('InteractivityController.deprecated'); this._selector = this.content.getFeature('DataPointSelections'); this._transaction = options.transaction; this._visActionHelper = this._visController ? this._visController.getActionHelper() : null; // use jQuery touchstart, touchend and touchmove to handle panning to avoid issues with hammerjs drag/start/end var panAndZoomEvents = [{ 'key': 'mousedown', 'callback': this.onMouseDown.bind(this) }, { 'key': 'mouseup', 'callback': this.onMouseUp.bind(this) }, { 'key': 'mousewheel wheel', 'callback': this.onMouseWheel.bind(this) }, { 'key': 'touchstart', 'callback': this.onClickTapPanStart.bind(this) }, { 'key': 'touchend', 'callback': this.onPanEnd.bind(this) }, { 'key': 'touchmove', 'callback': this.onPanMove.bind(this) }, { 'key': 'transformstart', 'callback': this.onPinchStart.bind(this), 'hammer': true }, { 'key': 'transform', 'callback': this.onPinch.bind(this), 'hammer': true }, { 'key': 'transformend', 'callback': this.onPinchEnd.bind(this), 'hammer': true }]; var hoverEvents = [{ 'key': 'mousemove touchmove', 'callback': this.onHover.bind(this) }, { 'key': 'mouseleave touchend', 'callback': this.onMouseLeave.bind(this) }]; var clickEvents = [{ 'key': 'contextmenu', 'callback': this.onClick.bind(this) }, { 'key': 'clicktap', 'callback': this.onClickTapPanStart.bind(this), 'data': { allowPropagationDefaultAction: true } }, { 'key': 'hold', 'callback': this.onHold.bind(this), 'hammer': true }]; var keyEvents = [{ 'key': 'keydown', 'callback': this.onKeyDown.bind(this) }]; this._visControlEvents = []; var interactivitySettings = this._visAPI.getInteractivitySettings(); if (!interactivitySettings.isClickDisabled) { this._visControlEvents.push.apply(this._visControlEvents, clickEvents); this._visControlEvents.push.apply(this._visControlEvents, keyEvents); } if (!interactivitySettings.isPanAndZoomDisabled) { this._visControlEvents.push.apply(this._visControlEvents, panAndZoomEvents); } if (!interactivitySettings.isHoverDisabled) { this._visControlEvents.push.apply(this._visControlEvents, hoverEvents); } this.serializeInteractivity = !!interactivitySettings.serialize; this._edgeSelection = !!options.edgeSelection; this._registerEvents(); this.lassoCtrl = new LassoController({ targetElement: this._targetElement, cordinateResolver: this._resolveCoordinates, isDragging: this._isDragging.bind(this), dashboard: this.dashboard }); // TODO: Remove this check after contentFeatureLoader is available // in live widgets in conversationalPanel if (this._ownerWidget.contentFeatureLoader) { // Add VisualizationGesture feature this._ownerWidget.contentFeatureLoader.registerFeature(this._ownerWidget.id, 'VisualizationGesture', this); } }; VisEventHandler.prototype.getAPI = function getAPI() { if (!this.api) { this.api = APIFactory.createAPI(this, [VisualizationGestureAPI]); } return this.api; }; /** * Handle the app configuration for serialized interactions. * Applications may configure the interactivity of the livewidget * from the perspective file with the following configuration * * config: { * interactions: { * serialize: true * } * } */ VisEventHandler.prototype.isReadyToInteract = function isReadyToInteract() { if (this.serializeInteractivity) { return this.state.getStatus() === this.state.STATUS.RENDERED; } // default: interactivity is always ready return true; }; /** * Restore the interactive state (ie. zoom, pan) to the visualization */ VisEventHandler.prototype.restoreState = function restoreState() { if (this._target.canRestore()) { this._target.restore(this._visAPI.getPropertyValue('interactivityState')); } }; /** * Remove the handler from target */ VisEventHandler.prototype.remove = function remove() { var _this = this; $(document).off('mouseup', this._onMouseUpHandler); $(document).off('touchend', this._onTouchEndHandler); //Release events from the RAVE control. if (this._targetElement) { this._visControlEvents.forEach(function (eventInfo) { if (eventInfo.hammer) { _this._targetElement.hammer().off(eventInfo.key, eventInfo.callback); } else { _this._targetElement.off(eventInfo.key, null, eventInfo.callback); } }); } //prevent from memory leak if (this._target) { this._target.remove(); this._target = null; } this._lastHoverEvent = null; this._tooltipItems = null; this._lastHighlighted = null; this.content = null; if (this.lassoCtrl) { this._endLassoSelection(); this.lassoCtrl.remove(); this.lassoCtrl = null; } }; /** * Register the events to the target */ VisEventHandler.prototype._registerEvents = function _registerEvents() { var _this2 = this; this._onMouseUpHandler = this._onDocumentMouseUp.bind(this); $(document).on('mouseup', this._onMouseUpHandler); this._onTouchEndHandler = this._onDocumentTouchEnd.bind(this); $(document).on('touchend', this._onTouchEndHandler); if (this._targetElement) { this._visControlEvents.forEach(function (eventInfo) { if (eventInfo.hammer) { _this2._targetElement.hammer(eventInfo.hammerOption).on(eventInfo.key, eventInfo.callback); } else { if (eventInfo.data) { _this2._targetElement.on(eventInfo.key, null, eventInfo.data, eventInfo.callback); } else { _this2._targetElement.on(eventInfo.key, null, eventInfo.callback); } } }); } }; /** * Handle mouse hover on Desktop and touchmove event on Mobile * If hover and pan are both enabled, on Mobile touchmove is binded both two event handlers * Both handlers will run and will execute in the order in which they were bound. * For example, in Explore crosshair and pan are both enabled. * For charts whose pan direction is left/right and crosshair is top/bottom, touching move along the diagonal of the chart results * a) pan the chart at left/right direction * b) move the crosshair at top/bottom direction * and vice versa. * * @param event Event Object */ VisEventHandler.prototype.onHover = function onHover(event) { if (!event.clientX) { this._resolveCoordinates(event); } // pan and zoom takes priority if (this._isDragging(event) && !this.lassoCtrl.isLassoMode()) { if (this._target.canZoom() && this._target.pan) { this._target.pan(event); } // This flag is used to detect if we moved the mouse while the mouse is down so that we can ignore the vis selection // Without this, if we pan while we are on top of an element, then the pan is done after the mouse is up, the click is invoked and we select the element this._skipSelection = true; } else { this._skipSelection = !this.isReadyToInteract(); this._skipHoverover = this._skipSelection; } var items = this._target && this._target.getEventTargets ? this._target.getEventTargets(event) : []; if (this._stopHoverover) { // only apply pointer cursor, no tooltips if (this._targetElement) { if (items && items.length > 0) { this._targetElement.addClass('cursor-pointer'); } else { this._targetElement.removeClass('cursor-pointer'); } } return; } // If the mouse is down while it's moving we should never show the tooltip or toolbar actions if (this._mouseDown) { this._clearHoverTimer(); this._visActionHelper.hideToolbarActions(); } this._handleImpliedSelections(items, event.ctrlKey || event.metaKey, false); this._handleHighlights(items); this._handleTooltips(event, items); this._triggerWidgetEvent('visevent:onmove', event, this._target, items, { mouseDown: this._mouseDown, onHold: this._onHold }); }; /** * Handle mouseleave on Desktop and touchmove event on Mobile * * @param event Event object */ VisEventHandler.prototype.onMouseLeave = function onMouseLeave(event) { if (!event.clientX) { this._resolveCoordinates(event); } var items = this._target && this._target.getEventTargets ? this._target.getEventTargets(event) : []; this._lastHoverEvent = event; this._tooltipItems = items; if (this._tooltipItems && this._tooltipItems.length > 0) { this._clearHoverTimer(); } else if (this._lastTooltip) { this._lastTooltip = null; this._clearHoverTimer(); this._oTimerOut = setTimeout(this._handleMouseleaveTimeout.bind(this), 500); } }; VisEventHandler.prototype._handleMouseleaveTimeout = function _handleMouseleaveTimeout() { if (!this._stopHoverover) { this._visActionHelper.hideToolbarActions(); } }; /** * Handle either click or tap event * * @param event Event object */ VisEventHandler.prototype.onClickTap = function onClickTap(event) { return event && event.type === 'tap' ? this.onTap(event) : this.onClick(event); }; /** * Handle mouse click event * * @param event Event object */ VisEventHandler.prototype.onClick = function onClick(event) { if (this._skipSelection) { this.setEventLocalProperty(event, 'preventDefaultContextBar', true); } else { var _getOnClickData2 = this._getOnClickData(event), shouldHandle = _getOnClickData2.shouldHandle, items = _getOnClickData2.items, isMultiSelect = _getOnClickData2.isMultiSelect; if (shouldHandle) { if (items) { if (items.length > 0) { this._handleSelections(event, items, { isMultiSelect: isMultiSelect }); event.preventDefault(); this.setEventLocalProperty(event, 'preventDefaultContextBar', true); } else { //prevent tooltip showing so it does not win over ODT this._clearHoverTimer(); this._clearAll(event); } } } } }; /** * We do not need to check for this._skipSelection because it is only used onHover. * We do not need to close the toolbar because we can only open a new one after closing the current one * when using the keyboard. */ VisEventHandler.prototype.onKeyDown = function onKeyDown(event) { if (this._target) { var result = this._target.processKeyDown(event); if (result) { var items = this._target.getEventTargets(event); if (items && items.length > 0) { this._handleSelections(event, items, { forceRightClick: true }); event.preventDefault(); this.setEventLocalProperty(event, 'preventDefaultContextBar', true); } } } }; /** * Returns the onClick data * * 1. Left Click + isLassoActive : Ignore * 2. Left Click + !isLassoActive: Return the clicked item only * 3. Right Click + isLassoActive: Return selectedItemCache * 4. Right Click + !isLassoActive * a. User clicked on an already selected item : Return the selectedItemCache * b. User clicked on a non-selected item : Return event targets * * @param event Event object */ VisEventHandler.prototype._getOnClickData = function _getOnClickData(event) { var items = this._target && this._target.getEventTargets ? this._target.getEventTargets(event) : []; var isRightClick = event.type === 'contextmenu'; var ignoreCase = !isRightClick && this.lassoCtrl.isLassoMode(); var isMultiSelect = !ignoreCase && items && items.length > 1; return { shouldHandle: !ignoreCase, items: items, isMultiSelect: isMultiSelect }; }; /** * Handle mobile tap event * * @param event Event object */ VisEventHandler.prototype.onTap = function onTap(event) { this._resolveCoordinates(event); if (this._skipSelection) { this.setEventLocalProperty(event, 'preventDefaultContextBar', true); } else { var items = this._target && this._target.getEventTargets ? this._target.getEventTargets(event) : []; if (items) { if (items.length > 0) { var bSelected = this._isItemsSelected(items); var bHighlighted = this._isItemsHighlighed(items); this.setEventLocalProperty(event, 'preventDefaultContextBar', true); // if the target items are either highlighted or selected then toggle the selection // Otherwise highlight and force a tooltip. if (bSelected || bHighlighted) { // force to toggle / append selections (aka. CTRL + left-click) this._handleSelections(event, items, { forceCtrlClick: true }); } else { this._handleHighlights(items); // force to show the tooltips this._handleTooltips(event, items, true); } } else { this._clearAll(event); } } } }; /** * Handle mobile hold (long press) event */ VisEventHandler.prototype.onHold = function onHold(event) { this._resolveCoordinates(event); var isMultiSelect = false; this._onHold = true; var items = void 0; if (this.lassoCtrl.isLassoMode()) { items = this._target && this._target.getTargetsByCords ? this._target.getTargetsByCords(this.lassoCtrl.getCoordinates(), false) : []; isMultiSelect = true; } else { items = this._target && this._target.getEventTargets ? this._target.getEventTargets(event) : []; } // treat it like an right-click this._handleSelections(event, items, { forceRightClick: true, isMultiSelect: isMultiSelect }); }; /** * Store the visualization Interactivity state */ VisEventHandler.prototype._storeInteractivityState = function _storeInteractivityState(state) { // store the state as a visualization property this._ownerWidget.updateVisProperties({ id: 'interactivityState', value: state }, { silent: true }); }; /** * Handle mouse wheel scroll event * * @param event Event object */ VisEventHandler.prototype.onMouseWheel = function onMouseWheel(event) { if (this._target.canZoom() && this._target.zoom) { this._target.zoomStart(event); this._target.zoom(event); var state = this._target.zoomEnd(event); if (state) { this._storeInteractivityState(state); } event.preventDefault(); } }; /** * Handle transformstart to handle beginning of zoom-in/out */ VisEventHandler.prototype.onPinchStart = function onPinchStart(event) { if (this._target.canZoom() && this._target.zoom) { this._resolveCoordinates(event); // Pan gesture starts due to the touch event that occurs before the pinch // explicitly cancel the pan gesture before starting the zoom gesture this._target.panEnd(event); this._target.zoomStart(event); } }; /** * Handle transform gesture to zoom-in/out */ VisEventHandler.prototype.onPinch = function onPinch(event) { if (this._target.canZoom() && this._target.zoom) { this._resolveCoordinates(event); this._target.zoom(event); // Prevent synthetic events now that a touch gesture is guaranteed (event.gesture || event.originalEvent || event).preventDefault(); } }; /** * Handle transformend to handle the end of zoom-in/out */ VisEventHandler.prototype.onPinchEnd = function onPinchEnd(event) { if (this._target.canZoom() && this._target.zoom) { this._resolveCoordinates(event); var state = this._target.zoomEnd(event); if (state) { this._storeInteractivityState(state); } } }; /** * Handle either the tap or touchstart */ VisEventHandler.prototype.onClickTapPanStart = function onClickTapPanStart(event) { // avoid clicktap being cancelled out by touchstart return event.type === 'touchstart' ? this.onPanStart(event) : this.onClickTap(event); }; /** * Handle dragstart to handle beginning of pan */ VisEventHandler.prototype.onPanStart = function onPanStart(event) { if (this._canPan(event)) { this._resolveCoordinates(event); this._target.panStart(event); // Do not prevent synthetic events on touchstart/touchend as some visualizations use click events with no touch fallback } }; /** * Handle dragend to handle the end of pan */ VisEventHandler.prototype.onPanEnd = function onPanEnd(event) { this._resolveCoordinates(event); var state = this._target.panEnd(event); if (state) { this._storeInteractivityState(state); } // Do not prevent synthetic events on touchstart/touchend as some visualizations use click events with no touch fallback }; /** * Handle touchmove to handle pan */ VisEventHandler.prototype.onPanMove = function onPanMove(event) { var originalEvent = event.gesture || event.originalEvent || event; var isZoom = originalEvent.touches && event.originalEvent.touches.length === 2; if (this._canPan(event)) { this._resolveCoordinates(event); this._target.pan(event); // Prevent synthetic events now that a touch gesture is guaranteed originalEvent.preventDefault(); } else if (isZoom) { // When user pinches with two touches, both 'transform' and 'touchmove' events are fired. // In order to prevent the entire page from zooming in and out, // Prevent default for all double touch gestures which are considered as zoom originalEvent.preventDefault(); } }; /** * Handle mouse down event, fired before click * * @param event Event object */ VisEventHandler.prototype.onMouseDown = function onMouseDown(event) { var _getOnClickData3 = this._getOnClickData(event), shouldHandle = _getOnClickData3.shouldHandle, items = _getOnClickData3.items; if (shouldHandle && !!items && items.length > 0) { // Multiselect (Defect #219253): If there are items under the point, // set key on eventChain to tell dashboard-core to not deselect this.setEventLocalProperty(event, 'preventWidgetDeselect', true); } this._skipSelection = false; this._mouseDown = { X: event.pageX, Y: event.pageY }; if (!this.lassoCtrl.isLassoMode() && this._target.canZoom && this._target.canZoom() && this._target.panStart) { this._target.panStart(event); } }; /** * Handle mouse up event */ VisEventHandler.prototype.onMouseUp = function onMouseUp(event) { if (this._target.canZoom && this._target.canZoom() && this._target.panEnd) { var state = this._target.panEnd(event); if (state) { this._storeInteractivityState(state); } } }; VisEventHandler.prototype.getEdgeSelection = function getEdgeSelection() { return this._edgeSelection; }; /** * Handle mouse up event */ VisEventHandler.prototype._onDocumentMouseUp = function _onDocumentMouseUp() { this._mouseDown = false; }; /** * Handle touch end event on mobile */ VisEventHandler.prototype._onDocumentTouchEnd = function _onDocumentTouchEnd() { if (this._onHold) { this._onHold = false; } }; /** * Determine whether the target can pan * * @param event Event object * @return true if the target can pan with the given event, otherwise false */ VisEventHandler.prototype._canPan = function _canPan(event) { return this._target.canZoom && this._target.canZoom() && event.originalEvent.touches.length === 1 && !this.lassoCtrl.isLassoMode(); }; /** * Resolve the coordinates from the given event * * @param event Event object * @return object containing the resolved x / y coordinates */ VisEventHandler.prototype._resolveCoordinates = function _resolveCoordinates(event) { var touches = null; if (event.targetTouches) { touches = event.targetTouches; } else if (event.originalEvent && event.originalEvent.targetTouches) { touches = event.originalEvent.targetTouches; } else if (event.gesture) { touches = event.gesture.touches; } var isTouch = touches && touches.length; if (isTouch) { var coords = touches && touches.length ? touches[0] : null; // touchend does not have coordinates if (coords && coords.clientX !== undefined) { event.isTouch = true; event.clientX = coords.clientX; event.clientY = coords.clientY; event.pageX = coords.pageX; event.pageY = coords.pageY; } } else { if (event.gesture && event.gesture.center) { event.clientX = event.clientX || event.gesture.center.clientX; event.clientY = event.clientY || event.gesture.center.clientY; event.pageX = event.pageX || event.gesture.center.pageX; event.pageY = event.pageY || event.gesture.center.pageY; } else if (event.originalEvent) { event.clientX = event.clientX || event.originalEvent.clientX || event.originalEvent.pageX; event.clientY = event.clientY || event.originalEvent.clientY || event.originalEvent.pageY; event.pageX = event.pageX || event.originalEvent.pageX; event.pageY = event.pageY || event.originalEvent.pageY; } } }; /** * Get the tooltip bound (coordinates) based on the given event * * @param event Event object */ VisEventHandler.prototype._getBoundsInfoFromEvent = function _getBoundsInfoFromEvent(event) { return { top: event.clientY, height: 1, left: event.clientX - 10, // Want the tooltip to show a little to the left of the mouse width: 20, parent: document.body }; }; /** * Set the event local property * * @param event Event object * @param name Property name * @param value Property value */ VisEventHandler.prototype.setEventLocalProperty = function setEventLocalProperty(event, name, value) { var eventChainLocal = new EventChainLocal(event); eventChainLocal.setProperty(name, value); }; /** * Determine whether the mouse is dragging * * @param event Event object */ VisEventHandler.prototype._isDragging = function _isDragging(event) { if (event.type === 'touchmove' && this.lassoCtrl.isLassoMode()) { return event.originalEvent.touches.length === 1; } return this._mouseDown && (Math.abs(this._mouseDown.X - event.pageX) > 5 || Math.abs(this._mouseDown.Y - event.pageY) > 5); }; /** * Clear the hover timer */ VisEventHandler.prototype._clearHoverTimer = function _clearHoverTimer() { if (this._oTimerOut !== null) { clearTimeout(this._oTimerOut); this._oTimerOut = null; } }; /** * Clear the last highlight */ VisEventHandler.prototype._clearLastHighlight = function _clearLastHighlight() { if (this._lastHighlighted) { // clear previous highlighted item decorations this._clearDecorations(this._lastHighlighted, 'highlight'); } this._lastHighlighted = undefined; this._skipHoverover = false; }; /** * Clear deocrations * * @param items Event target items * @param name Decoration name, Clears all decorations if name is undefined */ VisEventHandler.prototype._clearDecorations = function _clearDecorations(items, name) { if (this._target && this._target.decorateTarget) { this._target.decorateTarget(items, name, false); } }; /** * Compare two event targets * * @param items1 event targets 1 * @param items2 event targets 2 * @return true if two targets are the same, otherwise false */ VisEventHandler.prototype._compareItems = function _compareItems(items1, items2) { var keys1 = _.pluck(items1, 'key'); var keys2 = _.pluck(items2, 'key'); return _.difference(keys1, keys2).length === 0 && _.difference(keys2, keys1).length === 0; }; /** * Determines whether the given target items are currently highlighted * * @param items event target items * @return true if the target items are highlighted, otherwise false */ VisEventHandler.prototype._isItemsHighlighed = function _isItemsHighlighed(items) { return this._compareItems(this._lastHighlighted, items); }; /** * Determines whether the given target items are currently implicitly selected * * @param items event target items * @return true if the target items are implicitly selected, otherwise false */ VisEventHandler.prototype._isItemsImpliedSelected = function _isItemsImpliedSelected(items) { return _.find(this._lastImpliedSelection, function (impliedSelection) { return impliedSelection === items; }); }; /** * Handle hover tooltip event. * Show a delayed (500ms) tooltip on chart elements * * @param event Event object * @param items Event target items */ VisEventHandler.prototype._handleTooltips = function _handleTooltips(event, items, forceTooltips) { var _this3 = this; var prevTooltip = this._lastTooltip ? this._lastTooltip : []; this._lastHoverEvent = event; this._tooltipItems = items; if (this._tooltipItems && this._tooltipItems.length > 0) { // keep track of the selected item this._targetElement.addClass('cursor-pointer'); if (!this._compareItems(prevTooltip, this._tooltipItems)) { this._clearHoverTimer(); this._oTimerOut = setTimeout(function () { _this3._clearHoverTimer(); var itemExist = _this3._tooltipItems && _this3._tooltipItems.length > 0; var allowHover = !_this3._stopHoverover && !_this3._skipHoverover; var force = forceTooltips; if (itemExist && (allowHover || force)) { var aSelectionInfo = _this3._getDataPayload(_this3._tooltipItems, VisTooltipInfo); if (aSelectionInfo) { var infoShown = _this3._showSelectionInfo(aSelectionInfo, _this3._lastHoverEvent, false, true, _this3._drillOnly, /*noDrillthrough*/false, { isHover: true }); if (infoShown) { _this3._lastTooltip = _this3._tooltipItems; } _this3._lastHoverEvent = null; _this3._tooltipItems = null; } } }, 500); } } else { this._targetElement.removeClass('cursor-pointer'); if (this._lastTooltip) { this._lastTooltip = null; this._clearHoverTimer(); this._oTimerOut = setTimeout(function () { _this3._clearHoverTimer(); if (!_this3._stopHoverover) { _this3._visActionHelper.hideToolbarActions(); } }, 500); } } }; /** * Handle hightlight event. * Highlight the chart element upon hover. * * @param items Event target items * @param bAppend Append items in this._lastHighlighted to items if true */ VisEventHandler.prototype._handleHighlights = function _handleHighlights(items, bAppend) { var prevHighlight = this._lastHighlighted ? this._lastHighlighted : []; if (bAppend) { var nonSelected = _.filter(this._lastHighlighted, function (obj) { return obj.source && obj.source.getDecoration && !obj.source.getDecoration('selected'); }); items.push.apply(items, nonSelected); } if (items && items.length > 0) { if (!this._isItemsHighlighed(items)) { if (this._target && this._target.decorateTarget) { // clear previous highlights this._clearLastHighlight(); if (this._target.decorateTarget(items, 'highlight')) { //Keep track of the newly highlighted items this._lastHighlighted = items; } } } } else if (prevHighlight.length > 0) { // clear the previous selection this._clearLastHighlight(); } // re-render with the new highlight if (!this._compareItems(prevHighlight, this._lastHighlighted) && this._target.completeDecorations) { this._target.completeDecorations(); } }; /** * Handle selection event. * Selection highlight the chart element upon click. * * @param items Event target items * @param {boolean} [options.forceCtrlClick=fasle] - True forces a control + click selection * @param {boolean} [options.forceRightClick=fasle] - True forces a right click selection * @param {boolean} [options.isMultiSelect=false] - True indicates multiple datapoints are selected. */ VisEventHandler.prototype._handleSelections = function _handleSelections(event, items, options) { var bCtrlClick = options.forceCtrlClick || event.ctrlKey || event.metaKey; var bRightClick = options.forceRightClick || event.type === 'contextmenu'; this._skipHoverover = false; var resolved = null; if (bRightClick) { // Skip the hover to avoid tooltips winning over contextmenu this._skipHoverover = true; // handle right click var oNewSelection = this._getDataPayload(items, VisTooltipInfo); resolved = this._resolveItems(items); if (resolved.length > 0) { var _options = items[0].selectionContext ? { slots: items[0].selectionContext.slots, mapIndex: items[0].selectionContext.mapIndex, actions: items[0].selectionContext.actions } : {}; _options.area = items[0].area; _options.isAreaSelected = items[0].isAreaSelected; var includeApplyFilter = void 0, noFilters = void 0, noDrillthrough = void 0; if (items[0].onlyGlobalActions) { // Only want to show actions applicable to the full visualization, e.g. TextAction includeApplyFilter = false; noFilters = true; noDrillthrough = true; } else { includeApplyFilter = bRightClick; noFilters = false; noDrillthrough = false; } this._showSelectionInfo(oNewSelection, event, includeApplyFilter, noFilters, undefined, noDrillthrough, _options); this._triggerWidgetEvent('visevent:select', event, this._target, items, { selectionInfo: oNewSelection, append: bCtrlClick, lasso: options.isMultiSelect }); this._handleHighlights(items, bCtrlClick); return this._select(resolved, /*select*/true, bCtrlClick, /*pending*/true, items[0].isEdgeSelect, options.isMultiSelect); } else { // show title actions on right-click this._titleActions(event, items); } } else { resolved = this._resolveItems(items); this._handleHighlights(items); if (resolved.length > 0) { var isSelected = this._isSelected(resolved, items[0].isEdgeSelect); // When the's select is IDENTICAL to the existing selection, cancel selection (doSelect=false) // otherwise, set selection to match what the user clicked - even if what the user clicked contains or is contained by existing selection var doSelect = !isSelected; var doAppend = bCtrlClick || this.lassoCtrl.isLassoMode() && isSelected; var oSelectionInfo = this._getDataPayload(items); this._triggerWidgetEvent('visevent:select', event, this._target, items, { selectionInfo: oSelectionInfo, append: doAppend, lasso: options.isMultiSelect }); return this._select(resolved, doSelect, doAppend, /*pending*/false, items[0].isEdgeSelect, options.isMultiSelect); } else if (items && items.length >= 1) { var type = items[0].type; switch (type) { case 'customdata': return this._handleCustomDataSelections(items, event); case 'itemclass': return this._handleImpliedSelections(items, bCtrlClick, true); case 'summary': return this._handleImpliedSelections(items, bCtrlClick, true); default: break; } } } }; VisEventHandler.prototype._isSelectionsWithCustomData = function _isSelectionsWithCustomData(itemsArray) { var aResult = _.filter(itemsArray, function (item) { return item.type !== 'customdata'; }); return itemsArray.length > 0 && (!aResult || aResult.length === 0); //Means all selections are custom data selections }; /** * Handle implied selection event. (Similar to _handleHighlights; used to decorate * target with implied_selection instead of highlight). On hover, if it's not a multi-select, * remove the implied_selection styling on the previous implied selections. * * @param items Event target items * @param isMultiSelect {Boolean} True if multiselect action is occuring (ctrl + click) * @param apply {Boolean} True if the decoration should be applied */ VisEventHandler.prototype._handleImpliedSelections = function _handleImpliedSelections(items, isMultiSelect, apply) { var prevImpliedSelection = this._lastImpliedSelection ? this._lastImpliedSelection : []; if (apply && items && items.length > 0) { if (!this._isItemsImpliedSelected(items)) { // Add target items to array of implied selections this._lastImpliedSelection.push(items); this._target.decorateTarget(items, 'implied_selection', apply, isMultiSelect); } } else if (prevImpliedSelection.length > 0 && !isMultiSelect) { // Clear the previous implied selections this._lastImpliedSelection = []; this._clearDecorations(items, 'implied_selection'); } }; VisEventHandler.prototype._handleCustomDataSelections = function _handleCustomDataSelections(items, event) { this._triggerWidgetEvent('visevent:customdataselection', event, this._target, items); if (this._target && this._target.applyCustomDataSelection) { var currentSelectedIds = _.pluck(this._visAPI.getDecoratedCustomData('selected'), 'id'); this._target.applyCustomDataSelection(_.filter(items, function (item) { return !_.contains(currentSelectedIds, item.key); })); } }; /** * Resolve the event target items to data item ids and tuples items * * @param items Event target items * @return resolved array of object containing the data item ids and tuple items */ VisEventHandler.prototype._resolveItems = function _resolveItems(items) { var _this4 = this; var resolved = []; //The value index corresponds to the slot index _.each(items, function (item) { if (item.type === 'datapoint') { resolved = resolved.concat(_this4._resolveDataPointItem(item)); } else if (item.type === 'item') { resolved = resolved.concat(_this4._resolveEdgeItem(item)); } }); return resolved; }; /** * Add the event target item to the selections info object * * @param selections Selection info object * @param item Event target item */ VisEventHandler.prototype._processDataPointSelection = function _processDataPointSelection(selections, item) { var _this5 = this; var slots = this._getMappedSlotListByItem(item); _.each(item.values, function (rowdata, valueIndex) { var slot = item.slotAPIs ? item.slotAPIs[valueIndex] : slots[valueIndex]; if (slot) { var valueSpans = 0; _.each(rowdata, function (value, itemInSlotIndex) { if (value === null) { value = Formatter.format(null); } var slotDataItem = _this5._getSlotDataItem(slot, item, itemInSlotIndex + valueSpans); if (value.span) { //Any parts of a value that span multiple levels are considered "unbalanced". //values in the tuple beyond the span should be associated with items beyond the span valueSpans += value.span - 1; } if (slotDataItem && ((typeof value === 'undefined' ? 'undefined' : _typeof(value)) !== 'object' || !SlotAPIHelper.isMultiMeasuresSeriesOrValueDataItem(slotDataItem))) { selections.addSelection(slot, slotDataItem, value, item.isEdgeSelect); } }); } }); }; VisEventHandler.prototype._resolveDataPointItem = function _resolveDataPointItem(item) { var slots = this._getMappedSlotListByItem(item); var resolved = []; _.each(item.values, function (value, valueIndex) { var slot = item.slotAPIs ? item.slotAPIs[valueIndex] : slots[valueIndex]; if (slot) { var dataItems = slot.getDataItemList(); _.each(value, function (v, itemInSlotIndex) { var dataItem = dataItems[itemInSlotIndex]; if (dataItem && dataItem.getType() === 'attribute' && !SlotAPIHelper.isMultiMeasuresSeriesOrValueDataItem(dataItem)) { resolved.push({ dataItemId: dataItem.getId(), itemId: dataItem.getColumnId(), tupleItem: v }); } }); } }); return [resolved]; }; /** * Expand categories assigned of an edge to include dataItem information. */ VisEventHandler.prototype._resolveEdgeItem = function _resolveEdgeItem(item) { var slots = this._getMappedSlotListByItem(item); var resolved = []; //The values array within each item should be length 2 for edge items (first item is category, second is measure value) _.each(item.values, function (value, valueIndex) { var slot = slots[valueIndex]; if (slot) { var dataItems = slot.getDataItemList(); _.each(value, function (v, itemIndex) { var dataItem = dataItems[itemIndex]; if (!SlotAPIHelper.isMultiMeasuresSeriesOrValueDataItem(dataItem) && dataItem.getType() === 'attribute') { resolved.push({ dataItemId: dataItem.getId(), itemId: dataItem.getColumnId(), tupleItem: v }); } }); } }); return resolved; }; /** * Add the event target item to the selections info object * * @param selections Selection info object * @param item Event target item */ VisEventHandler.prototype._processEdgeSelection = function _processEdgeSelection(selections, item) { var _this6 = this; var slots = this._getMappedSlotListByItem(item); if (item.values) { _.each(item.values, function (value, valueIndex, values) { if (value) { for (var i = 0; i < value.length; i++) { if (typeof value[i] !== 'undefined') { var slot = slots[valueIndex]; //Current selection items could have undefined/unset dataItem in the payload var slotDataItem = void 0; if (slot.getDefinition().getProperty('multiMeasure') && slot.getDataItemList().length > 0) { (function () { // This is for a specific case where a multi measures value is not in the correct index // The multiMeasuresSeriesSlot data item id is compared to the id's of this slot's data items to find the correct one var dataset = slot.getDefinition().getDatasetIdList()[0]; var multiMeasuresSeriesSlot = _.find(slots, function (slot) { return SlotAPIHelper.isMultiMeasuresSeriesSlot(slot) && slot.getDefinition().getDatasetIdList()[0] === dataset; }); if (!multiMeasuresSeriesSlot) { multiMeasuresSeriesSlot = SlotAPIHelper.getMultiMeasureSeriesSlot(_this6.visualization, dataset); } var multiMeasuresSlotIndex = _.indexOf(_.map(slots, function (slot) { return slot.getId(); }), multiMeasuresSeriesSlot.getId()); var multiMeasuresDataItemIndex = _.indexOf(_.map(multiMeasuresSeriesSlot.getDataItemList(), function (dataItemApi) { return dataItemApi.getColumnId(); }), SlotAPIHelper.MULTI_MEASURES_SERIES); if (multiMeasuresSlotIndex >= 0 && multiMeasuresDataItemIndex >= 0 && values[multiMeasuresSlotIndex] && values[multiMeasuresSlotIndex][multiMeasuresDataItemIndex] && values[multiMeasuresSlotIndex][multiMeasuresDataItemIndex].u) { var dataItemUId = values[multiMeasuresSlotIndex][multiMeasuresDataItemIndex].u; slotDataItem = _.find(slot.getDataItemList(), function (dataItem) { return dataItem.getId() === dataItemUId; }); // The data item id should be in the form '_multiMeasuresSeries(SHEET1.Income)' // The value within parentheses is parsed to find the data item sharing that id } })(); } if (slotDataItem === undefined) { slotDataItem = slot.getDataItemList()[i]; } if (slotDataItem) { selections.addSelection(slot, slotDataItem, value[i], item.isEdgeSelect); } } } } }); } }; /** * Add the event target item to the selection as a custom tooltip * @param selections Selection info object * @param item Event target item * @example * a custom data selection allows exposing tooltips for customdata * custom data tooltips can be provided in 2 different forms, titles and labels(name/value pairs). * { * value: 'Example of a tooltip title' * }, * { * label: 'Predictive strength', * value: '98%' * }, * { * label: 'Example of a label without a value' * } */ VisEventHandler.prototype._processCustomDataSelection = function _processCustomDataSelection(selections, item) { if (this._target && this._target.getCustomDataSelections && selections.addCustomSelection) { var customSelections = this._target.getCustomDataSelections(item); _.each(customSelections, function (s) { return selections.addCustomSelection((typeof s === 'undefined' ? 'undefined' : _typeof(s)) === 'object' ? s.label : s, (typeof s === 'undefined' ? 'undefined' : _typeof(s)) === 'object' ? s.value : undefined); }); } }; /** * Add the event target item tooltip decoration as a custom tooltip * @param selections Selection info object * @param items Event target items */ VisEventHandler.prototype._processDecorationTooltips = function _processDecorationTooltips(selections, items) { try { // Used by SA and forecast only currently. Only display annotation decos if one datapoint is selected if (items && items.length === 1) { if (this._target && this._target.getDecoration && selections.addCustomSelection) { var tooltip = this._target.getDecoration(items[0], VisEventHandler.DECORATIONS.TOOLTIP); if (tooltip && typeof tooltip === 'string') { selections.addCustomSelection(tooltip); } else if (tooltip && Array.isArray(tooltip)) { // Each element of tooltip is Array of length 2. tooltip.forEach(function (tip) { if (Array.isArray(tip) && tip.length === 2) { selections.addCustomSelection(tip[0], tip[1]); } }); } } } } catch (e) { this.logger.error(e); } }; /** * Identify a data item from a slot by index. * Usually this is straight forward as slot.getDataItemList()[index]. * However when measures are nested, the measure is give a series, * the series needs to be backtraced to the original measure data item * * example: * Each measures are added to a series data item as a tuple items as below: * { * itemClass: { * id: '_multiMeasuresSeries' * h: [{ * u: '_multiMeasuresSeries', * d: 'Measures' * }, { * u: 'uploadfile.csv.Year', * d: 'Year' * }] * }, * items:[{ * t: [{ * u: 'id_uploadfile.csv.Revenue' * d: 'Revenue' * }, { * u: 'uploadfile.csv.Year->[2016]', * d: '2017' * }] * }, { * t:[{ * u: 'id_uploadfile.csv.Planned_Revenue' * d: 'Planned Revenue' * }, { * u: 'uploadfile.csv.Year->[2017]', * d: '2017' * }] * }, { * t: [{ * u: 'id_uploadfile.csv.Revenue' * d: 'Revenue' * }, { * u: 'uploadfile.csv.Year->[2016]', * d: '2017' * }] * }, { * t:[{ * u: 'id_uploadfile.csv.Planned_Revenue' * d: 'Planned Revenue' * }, { * u: 'uploadfile.csv.Year->[2017]', * d: '2017' * }] * }] * } * * @param slot The target slot of the data item * @param item Event target item * @param index data item index within the slot */ VisEventHandler.prototype._getSlotDataItem = function _getSlotDataItem(slot, item, index) { var dataItemList = slot.getDataItemList(); // nested measures? if (SlotAPIHelper.isMultiMeasuresValueSlot(slot)) { var slots = this._getMappedSlotListByItem(item); var slotIndex = void 0, dataItemIndex = void 0; // find the multimeasures slot index _.find(slots, function (slot, index) { if (SlotAPIHelper.isMultiMeasuresSeriesSlot(slot)) { slotIndex = index; return true; } return false; }); // find the multimeasures series dataitem index if (slotIndex) { _.find(slots[slotIndex].getDataItemList(), function (dataItem, index) { if (SlotAPIHelper.isMultiMeasuresSeriesOrValueDataItem(dataItem)) { dataItemIndex = index; return true; } return false; }); var multiMeasuresSeries = item.values[slotIndex][dataItemIndex]; var measureId = multiMeasuresSeries.u; // return the measure data item return _.find(slot.getDataItemList(), function (dataItem) { return dataItem.getId() === measureId; }); } else { return slot.getDataItemList()[index]; } } else { return dataItemList[index]; } }; /** * Check whether the given resolved items are selected * * @param resolvedItems Array of resolved items */ VisEventHandler.prototype._isSelected = function _isSelected(resolvedItems, isEdgeSelect) { var payload = this._getSelectionPayload(resolvedItems, false, isEdgeSelect); return this._selector.isSelected(payload, this._edgeSelection || isEdgeSelect); }; /** * Determines whether the given target items are custom data * @param items event target items * @return true if the target items are custom data, otherwise false */ VisEventHandler.prototype._isCustomDataItems = function _isCustomDataItems(items) { return _.chain(items).pluck('type').contains('customdata').value(); }; VisEventHandler.prototype._isCustomDataSelected = function _isCustomDataSelected(items) { var currentSelectionIds = _.pluck(this._visAPI.getDecoratedCustomData('selected'), 'id'); // check if there is something currently selected if (currentSelectionIds.length > 0) { var customItemIds = _.chain(items).pluck('values').flatten().pluck('payload').pluck('id').value(); if (customItemIds.length > 0) { return _.intersection(currentSelectionIds, customItemIds).length === customItemIds.length; } } return false; }; /** * Determines whether the given target items are currently selected * * @param items event target items * @return true if the target items are highlighted, otherwise false */ VisEventHandler.prototype._isItemsSelected = function _isItemsSelected(items) { var isSelected = false; if (this._isCustomDataItems(items)) { isSelected = this._isCustomDataSelected(items); } else { var resolved = this._resolveItems(items); isSelected = resolved.length > 0 ? this._isSelected(resolved, items[0].isEdgeSelect) : false; } return isSelected; }; /** * Add/Remove the selected items to the selection controller * * @param resolved Array of resolved items * @param doSelect boolean flag indicating whether to add or remove selection * @param append boolean flag indicating whether to append to the existing selections * @param pending boolean indicating that the selection should be deferred (applied on keep/exclude/filter later on). * @param multiTuple boolean indicating multiple tuples are selected */ VisEventHandler.prototype._select = function _select(resolved, doSelect, append, pending, isEdgeSelect, multiTuple) { var transactionToken = this._transaction.startTransaction(); var selections = pending ? this.content.getFeature('DataPointSelections.pending') : this.content.getFeature('DataPointSelections'); var payload = this._getSelectionPayload(resolved, multiTuple, isEdgeSelect); if (!append) { selections.clearAll(transactionToken); } if (doSelect) { selections.select(payload, transactionToken); } else { selections.deselect(payload, transactionToken); } this._transaction.endTransaction(transactionToken); }; VisEventHandler.prototype._getSelectionPayload = function _getSelectionPayload(selections, multiTuple) { var edgeSelect = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; if (multiTuple) { if (edgeSelect) { return _.map(_.flatten(selections), function (s) { return { categories: [{ dataItemId: s.dataItemId, columnId: s.itemId, label: s.tupleItem && s.tupleItem.d, parentId: s.tupleItem && s.tupleItem.p && s.tupleItem.p.u, value: s.tupleItem && s.tupleItem.u }], edgeSelect: edgeSelect }; }); } else { return _.map(selections, function (datapoint) { return { categories: _.map(datapoint, function (s) { return { dataItemId: s.dataItemId, columnId: s.itemId, label: s.tupleItem && s.tupleItem.d, parentId: s.tupleItem && s.tupleItem.p && s.tupleItem.p.u, value: s.tupleItem && s.tupleItem.u }; }), edgeSelect: edgeSelect }; }); } } else { return [{ categories: _.chain(selections).flatten().map(function (s) { return { dataItemId: s.dataItemId, columnId: s.itemId, label: s.tupleItem && s.tupleItem.d, parentId: s.tupleItem && s.tupleItem.p && s.tupleItem.p.u, value: s.tupleItem && s.tupleItem.u }; }).value(), edgeSelect: edgeSelect }]; } }; /** * Reduce items to itemIds and tuples * * @param resolved Array of resolved items */ VisEventHandler.prototype.reduceItems = function reduceItems(resolved) { var selection = { itemIds: [], tuple: [] }; resolved.reduce(function (accumulator, item) { var _itemsId = void 0; var _tuple = void 0; if (Array.isArray(item)) { //datapoint selection _itemsId = _.map(item, function (o) { return o.itemId; }); _tuple = _.map(item, function (o) { if (_.isUndefined(o.tupleItem && o.tupleItem.u)) { return { u: o.tupleItem, d: o.tupleItem }; } return o.tupleItem; }); } else { //edge selection _itemsId = [item.itemId]; _tuple = item.tupleItem; } accumulator.itemIds = _.uniq(accumulator.itemIds.concat(_itemsId)); accumulator.tuple.push(Array.isArray(_tuple) ? _.uniq(_tuple, false, function (t) { return t && t.u || t; }) : _tuple); return accumulator; }, selection); return selection; }; /** * Show the title action toolbar for the given items */ VisEventHandler.prototype._titleActions = function _titleActions(event, items) { var _this7 = this; var aSlots = []; var itemsArea = items && items[0] ? items[0].area : undefined; if (items && items.length) { for (var i = 0; i < items.length; i++) { var item = items[i]; if (item.type !== 'customdata') { (function () { var slots = _this7.visualization.getSlots().getMappedSlotList(); item.values.forEach(function (value, index) { if (!aSlots.find(function (slot) { return slot.getId() === slots[index].getId(); })) { aSlots.push(slots[index]); } }); })(); } } var mapIndex = items[0].selectionContext ? items[0].selectionContext.mapIndex : undefined; var actions = items[0].selectionContext && items[0].selectionContext.actions || []; // Array for when there are multiple data items selected within a slot this.setEventLocalProperty(event, 'preventDefaultContextBar', true); this._visActionHelper.showTitleActions(aSlots, { 'bounds': this._getBoundsInfoFromEvent(event) }, null, // selected slot items are null for non-focus mode actions mapIndex, itemsArea, actions); } }; /** * Clear all selections from the selection controller */ VisEventHandler.prototype._clearAll = function _clearAll(event) { var _this8 = this; this._triggerWidgetEvent('visevent:clearselection', event); // clear selection controller return this.content.getFeature('DataPointSelections').clearAll().then(function () { return _this8.content.getFeature('DataPointSelections.pending').clearAll(); }).then(function () { // clear custom data selection return _this8.clearCustomDataSelection(); }); }; VisEventHandler.prototype.clearCustomDataSelection = function clearCustomDataSelection(options) { this._target.applyCustomDataSelection([], options); }; /** * Obtain the selection info object based on the event. * * @param {Object} event - Event object * @param {Class} InfoClass - VisSelectionInfo (default) or VisTooltipInfo */ VisEventHandler.prototype._getDataPayload = function _getDataPayload(items, InfoClass) { var _this9 = this; var VisInfoClass = InfoClass || VisSelectionInfo; var selections = new VisInfoClass(); _.each(items, function (item, itemIdx) { switch (item.type) { case 'datapoint': return _this9._processDataPointSelection(selections, item); case 'item': return _this9._processEdgeSelection(selections, item, itemIdx); case 'customdata': return _this9._processCustomDataSelection(selections, item); default: return; } }); this._processDecorationTooltips(selections, items); return selections; }; /** * Display the selection tooltip. */ VisEventHandler.prototype._showSelectionInfo = function _showSelectionInfo(oSelectionObj, event, includeApplyFilter, noFilters, drillOnly, noDrillthrough, options) { var infoShown = false; if (oSelectionObj && oSelectionObj.getCategorySelections()) { this._visActionHelper.showDataPointActions(oSelectionObj, { // Create fake bound information so that the tooltip shows up where the mouse is 'bounds': this._getBoundsInfoFromEvent(event), 'targetNode': event.currentTarget }, includeApplyFilter, noFilters, drillOnly, noDrillthrough, options); infoShown = true; this.setEventLocalProperty(event, 'preventDefaultContextBar', true); } return infoShown; }; /** * Api method wrapper for #toggleLassoSelect * @param {boolean} state * @param {string} viewId */ VisEventHandler.prototype.setLassoSelectState = function setLassoSelectState(state) { var viewId = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : LassoController.DEFAULT_VIEW_ID; if (this.lassoCtrl.getCurrentViewId() && this.lassoCtrl.getCurrentViewId() !== viewId) { this._endLassoSelection(); state = true; } return this.toggleLassoSelect(state, viewId); }; /** * Toggle the state of lasso mode * @param {boolean} [state] - The final state of lasso mode for the Vis. True means * switch on, false means switch off. When not provided, the current state is inverted. */ VisEventHandler.prototype.toggleLassoSelect = function toggleLassoSelect(state, viewId) { var enabled = arguments.length === 0 ? !this.lassoCtrl.isLassoMode() : !!state; if (this.lassoCtrl.isLassoMode() !== enabled) { if (enabled) { this.lassoCtrl.activateLassoMode(true, viewId); this.lassoCtrl.on('lassoSelect:done', this._handleLassoSelection, this); this.lassoCtrl.on('lassoSelect:cordsChange', this._handleLassoCordChange, this); this.lassoCtrl.on('lassoSelect:cancel', this._endLassoSelection, this); } else { this._endLassoSelection(); } } return enabled; }; VisEventHandler.prototype.getLassoViewId = function getLassoViewId() { return this.lassoCtrl.getCurrentViewId(); }; VisEventHandler.prototype._handleLassoSelection = function _handleLassoSelection(payload) { try { var items = []; if (!_.isUndefined(payload.radius)) { items = this._target && this._target.getEventTargets ? this._target.getEventTargets(payload.event, payload.radius) : []; } else { items = this._target && this._target.getTargetsByCords ? this._target.getTargetsByCords(payload.cords, /*sumarize*/false) : []; } if (items) { var options = { isMultiSelect: true }; // iPad has no ctrl click, default is append always by design // force ctrl click if selection was triggered by mobile end touch event // https://github.ibm.com/BusinessAnalytics/WACA-UXDR/issues/2656 if (payload.event.type === 'touchend') { options.forceCtrlClick = true; } this._handleHighlights(items); this._handleSelections(payload.event, items, options); } } catch (e) { this.logger.error(e); } }; VisEventHandler.prototype._handleLassoCordChange = function _handleLassoCordChange(payload) { try { var items = []; if (!_.isUndefined(payload.radius)) { items = this._target && this._target.getEventTargets ? this._target.getEventTargets(payload.event, payload.radius) : []; } else { items = this._target && this._target.getTargetsByCords ? this._target.getTargetsByCords(payload.cords, /*sumarize*/false) : []; } if (items) { this._handleHighlights(items); } } catch (e) { this.logger.error(e); } }; VisEventHandler.prototype._endLassoSelection = function _endLassoSelection() { this.lassoCtrl.off('lassoSelect:done', this._handleLassoSelection, this); this.lassoCtrl.off('lassoSelect:cordsChange', this._handleLassoCordChange, this); this.lassoCtrl.off('lassoSelect:cancel', this._endLassoSelection, this); this.lassoCtrl.activateLassoMode(false); }; VisEventHandler.prototype.isLassoSelectActive = function isLassoSelectActive() { return this.lassoCtrl.isLassoMode(); }; /** * Api method wrapper for #isLassoSelectActive */ VisEventHandler.prototype.getLassoSelectState = function getLassoSelectState() { return this.isLassoSelectActive(); }; //Send a "visevent" to notify listeners (features etc.) that a visualization-specific event has occurred //Eg: Supply information when the user is moving over the visualization or selecting a visualization element. VisEventHandler.prototype._triggerWidgetEvent = function _triggerWidgetEvent(name, event, target, items, additionalPayloadItems) { var payload = { sender: this._widgetApi, event: event, target: target, items: items }; _.each(additionalPayloadItems, function (additionalPayloadItems, key) { payload[key] = additionalPayloadItems; }); this._ownerWidget.trigger(name, payload); }; /** * Set _stopHoverover flag. * @param {Object} payload - indicate if the event is triggered by hover. e.g. {isHover: true}. */ VisEventHandler.prototype.setPopoverClosed = function setPopoverClosed(payload) { this._isHoverFlyoutShowing = false; if (!payload || !payload.isHover) { this._stopHoverover = false; } }; /** * Set _stopHoverover flag. Stop hover when the close event is not triggered by hover. * @param {Object} payload - indicate if the event is triggered by hover or widget ODT. e.g. {isHover: true}. */ VisEventHandler.prototype.setPopoverOpened = function setPopoverOpened(payload) { if (!payload || !payload.isHover) { this._stopHoverover = true; if (payload && payload.isWidgetOdt) { this._stopHoverover = false; } } }; VisEventHandler.prototype._getMappedSlotListByItem = function _getMappedSlotListByItem(item) { if (item && item.datasetId) { return SlotAPIHelper.getMappedSlotListByDataset(this.visualization, item.datasetId); } else { return this.visualization.getSlots().getMappedSlotList(); } }; return VisEventHandler; }(); /** * Decorations supported by VisEventHandler */ VisEventHandler.DECORATIONS = { HIGHLIGHT: 'highlight', SELECT: 'select', TOOLTIP: 'tooltip' }; return VisEventHandler; }); //# sourceMappingURL=VisEventHandler.js.map